Files
sdk/runtime/vm/compiler/assembler/assembler_test.cc
T
Tess Strickland 49f998dc31 [vm/compiler] Handle hash caches in the InstantiateTypeArguments stubs.
Lower the threshold for converting from a linear to a hash-based cache
on most architectures from 500 to 10.

Due to register pressure, the InstantiateTypeArguments stub on IA32
continues to go to the runtime for hash caches, and so we do not
lower the threshold for converting to a hash-based cache there.

The following are benchmark results for those benchmark that use enough
Instantiations to trigger the use of hash-based caches. In the following
tables, "Results 1" denotes the benchmark results from only this change,
whereas "Results 2" include from comparing to the results prior to
4f925105cf, when only linear caches were used.

Dart AOT:

* InstantiateTypeArguments.Instantiate100

Arch   | CPU            | Results 1 | Results 2
-------|----------------|-----------------------
ARM    | Odroid-C2      |   382.8%  |   381.5%
ARM    | Raspberry Pi 4 |   486.7%  |   449.2%
ARM64  | Odroid-C2      |   328.1%  |   372.8%
ARM64  | Raspberry Pi 4 |  1283%    |  1281%
ARM64C | Raspberry Pi 4 |  2353%    |  2811%
X64    | Intel Xeon     |   568.7%  |   584.9%

* InstantiateTypeArguments.Instantiate1000

Arch   | CPU            | Results 1  | Results 2
-------|----------------|------------------------
ARM    | Odroid-C2      |   743.7%   |  3821%
ARM    | Raspberry Pi 4 |   486.7%   |  3218%
ARM64  | Odroid-C2      |   584.7%   |  3222%
ARM64  | Raspberry Pi 4 |   430.7%   |  8172%
ARM64C | Raspberry Pi 4 |   491.4%   | 16699%
X64    | Intel Xeon     |   954.1%   |  5528%


Dart JIT:

* InstantiateTypeArguments.Instantiate100

Arch   | CPU            | Results 1 | Results 2
-------|----------------|-----------------------
ARM    | Raspberry Pi 4 |   315.7%  |   295.1%
ARM64  | Raspberry Pi 4 |  1070%    |  1058%
ARM64C | Raspberry Pi 4 |  1769%    |  2095%
X64    | Intel Xeon     |   507.4%  |   496.2%

* InstantiateTypeArguments.Instantiate1000

Arch   | CPU            | Results 1 | Results 2
-------|----------------|-----------------------
ARM    | Raspberry Pi 4 |   565.2%  |  2550%
ARM64  | Raspberry Pi 4 |   406.8%  |  7375%
ARM64C | Raspberry Pi 4 |   379.6%  | 12996%
X64    | Intel Xeon     |   807.9%  |  4459%

During work on this change, an issue was found where cache lookups
in the stub on ARM64C always failed and went to runtime, even with
the old linear-only caches, hence the much larger performance gains
in those rows above.

TEST=vm/cc/TypeArguments_Cache_{Some,Many}Instantiations

Fixes: https://github.com/dart-lang/sdk/issues/48344
Change-Id: I3d29566ba0582502954c9fc59626ceb8fd40317a
Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-dwarf-linux-product-x64-try,vm-kernel-precomp-linux-product-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-nnbd-mac-release-arm64-try,vm-kernel-precomp-nnbd-linux-release-simarm_x64-try,vm-kernel-precomp-linux-release-simarm-try,vm-kernel-precomp-nnbd-linux-release-x64-try,vm-kernel-precomp-nnbd-linux-release-simarm64-try,vm-kernel-precomp-nnbd-linux-debug-simriscv64-try,vm-kernel-precomp-tsan-linux-release-x64-try,vm-kernel-tsan-linux-release-x64-try,vm-kernel-precomp-linux-debug-x64c-try,vm-kernel-nnbd-linux-debug-simriscv64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try,vm-kernel-linux-release-ia32-try,vm-kernel-nnbd-linux-release-simarm-try,vm-kernel-nnbd-linux-release-simarm64-try,vm-kernel-nnbd-linux-release-ia32-try,vm-kernel-nnbd-mac-release-arm64-try,vm-kernel-linux-debug-x64c-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/270702
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
2022-12-01 10:15:58 +00:00

140 lines
5.0 KiB
C++

// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/compiler/assembler/assembler.h"
#include "vm/globals.h"
#include "vm/hash.h"
#include "vm/os.h"
#include "vm/random.h"
#include "vm/simulator.h"
#include "vm/unit_test.h"
#include "vm/virtual_memory.h"
namespace dart {
namespace compiler {
ASSEMBLER_TEST_EXTERN(StoreIntoObject);
} // namespace compiler
ASSEMBLER_TEST_RUN(StoreIntoObject, test) {
#define TEST_CODE(value, growable_array, thread) \
test->Invoke<void, ObjectPtr, ObjectPtr, Thread*>(value, growable_array, \
thread)
const Array& old_array = Array::Handle(Array::New(3, Heap::kOld));
const Array& new_array = Array::Handle(Array::New(3, Heap::kNew));
const GrowableObjectArray& grow_old_array = GrowableObjectArray::Handle(
GrowableObjectArray::New(old_array, Heap::kOld));
const GrowableObjectArray& grow_new_array = GrowableObjectArray::Handle(
GrowableObjectArray::New(old_array, Heap::kNew));
Smi& smi = Smi::Handle();
Thread* thread = Thread::Current();
EXPECT(old_array.ptr() == grow_old_array.data());
EXPECT(!thread->StoreBufferContains(grow_old_array.ptr()));
EXPECT(old_array.ptr() == grow_new_array.data());
EXPECT(!thread->StoreBufferContains(grow_new_array.ptr()));
// Store Smis into the old object.
for (int i = -128; i < 128; i++) {
smi = Smi::New(i);
TEST_CODE(smi.ptr(), grow_old_array.ptr(), thread);
EXPECT(static_cast<CompressedObjectPtr>(smi.ptr()) ==
static_cast<CompressedObjectPtr>(grow_old_array.data()));
EXPECT(!thread->StoreBufferContains(grow_old_array.ptr()));
}
// Store an old object into the old object.
TEST_CODE(old_array.ptr(), grow_old_array.ptr(), thread);
EXPECT(old_array.ptr() == grow_old_array.data());
EXPECT(!thread->StoreBufferContains(grow_old_array.ptr()));
// Store a new object into the old object.
TEST_CODE(new_array.ptr(), grow_old_array.ptr(), thread);
EXPECT(new_array.ptr() == grow_old_array.data());
EXPECT(thread->StoreBufferContains(grow_old_array.ptr()));
// Store a new object into the new object.
TEST_CODE(new_array.ptr(), grow_new_array.ptr(), thread);
EXPECT(new_array.ptr() == grow_new_array.data());
EXPECT(!thread->StoreBufferContains(grow_new_array.ptr()));
// Store an old object into the new object.
TEST_CODE(old_array.ptr(), grow_new_array.ptr(), thread);
EXPECT(old_array.ptr() == grow_new_array.data());
EXPECT(!thread->StoreBufferContains(grow_new_array.ptr()));
}
namespace compiler {
#define __ assembler->
ASSEMBLER_TEST_GENERATE(InstantiateTypeArgumentsHashKeys, assembler) {
#if defined(TARGET_ARCH_IA32)
const Register kArg1Reg = EAX;
const Register kArg2Reg = ECX;
__ movl(kArg1Reg, Address(ESP, 2 * target::kWordSize));
__ movl(kArg2Reg, Address(ESP, 1 * target::kWordSize));
#else
const Register kArg1Reg = CallingConventions::ArgumentRegisters[0];
const Register kArg2Reg = CallingConventions::ArgumentRegisters[1];
#endif
__ CombineHashes(kArg1Reg, kArg2Reg);
__ FinalizeHash(kArg1Reg, kArg2Reg);
__ MoveRegister(CallingConventions::kReturnReg, kArg1Reg);
__ Ret();
}
#undef __
} // namespace compiler
ASSEMBLER_TEST_RUN(InstantiateTypeArgumentsHashKeys, test) {
typedef uint32_t (*HashKeysCode)(uword hash, uword other) DART_UNUSED;
auto hash_test = [&](const Expect& expect, uword hash1, uword hash2) {
const uint32_t expected = FinalizeHash(CombineHashes(hash1, hash2));
const uint32_t got = EXECUTE_TEST_CODE_UWORD_UWORD_UINT32(
HashKeysCode, test->entry(), hash1, hash2);
if (got == expected) return;
TextBuffer buffer(128);
buffer.Printf("For hash1 = %" Pu " and hash2 = %" Pu
": expected result %u, got result %u",
hash1, hash2, expected, got);
expect.Fail("%s", buffer.buffer());
};
#define HASH_TEST(hash1, hash2) \
hash_test(Expect(__FILE__, __LINE__), hash1, hash2)
const intptr_t kNumRandomTests = 500;
Random random;
// First, fixed and random 32 bit tests for all architectures.
HASH_TEST(1, 1);
HASH_TEST(10, 20);
HASH_TEST(20, 10);
HASH_TEST(kMaxUint16, kMaxUint32 - kMaxUint16);
HASH_TEST(kMaxUint32 - kMaxUint16, kMaxUint16);
for (intptr_t i = 0; i < kNumRandomTests; i++) {
const uword hash1 = random.NextUInt32();
const uword hash2 = random.NextUInt32();
HASH_TEST(hash1, hash2);
}
#if defined(TARGET_ARCH_IS_64_BIT)
// Now 64-bit tests on 64-bit architectures.
HASH_TEST(kMaxUint16, kMaxUint64 - kMaxUint16);
HASH_TEST(kMaxUint64 - kMaxUint16, kMaxUint16);
for (intptr_t i = 0; i < kNumRandomTests; i++) {
const uword hash1 = random.NextUInt64();
const uword hash2 = random.NextUInt64();
HASH_TEST(hash1, hash2);
}
#endif
#undef HASH_TEST
}
} // namespace dart