Skip to content

Commit

Permalink
[vm/ffi] Optimize struct copies
Browse files Browse the repository at this point in the history
Replace the `_memCopy` implementation in Dart with `MemoryCopyInstr`.

Speeds up copying from and to `Pointer`s by up to a 100x for large
structs.

TEST=tests/ffi/function_callbacks_structs_by_value_test.dart

BENCHMARK=benchmarks/FfiStructCopy/dart/FfiStructCopy.dart

Before (x64 JIT):

FfiStructCopy.Copy1Bytes(RunTimeRaw): 86.26189953740506 ns.
FfiStructCopy.Copy16Bytes(RunTimeRaw): 14.006969563521945 ns.
FfiStructCopy.Copy1024Bytes(RunTimeRaw): 8.567228629242734 ns.
FfiStructCopy.Copy65536Bytes(RunTimeRaw): 8.469047080663412 ns.

After (x64 JIT):

FfiStructCopy.Copy1Bytes(RunTimeRaw): 56.79513144264321 ns.
FfiStructCopy.Copy16Bytes(RunTimeRaw): 3.562479879234367 ns.
FfiStructCopy.Copy1024Bytes(RunTimeRaw): 0.10771875669750132 ns.
FfiStructCopy.Copy65536Bytes(RunTimeRaw): 0.02758346614218262 ns.

Bug: #43967
Change-Id: I5d51c39a13b8c9522ee88d81bba7365caef70a27
Cq-Include-Trybots: luci.dart.try:vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64c-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-precomp-ffi-qemu-linux-release-riscv64-try,vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-reload-linux-debug-x64-try,vm-kernel-nnbd-mac-debug-arm64-try,vm-kernel-nnbd-mac-debug-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-debug-x64c-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/277523
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Aske Simon Christensen <askesc@google.com>
  • Loading branch information
dcharkes authored and Commit Queue committed Jan 2, 2023
1 parent 81b137d commit 5d0325d
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 38 deletions.
20 changes: 20 additions & 0 deletions runtime/vm/compiler/frontend/kernel_to_il.cc
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,7 @@ bool FlowGraphBuilder::IsRecognizedMethodForFlowGraph(
case MethodRecognizer::kTypedData_Float32x4Array_factory:
case MethodRecognizer::kTypedData_Int32x4Array_factory:
case MethodRecognizer::kTypedData_Float64x2Array_factory:
case MethodRecognizer::kMemCopy:
case MethodRecognizer::kFfiLoadInt8:
case MethodRecognizer::kFfiLoadInt16:
case MethodRecognizer::kFfiLoadInt32:
Expand Down Expand Up @@ -1232,6 +1233,25 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfRecognizedMethod(
body += Utf8Scan();
body += Box(kUnboxedIntPtr);
break;
case MethodRecognizer::kMemCopy: {
ASSERT_EQUAL(function.NumParameters(), 5);
LocalVariable* arg_target = parsed_function_->RawParameterVariable(0);
LocalVariable* arg_target_offset_in_bytes =
parsed_function_->RawParameterVariable(1);
LocalVariable* arg_source = parsed_function_->RawParameterVariable(2);
LocalVariable* arg_source_offset_in_bytes =
parsed_function_->RawParameterVariable(3);
LocalVariable* arg_length_in_bytes =
parsed_function_->RawParameterVariable(4);
body += LoadLocal(arg_source);
body += LoadLocal(arg_target);
body += LoadLocal(arg_source_offset_in_bytes);
body += LoadLocal(arg_target_offset_in_bytes);
body += LoadLocal(arg_length_in_bytes);
// Pointers and TypedData have the same layout.
body += MemoryCopy(kTypedDataUint8ArrayCid, kTypedDataUint8ArrayCid);
body += NullConstant();
} break;
case MethodRecognizer::kFfiAbi:
ASSERT_EQUAL(function.NumParameters(), 0);
body += IntConstant(static_cast<int64_t>(compiler::ffi::TargetAbi()));
Expand Down
1 change: 1 addition & 0 deletions runtime/vm/compiler/recognized_methods_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ namespace dart {
V(_Smi, get:hashCode, Smi_hashCode, 0x75e0ccd2) \
V(_Mint, get:hashCode, Mint_hashCode, 0x75e0ccd2) \
V(_Double, get:hashCode, Double_hashCode, 0x75e0ccd2) \
V(::, _memCopy, MemCopy, 0x274f4816) \

// List of intrinsics:
// (class-name, function-name, intrinsification method, fingerprint).
Expand Down
42 changes: 4 additions & 38 deletions sdk/lib/_internal/vm/lib/ffi_patch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -246,44 +246,10 @@ class _FfiAbiSpecificMapping {
/// Copies data byte-wise from [source] to [target].
///
/// [source] and [target] should either be [Pointer] or [TypedData].
///
/// TODO(dartbug.com/37271): Make recognized method and use MemoryCopyInstr.
void _memCopy(Object target, int targetOffsetInBytes, Object source,
int sourceOffsetInBytes, int lengthInBytes) {
assert(source is Pointer || source is TypedData);
assert(target is Pointer || target is TypedData);
if (source is Pointer) {
final sourcePointer = source.cast<Uint8>();
if (target is Pointer) {
final targetPointer = target.cast<Uint8>();
for (int i = 0; i < lengthInBytes; i++) {
targetPointer[i + targetOffsetInBytes] =
sourcePointer[i + sourceOffsetInBytes];
}
} else if (target is TypedData) {
final targetTypedData = target.buffer.asUint8List(target.offsetInBytes);
for (int i = 0; i < lengthInBytes; i++) {
targetTypedData[i + targetOffsetInBytes] =
sourcePointer[i + sourceOffsetInBytes];
}
}
} else if (source is TypedData) {
final sourceTypedData = source.buffer.asUint8List(source.offsetInBytes);
if (target is Pointer) {
final targetPointer = target.cast<Uint8>();
for (int i = 0; i < lengthInBytes; i++) {
targetPointer[i + targetOffsetInBytes] =
sourceTypedData[i + sourceOffsetInBytes];
}
} else if (target is TypedData) {
final targetTypedData = target.buffer.asUint8List(target.offsetInBytes);
targetTypedData.setRange(
targetOffsetInBytes,
targetOffsetInBytes + lengthInBytes,
sourceTypedData.sublist(sourceOffsetInBytes));
}
}
}
@pragma("vm:entry-point")
@pragma("vm:recognized", "other")
external void _memCopy(Object target, int targetOffsetInBytes, Object source,
int sourceOffsetInBytes, int lengthInBytes);

// The following functions are implemented in the method recognizer.
//
Expand Down

0 comments on commit 5d0325d

Please sign in to comment.