在 Stackoverflow 上,我经常在我的答案中对 JS 解决方案进行基准测试,人们问他们如何编写自己的基准测试。因此,这篇文章更像是一个答案,因为有方便的代码片段工具,可以直接将基准注入其中。
所以请不要投票/反对或关闭,如果不感兴趣就跳过。
主页:https://github.com/silentmantra/benchmark
例如,您可以检查https://stackoverflow.com/search?tab=newest&q=user%3a14098260%20benchmark&searchOn=3
基准测试代码应包含在
<script>
标签内联中。然后您应该加载基准测试工具的代码,您可以通过执行此操作
<script>
。确保它在您的 <script>
和基准之后加载(您可以 defer
(也可以使用 type="module"
)):<script src="https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js"></script>
/*@skip*/ fetch('https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js').then(r => r.text().then(eval));
正如您在
/* @skip */
中看到的,您可以从基准代码中排除一行代码,但最初在 <script>
中执行它。
加载该工具后,在 UI 中转换您的
<script>
以显示基准测试结果并执行它们。结果显示为 JSON 并剪切到前 500 个字符左右。
一个简单的基准可能是
// @benchmark Solution 1
其中
@benchmark
后面的所有内容都是解决方案的名称。
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 个变量:
$chunk
- 组成输入的块。可以是字符串、数组或返回其中任意一个的函数。$input
- 用于填充块的数组或字符串。$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));