菜单
开源

SharedArray

SharedArray 是一个类数组对象,它在 VU(虚拟用户)之间共享底层内存。该函数只执行一次,其结果也只在内存中保存一次。当脚本请求元素时,k6 会返回该元素的*副本*。

您必须在init 上下文中构造一个 SharedArray。它的构造函数需要一个用于 SharedArray 的名称以及一个必须返回一个数组对象的函数。

JavaScript
import { SharedArray } from 'k6/data';

const data = new SharedArray('some name', function () {
  const dataArray = [];
  // more operations
  return dataArray; // must be an array
});

名称参数是必需的。VU 是完全独立的 JS VM,k6 需要某种方式来识别它需要返回的 SharedArray。您可以拥有多个 SharedArray,甚至可以只为给定的 VU 加载其中一部分,尽管这不太可能带来任何性能优势。

SharedArray 支持的操作包括

  • 使用 length 获取元素的数量
  • 使用正常语法 array[index] 按索引获取元素
  • 使用 for-of 循环

在大多数情况下,您可以通过将数组数据结构包装在 SharedArray 中来减少其内存使用。一旦构造完成,SharedArray 是只读的,因此您不能使用 SharedArray 在 VU 之间通信数据

注意

尝试在init 上下文之外实例化 SharedArray 会导致异常 new SharedArray must be called in the init context

此限制最终将被移除,但目前意味着您只能在测试的非常开始时使用 SharedArray 填充测试数据,而不能将其用作从响应接收数据的结果(例如)。

示例

JavaScript
import { SharedArray } from 'k6/data';

const data = new SharedArray('some name', function () {
  // All heavy work (opening and processing big files for example) should be done inside here.
  // This way it will happen only once and the result will be shared between all VUs, saving time and memory.
  const f = JSON.parse(open('./somefile.json'));
  return f; // f must be an array
});

export default function () {
  const element = data[Math.floor(Math.random() * data.length)];
  // do something with element
}

性能特性

在内部,当前 SharedArray 的实现将数据编组为 JSON,并且仅在请求时才对元素进行解组。

通常,此操作应该是不可察觉的(相对于您对数据所做的其他操作)。但是,对于小数据集,SharedArray 的性能可能会更差。然而,这高度依赖于用例。

为了测试这一点,我们在 v0.31.0 版本上使用 100 个 VU 运行了以下脚本。

JavaScript
import { check } from 'k6';
import http from 'k6/http';
import { SharedArray } from 'k6/data';

const n = parseInt(__ENV.N);
function generateArray() {
  const arr = new Array(n);
  for (let i = 0; i < n; i++) {
    arr[i] = { something: 'something else' + i, password: '12314561' };
  }
  return arr;
}

let data;
if (__ENV.SHARED === 'true') {
  data = new SharedArray('my data', generateArray);
} else {
  data = generateArray();
}

export default function () {
  const iterationData = data[Math.floor(Math.random() * data.length)];
  const res = http.post('https://quickpizza.grafana.com/api/post', JSON.stringify(iterationData), {
    headers: { 'Content-type': 'application/json' },
  });
  check(res, { 'status 200': (r) => r.status === 200 });
}

如表所示,在较低数据行数时性能差异不大:直到约 1000 数据行时,SharedArray 在内存使用方面几乎没有优势,并且 CPU 使用率的上限更高(尽管没有显著高)。

在 1 万行及以上时,内存节省也开始显著转化为 CPU 节省。

数据行数共享挂钟时间CPU %内存使用量http 请求数
1002:01:7070-79%213-217MB92191-98837
1002:01:8074-75%224-232MB96851-98643
10002:01:6074-79%209-216MB98251-98806
10002:01:9075-77%333-339MB98069-98951
100002:01:7078-79%213-217MB97953-98735
100002:03:0080-83%1364-1400MB96816-98852
1000002:02:2078-79%238-275MB98103-98540
1000002:14:00120-124%8.3-9.1GB96003-97802

在 v0.30.0 中,在较低数量时 CPU 使用率的差异约为 10-15%,但在约 1 万数据行时也开始趋于平稳,并在 10 万数据行时明显更胜一筹。

CPU/内存数据来自使用 /usr/bin/time。请参阅包含原始数据的 gist

这些数字仅供说明:性能可能会受到从 SharedArray 检索到的元素的任何额外处理的影响,或者如果使用了输出,或者获取了多个元素等。虽然 SharedArray 会有一些 CPU 使用率,但在仅有 10 个元素的给定情况下,它可能微不足道,或者对于 10 万个元素而言,其问题比内存使用量更大。因此,如有疑问,您应该进行一些基准测试,并根据您的用例决定哪些权衡更重要。