From 35057828017778c45d57077b64829fd4f6b12539 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Sun, 16 Jun 2024 19:44:58 +0200 Subject: [PATCH] buffer: make indexOf(byte) faster Add a V8 fast API implementation for indexOfNumber, which significantly improves the performance of finding a single byte within a buffer. Refs: https://github.com/nodejs/node/pull/52993 PR-URL: https://github.com/nodejs/node/pull/53455 Reviewed-By: Yagiz Nizipli Reviewed-By: Robert Nagy Reviewed-By: Mohammed Keyvanzadeh --- src/node_buffer.cc | 67 ++++++++++++++++++++++++----------- src/node_external_reference.h | 7 ++++ 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/src/node_buffer.cc b/src/node_buffer.cc index e7e3ead4416a40..527a4cf4a078d6 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -57,6 +57,7 @@ using v8::ArrayBufferView; using v8::BackingStore; using v8::Context; using v8::EscapableHandleScope; +using v8::FastApiTypedArray; using v8::FunctionCallbackInfo; using v8::Global; using v8::HandleScope; @@ -1071,37 +1072,57 @@ void IndexOfBuffer(const FunctionCallbackInfo& args) { result == haystack_length ? -1 : static_cast(result)); } -void IndexOfNumber(const FunctionCallbackInfo& args) { +int32_t IndexOfNumber(const uint8_t* buffer_data, + size_t buffer_length, + uint32_t needle, + int64_t offset_i64, + bool is_forward) { + int64_t opt_offset = IndexOfOffset(buffer_length, offset_i64, 1, is_forward); + if (opt_offset <= -1 || buffer_length == 0) { + return -1; + } + size_t offset = static_cast(opt_offset); + CHECK_LT(offset, buffer_length); + + const void* ptr; + if (is_forward) { + ptr = memchr(buffer_data + offset, needle, buffer_length - offset); + } else { + ptr = node::stringsearch::MemrchrFill(buffer_data, needle, offset + 1); + } + const uint8_t* ptr_uint8 = static_cast(ptr); + return ptr != nullptr ? static_cast(ptr_uint8 - buffer_data) : -1; +} + +void SlowIndexOfNumber(const FunctionCallbackInfo& args) { CHECK(args[1]->IsUint32()); CHECK(args[2]->IsNumber()); CHECK(args[3]->IsBoolean()); THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); - ArrayBufferViewContents buffer(args[0]); + ArrayBufferViewContents buffer(args[0]); uint32_t needle = args[1].As()->Value(); int64_t offset_i64 = args[2].As()->Value(); bool is_forward = args[3]->IsTrue(); - int64_t opt_offset = - IndexOfOffset(buffer.length(), offset_i64, 1, is_forward); - if (opt_offset <= -1 || buffer.length() == 0) { - return args.GetReturnValue().Set(-1); - } - size_t offset = static_cast(opt_offset); - CHECK_LT(offset, buffer.length()); + args.GetReturnValue().Set(IndexOfNumber( + buffer.data(), buffer.length(), needle, offset_i64, is_forward)); +} - const void* ptr; - if (is_forward) { - ptr = memchr(buffer.data() + offset, needle, buffer.length() - offset); - } else { - ptr = node::stringsearch::MemrchrFill(buffer.data(), needle, offset + 1); - } - const char* ptr_char = static_cast(ptr); - args.GetReturnValue().Set(ptr ? static_cast(ptr_char - buffer.data()) - : -1); +int32_t FastIndexOfNumber(v8::Local, + const FastApiTypedArray& buffer, + uint32_t needle, + int64_t offset_i64, + bool is_forward) { + uint8_t* buffer_data; + CHECK(buffer.getStorageIfAligned(&buffer_data)); + return IndexOfNumber( + buffer_data, buffer.length(), needle, offset_i64, is_forward); } +static v8::CFunction fast_index_of_number( + v8::CFunction::Make(FastIndexOfNumber)); void Swap16(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); @@ -1413,7 +1434,11 @@ void Initialize(Local target, SetMethodNoSideEffect(context, target, "compareOffset", CompareOffset); SetMethod(context, target, "fill", Fill); SetMethodNoSideEffect(context, target, "indexOfBuffer", IndexOfBuffer); - SetMethodNoSideEffect(context, target, "indexOfNumber", IndexOfNumber); + SetFastMethodNoSideEffect(context, + target, + "indexOfNumber", + SlowIndexOfNumber, + &fast_index_of_number); SetMethodNoSideEffect(context, target, "indexOfString", IndexOfString); SetMethod(context, target, "detachArrayBuffer", DetachArrayBuffer); @@ -1472,7 +1497,9 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) { registry->Register(CompareOffset); registry->Register(Fill); registry->Register(IndexOfBuffer); - registry->Register(IndexOfNumber); + registry->Register(SlowIndexOfNumber); + registry->Register(FastIndexOfNumber); + registry->Register(fast_index_of_number.GetTypeInfo()); registry->Register(IndexOfString); registry->Register(Swap16); diff --git a/src/node_external_reference.h b/src/node_external_reference.h index a3317d25ad6a96..4e2ad9024020fa 100644 --- a/src/node_external_reference.h +++ b/src/node_external_reference.h @@ -27,6 +27,12 @@ using CFunctionCallbackWithStrings = bool (*)(v8::Local, const v8::FastOneByteString& input, const v8::FastOneByteString& base); +using CFunctionCallbackWithUint8ArrayUint32Int64Bool = + int32_t (*)(v8::Local, + const v8::FastApiTypedArray&, + uint32_t, + int64_t, + bool); using CFunctionWithUint32 = uint32_t (*)(v8::Local, const uint32_t input); using CFunctionWithDoubleReturnDouble = double (*)(v8::Local, @@ -51,6 +57,7 @@ class ExternalReferenceRegistry { V(CFunctionCallbackWithBool) \ V(CFunctionCallbackWithString) \ V(CFunctionCallbackWithStrings) \ + V(CFunctionCallbackWithUint8ArrayUint32Int64Bool) \ V(CFunctionWithUint32) \ V(CFunctionWithDoubleReturnDouble) \ V(CFunctionWithInt64Fallback) \