mirror of
https://github.com/nodejs/node.git
synced 2026-10-29 02:35:20 +08:00
StringBytes::Write() already used simdutf to encode one-byte strings as UTF-8 but sent every two-byte (UTF-16) string through v8::String::WriteUtf8V2(), which is several times slower. That path is behind Buffer.from(string), buf.write(), fs.write*() with string data and every string written to a libuv stream, and JSON.stringify() output is a two-byte string as soon as any value in the payload is outside Latin-1. Encode two-byte strings with simdutf as well whenever their UTF-8 form is guaranteed to fit in the target: well-formed input is converted directly, and input with unpaired surrogates is converted from a copy passed through simdutf::to_well_formed_utf16(), which replaces each unpaired surrogate with U+FFFD exactly like kReplaceInvalidUtf8 (this mirrors what TextEncoder already does). Writes that have to truncate at a character boundary keep using WriteUtf8V2(), so their output is byte-for-byte unchanged, and so do strings of up to 32 code units, for which V8 is already as fast (the same threshold TextEncoder uses). buf.write() of a 2 KiB two-byte string improves ~5x (astral-heavy and lone-surrogate strings ~3.5x and ~5x), Buffer.from() of a 64 KiB JSON string ~2.7x; one-byte strings are unaffected. Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com> PR-URL: https://github.com/nodejs/node/pull/65324 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day> Reviewed-By: Daniel Lemire <daniel@lemire.me> Reviewed-By: James M Snell <jasnell@gmail.com>
37 lines
1.1 KiB
JavaScript
37 lines
1.1 KiB
JavaScript
'use strict';
|
|
|
|
// buf.write(string, 'utf8') for strings whose in-memory representation is
|
|
// one-byte (Latin-1) or two-byte (UTF-16), which take different encoder paths.
|
|
const common = require('../common.js');
|
|
const bench = common.createBenchmark(main, {
|
|
chars: ['one-byte', 'two-byte', 'two-byte-astral', 'two-byte-lone-surrogate'],
|
|
len: [16, 256, 2048, 65536],
|
|
n: [5e5],
|
|
});
|
|
|
|
function makeString(chars, len) {
|
|
switch (chars) {
|
|
case 'one-byte':
|
|
return 'aé'.repeat(len / 2);
|
|
case 'two-byte':
|
|
return 'aé€日'.repeat(len / 4);
|
|
case 'two-byte-astral':
|
|
return 'aé€日\u{1F600}'.repeat(len / 6).padEnd(len, 'a');
|
|
case 'two-byte-lone-surrogate':
|
|
return 'aé€日'.repeat(len / 4 - 1) + 'ab\ud800c';
|
|
default:
|
|
throw new Error(chars);
|
|
}
|
|
}
|
|
|
|
function main({ chars, len, n }) {
|
|
const string = makeString(chars, len);
|
|
const buf = Buffer.allocUnsafe(Buffer.byteLength(string));
|
|
if (len >= 65536) n = Math.floor(n / 32);
|
|
bench.start();
|
|
for (let i = 0; i < n; ++i) {
|
|
buf.write(string, 0, 'utf8');
|
|
}
|
|
bench.end(n);
|
|
}
|