如何使用silentmantra/benchmark?

问题描述 投票:0回答:1

在 Stackoverflow 上,我经常在我的答案中对 JS 解决方案进行基准测试,人们问他们如何编写自己的基准测试。因此,这篇文章更像是一个答案,因为有方便的代码片段工具,可以直接将基准注入其中。

所以请不要投票/反对或关闭,如果不感兴趣就跳过。

主页:https://github.com/silentmantra/benchmark
例如,您可以检查https://stackoverflow.com/search?tab=newest&q=user%3a14098260%20benchmark&searchOn=3

javascript benchmarking microbenchmark
1个回答
0
投票

基准测试代码应包含在

<script>
标签内联中。然后您应该加载基准测试工具的代码,您可以通过

执行此操作
  1. <script>
    。确保它在您的
    <script>
    和基准之后加载(您可以
    defer
    (也可以使用
    type="module"
    )):
<script src="https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js"></script>
  1. 直接在基准代码内的任何位置(通常是最后一行):
/*@skip*/ fetch('https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js').then(r => r.text().then(eval));

正如您在

/* @skip */
中看到的,您可以从基准代码中排除一行代码,但最初在
<script>
中执行它。

加载该工具后,在 UI 中转换您的

<script>
以显示基准测试结果并执行它们。结果显示为 JSON 并剪切到前 500 个字符左右。

一个简单的基准可能是

  1. 在每个基准测试之前执行的一些代码
  2. 开头的一个或多个解决方案
// @benchmark Solution 1

其中

@benchmark
后面的所有内容都是解决方案的名称。

  1. 解决方案的执行结果是最后一行的结果(通过
    eval()
    执行)。
    const input = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua';
    
    // @benchmark Array::reverse()
    input.split('').reverse().join('');
    
    // @benchmark string concat
    let result = '';
    for(let i = input.length - 1; i >= 0; i--){
      result += input[i];
    }
    result;
    
    /*@skip*/ fetch('https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js').then(r => r.text().then(eval));

单击

RUN
后,该工具会估计运行每个解决方案的周期数,因此解决方案的平均时间将不少于 100 毫秒,并且默认情况下运行每个解决方案 5 次(您可以通过输入次数来更改它)。运行后,您可以将结果复制为标记。所以上述基准给出:

` Chrome/122
------------------------------------------------------
string concat     1.00x | x1000000 399 404 422 435 441
Array::reverse()  3.08x |  x100000 123 124 130 131 139
------------------------------------------------------
https://github.com/silentmantra/benchmark `

第一个解决方案是最快的,标记为

1x
。其他解决方案被标记为如何比顶级解决方案慢。下一列是解决方案运行的周期数,接下来的 5 个(运行解决方案的次数)是运行周期所需的毫秒数,其中最短时间为第一个(按升序排序)。基本上根据我的经验,最短时间是解决方案执行效果最能体现的价值。

您可以手动设置时间和周期(有时对于重型解决方案,您可能更喜欢手动将周期设置为 1):

const TIMES = 3, CYCLES = 1000000;
const input = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua';

// @benchmark Array::reverse()
input.split('').reverse().join('');

// @benchmark string concat
let result = '';
for(let i = input.length - 1; i >= 0; i--){
  result += input[i];
}
result;

/*@skip*/ fetch('https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js').then(r => r.text().then(eval));

算法具有不同的时间复杂度,因此可以对不同输入大小的解决方案进行基准测试。为此,我们有 3 个变量:

  1. $chunk
    - 组成输入的块。可以是字符串、数组或返回其中任意一个的函数。
  2. $input
    - 用于填充块的数组或字符串。
  3. $chunks
    - 要运行的块数量的数组(默认
    [1, 10, 100, 1000]
    - 无需定义)。

让我们用字符串来检查一下:

const $chunk = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua';
let $input = '';

// @benchmark count stupid
{
  const chars = {};
  for(let i = 0; i < $input.length; i++){
    const char = $input[i];
    chars[char] ??= $input.split('').filter(c => c === char).length;
  }
  chars;
}

// @benchmark count smart
{
  const chars = {};
  for(let i = 0; i < $input.length; i++){
    const char = $input[i];
    chars[char] ??= 0;
    chars[char]++;
  }
  chars;
}
/*@skip*/ fetch('https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js').then(r => r.text().then(eval));

` Chrome/122
--------------------------------------------------------------------------------
>                   n=122      |     n=1220     |    n=12200    |    n=122000   
count smart    1.00x x100k 284 | 1.00x x10k 183 | 1.00x x1k 186 | 1.00x x100 186
count stupid   2.56x x100k 727 | 3.33x x10k 609 | 3.40x x1k 633 | 7.26x  x10 135
--------------------------------------------------------------------------------
https://github.com/silentmantra/benchmark `

顶级解决方案是输入大小最多的最佳解决方案。

请注意,您可以使用

{}
块创建块作用域以在其中运行解决方案以避免变量冲突。

具有分块功能:

const $chunks = [100, 1000, 10000, 100000];
const $chunk = () => [Math.random()];
const $input = [];

// @benchmark count stupid
{
  const chars = {};
  const allNums = $input.map(n => n.toString().slice(2)).join('');

  for(let i = 0; i < allNums.length; i++){
    const char = allNums[i];
    chars[char] ??= allNums.split('').filter(c => c === char).length;
  }
  chars;
}

// @benchmark count smart
{
  const chars = {};
  $input.forEach(num => {
    for(const char of num.toString().slice(2)){
      chars[char] ??= 0;
      chars[char]++;
    }
  });
  chars;
}
/*@skip*/ fetch('https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js').then(r => r.text().then(eval));

` Chrome/122
------------------------------------------------------------------------------
>                  n=100      |    n=1000     |    n=10000     |   n=100000   
count smart    1.00x x10k 172 | 1.00x x1k 191 | 1.00x x100 246 | 1.00x x10 337
count stupid   4.19x x10k 720 | 4.01x x1k 766 | 5.53x  x10 136 | 5.43x  x1 183
------------------------------------------------------------------------------
https://github.com/silentmantra/benchmark `

如果您需要在循环之前执行一些代码,请使用

// @run
。例如,您可以引入解决方案中使用的一组函数/类(在每个周期中初始化它们的成本很高并且会扭曲结果)。

const $chunks = [100, 1000, 10000, 100000];
const $chunk = () => [Math.random()];
const $input = [];

// @benchmark count stupid
{
  const countChars = $input => {
    const chars = {};
    const allNums = $input.map(n => n.toString().slice(2)).join('');

    for(let i = 0; i < allNums.length; i++){
      const char = allNums[i];
      chars[char] ??= allNums.split('').filter(c => c === char).length;
    }
    return chars;
  };
  // @run
  countChars($input);
}

// @benchmark count smart
{
  const countChars = $input => {
    const chars = {};
    $input.forEach(num => {
      for(const char of num.toString().slice(2)){
        chars[char] ??= 0;
        chars[char]++;
      }
    });
    return chars;
  };
  // @run
  countChars($input);
}
/*@skip*/ fetch('https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js').then(r => r.text().then(eval));

© www.soinside.com 2019 - 2024. All rights reserved.