Skip to content

Commit 9782ca2

Browse files
gurgundaytargos
authored andcommitted
zlib: implement fast path for crc32
PR-URL: #59813 Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Daniel Lemire <[email protected]> Reviewed-By: Trivikram Kamat <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]>
1 parent fed1dac commit 9782ca2

File tree

3 files changed

+100
-10
lines changed

3 files changed

+100
-10
lines changed

‎benchmark/zlib/crc32.js‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict';
2+
3+
constcommon=require('../common.js');
4+
const{ crc32 }=require('zlib');
5+
6+
// Benchmark crc32 on Buffer and String inputs across sizes.
7+
// Iteration count is scaled inversely with input length to keep runtime sane.
8+
// Example:
9+
// node benchmark/zlib/crc32.js type=buffer len=4096 n=4000000
10+
// ./out/Release/node benchmark/zlib/crc32.js --test
11+
12+
constbench=common.createBenchmark(main,{
13+
type: ['buffer','string'],
14+
len: [32,256,4096,65536],
15+
n: [4e6],
16+
});
17+
18+
functionmakeBuffer(size){
19+
constbuf=Buffer.allocUnsafe(size);
20+
for(leti=0;i<size;i++)buf[i]=(i*1103515245+12345)&0xff;
21+
returnbuf;
22+
}
23+
24+
functionmakeAsciiString(size){
25+
constchars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
26+
letout='';
27+
for(leti=0,j=0;i<size;i++,j=(j+7)%chars.length)out+=chars[j];
28+
returnout;
29+
}
30+
31+
functionmain({ type, len, n }){
32+
// Scale iterations so that total processed bytes roughly constant around n*4096 bytes.
33+
constscale=4096/len;
34+
constiters=Math.max(1,Math.floor(n*scale));
35+
36+
constdata=type==='buffer' ? makeBuffer(len) : makeAsciiString(len);
37+
38+
letacc=0;
39+
for(leti=0;i<Math.min(iters,10000);i++)acc^=crc32(data,0);
40+
41+
bench.start();
42+
letsum=0;
43+
for(leti=0;i<iters;i++)sum^=crc32(data,0);
44+
bench.end(iters);
45+
46+
if(sum===acc-1)process.stderr.write('');
47+
}

‎src/node_zlib.cc‎

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include"threadpoolwork-inl.h"
3131
#include"util-inl.h"
3232

33+
#include"node_debug.h"
34+
#include"v8-fast-api-calls.h"
3335
#include"v8.h"
3436

3537
#include"brotli/decode.h"
@@ -48,6 +50,7 @@
4850
namespacenode{
4951

5052
using v8::ArrayBuffer;
53+
using v8::CFunction;
5154
using v8::Context;
5255
using v8::Function;
5356
using v8::FunctionCallbackInfo;
@@ -1657,22 +1660,35 @@ T CallOnSequence(v8::Isolate* isolate, Local<Value> value, F callback){
16571660
}
16581661
}
16591662

1660-
// TODO(joyeecheung): use fast API
1663+
staticinlineuint32_tCRC32Impl(Isolate* isolate,
1664+
Local<Value> data,
1665+
uint32_t value){
1666+
return CallOnSequence<uint32_t>(
1667+
isolate, data, [&](constchar* ptr, size_t size) -> uint32_t{
1668+
returnstatic_cast<uint32_t>(
1669+
crc32(value, reinterpret_cast<const Bytef*>(ptr), size));
1670+
});
1671+
}
1672+
16611673
staticvoidCRC32(const FunctionCallbackInfo<Value>& args){
16621674
CHECK(args[0]->IsArrayBufferView() || args[0]->IsString());
16631675
CHECK(args[1]->IsUint32());
16641676
uint32_t value = args[1].As<v8::Uint32>()->Value();
1677+
args.GetReturnValue().Set(CRC32Impl(args.GetIsolate(), args[0], value));
1678+
}
16651679

1666-
uint32_tresult = CallOnSequence<uint32_t>(
1667-
args.GetIsolate(),
1668-
args[0],
1669-
[&](constchar* data, size_t size) -> uint32_t{
1670-
returncrc32(value, reinterpret_cast<const Bytef*>(data), size);
1671-
});
1672-
1673-
args.GetReturnValue().Set(result);
1680+
staticuint32_tFastCRC32(v8::Local<v8::Value> receiver,
1681+
v8::Local<v8::Value> data,
1682+
uint32_t value,
1683+
// NOLINTNEXTLINE(runtime/references)
1684+
v8::FastApiCallbackOptions& options){
1685+
TRACK_V8_FAST_API_CALL("zlib.crc32");
1686+
v8::HandleScope handle_scope(options.isolate);
1687+
returnCRC32Impl(options.isolate, data, value);
16741688
}
16751689

1690+
static CFunction fast_crc32_(CFunction::Make(FastCRC32));
1691+
16761692
voidInitialize(Local<Object> target,
16771693
Local<Value> unused,
16781694
Local<Context> context,
@@ -1685,7 +1701,7 @@ void Initialize(Local<Object> target,
16851701
MakeClass<ZstdCompressStream>::Make(env, target, "ZstdCompress");
16861702
MakeClass<ZstdDecompressStream>::Make(env, target, "ZstdDecompress");
16871703

1688-
SetMethod(context, target, "crc32", CRC32);
1704+
SetFastMethodNoSideEffect(context, target, "crc32", CRC32, &fast_crc32_);
16891705
target->Set(env->context(),
16901706
FIXED_ONE_BYTE_STRING(env->isolate(), "ZLIB_VERSION"),
16911707
FIXED_ONE_BYTE_STRING(env->isolate(), ZLIB_VERSION)).Check();
@@ -1698,6 +1714,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry){
16981714
MakeClass<ZstdCompressStream>::Make(registry);
16991715
MakeClass<ZstdDecompressStream>::Make(registry);
17001716
registry->Register(CRC32);
1717+
registry->Register(fast_crc32_);
17011718
}
17021719

17031720
} // anonymous namespace
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Flags: --expose-internals --no-warnings --allow-natives-syntax
2+
'use strict';
3+
4+
constcommon=require('../common');
5+
constassert=require('assert');
6+
constzlib=require('zlib');
7+
8+
{
9+
functiontestFastPath(){
10+
constexpected=0xd87f7e0c;// zlib.crc32('test', 0)
11+
assert.strictEqual(zlib.crc32('test',0),expected);
12+
returnexpected;
13+
}
14+
15+
eval('%PrepareFunctionForOptimization(zlib.crc32)');
16+
testFastPath();
17+
eval('%OptimizeFunctionOnNextCall(zlib.crc32)');
18+
testFastPath();
19+
testFastPath();
20+
21+
if(common.isDebug){
22+
const{ internalBinding }=require('internal/test/binding');
23+
const{ getV8FastApiCallCount }=internalBinding('debug');
24+
assert.strictEqual(getV8FastApiCallCount('zlib.crc32'),2);
25+
}
26+
}

0 commit comments

Comments
(0)