背景:我正在节点端构建后端并优化带宽。基本上我试图不发送客户端已经拥有的缩略图(存储为 10KiB 字符串),yada yada,在短时间内我直接比较缩略图数据,因为我无法确定它们是否已更新但已移动此后采取不同的解决方案。这只是一个好奇心,不再有任何实际意义。
好吧,我在 V8 中发现了这种行为,默认情况下,大字符串比较很慢,但是(大概)对这些字符串进行哈希处理(例如将它们粘贴到 Set 中),比较速度会加快很多。我已经确认这发生在节点以及基于铬的电子前端。
示例:
(()=>{
// making 10k character strings
const big_strings = [];
const palette = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
for (let i = 0; i < 100; i++) {
let big_string = "";
for (let i = 0; i < 10_000; i++) {
big_string += palette[Math.floor(Math.random() * palette.length)];
}
big_strings.push(big_string);
}
// comparison functions
function cmp1(a, b) {
return a === b;
}
function cmp2(a, b) {
const lol = new Set();
lol.add(a);
lol.add(b);
return a === b;
}
// tests
let sorted_1, sorted_2;
console.time("Test 1");
for (let i = 0; i < 100; i++) {
sorted_1 = big_strings.slice();
sorted_1.sort(cmp1);
}
console.timeEnd("Test 1");
console.time("Test 1");
for (let i = 0; i < 100; i++) {
sorted_1 = big_strings.slice();
sorted_1.sort(cmp1);
}
console.timeEnd("Test 1");
console.time("Test 1");
for (let i = 0; i < 100; i++) {
sorted_1 = big_strings.slice();
sorted_1.sort(cmp1);
}
console.timeEnd("Test 1");
console.time("Test 2");
for (let i = 0; i < 100; i++) {
sorted_2 = big_strings.slice();
sorted_2.sort(cmp2);
}
console.timeEnd("Test 2");
console.time("Test 1");
for (let i = 0; i < 100; i++) {
sorted_1 = big_strings.slice();
sorted_1.sort(cmp1);
}
console.timeEnd("Test 1");
// sanity check
let good = 0;
const total = Math.max(sorted_1.length, sorted_2.length);
for (let i = 0; i < total; i++) {
if (sorted_1[i] === sorted_2[i]) {
good++;
} else {
throw("Results do not match.");
}
}
console.log(`Sanity check OK ${good} / ${total}`);
})();
在示例中,有两个比较函数,一个简单地执行 a === b,另一个首先将 a 和 b 粘贴到一次性 Set 中。我可以根据需要多次使用第一个比较函数运行测试(本例中为 3 次),但它总是很慢。然后我使用第二个比较函数运行测试一次,之后,对这些特定字符串的所有测试都会非常快。
这种行为会带来什么后果?为什么 V8 不在后台对字符串本身进行哈希处理?
这里不进行比较,而是进行相等,并且似乎使用
Set
可以优化它。 Afaik V8 字符串是对象,并且可能存在与 Set
相关的一些相等性检查兑现,因为有一个全局字符串注册表,并且相同的字符串是相同的字符串对象。
确实可能会发生一些哈希、缓存和优化,但我根本不会依赖它们。该实现是一个黑匣子,所以不要尝试施展魔法。
关于您的示例,创建集合时对实际比较没有帮助,只会减慢速度。
` Chrome/123
---------------------------------------------------------------------------------
> n=10 | n=100 | n=1000 | n=10000
Test 1 ■ 1.00x x100k 692 | ■ 1.00x x10k 338 | ■ 1.00x x1k 138 | ■ 1.00x x10 56
Test 2 1.18x x100k 819 | 1.74x x10k 589 | 2.87x x1k 396 | 1.96x x10 110
---------------------------------------------------------------------------------
https://github.com/silentmantra/benchmark `
// making 10k character strings
let $length = 10;
const big_strings = [];
const palette = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
for (let i = 0; i < $length; i++) {
let big_string = "";
for (let i = 0; i < 10_0; i++) {
big_string += palette[Math.floor(Math.random() * palette.length)];
}
big_strings.push(big_string);
}
let $input = big_strings;
// comparison functions
function cmp1(a, b) {
return a > b ? 1 : a < b ? -1 : 0;
}
function cmp2(a, b) {
const lol = new Set();
lol.add(a);
lol.add(b);
return a > b ? 1 : a < b ? -1 : 0;
}
// @benchmark Test 1
$input.toSorted(cmp1);
// @benchmark Test 2
$input.toSorted(cmp2);
/*@skip*/ fetch('https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js').then(r => r.text().then(eval));