Skip to content

Commit c2f07a6

Browse files
authored
Merge pull request #3776 from apple/stdlib-AnyHashable-caching
2 parents af591bc + b0481b1 commit c2f07a6

File tree

4 files changed

+116
-10
lines changed

4 files changed

+116
-10
lines changed

benchmark/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ set(SWIFT_BENCH_MODULES
2121
single-source/unit-tests/StackPromo
2222
single-source/Ackermann
2323
single-source/AngryPhonebook
24+
single-source/AnyHashableWithAClass
2425
single-source/Array2D
2526
single-source/ArrayAppend
2627
single-source/ArrayInClass
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
// This benchmark tests AnyHashable's initializer that needs to dynamically
14+
// upcast the instance to the type that introduces the Hashable
15+
// conformance.
16+
17+
class TestHashableBase : Hashable {
18+
var value: Int
19+
init(_ value: Int) {
20+
self.value = value
21+
}
22+
var hashValue: Int {
23+
return value
24+
}
25+
static func == (
26+
lhs: TestHashableBase,
27+
rhs: TestHashableBase
28+
) -> Bool {
29+
return lhs.value == rhs.value
30+
}
31+
}
32+
33+
class TestHashableDerived1 : TestHashableBase {}
34+
class TestHashableDerived2 : TestHashableDerived1 {}
35+
class TestHashableDerived3 : TestHashableDerived2 {}
36+
class TestHashableDerived4 : TestHashableDerived3 {}
37+
class TestHashableDerived5 : TestHashableDerived4 {}
38+
39+
@inline(never)
40+
public func run_AnyHashableWithAClass(_ N: Int) {
41+
let c = TestHashableDerived5(10)
42+
for _ in 0...(N*500000) {
43+
_ = AnyHashable(c)
44+
}
45+
}
46+

benchmark/utils/main.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import TestsUtils
2222
import DriverUtils
2323
import Ackermann
2424
import AngryPhonebook
25+
import AnyHashableWithAClass
2526
import Array2D
2627
import ArrayAppend
2728
import ArrayInClass
@@ -100,6 +101,7 @@ import XorLoop
100101

101102
precommitTests = [
102103
"AngryPhonebook": run_AngryPhonebook,
104+
"AnyHashableWithAClass": run_AnyHashableWithAClass,
103105
"Array2D": run_Array2D,
104106
"ArrayAppend": run_ArrayAppend,
105107
"ArrayAppendReserved": run_ArrayAppendReserved,

stdlib/public/stubs/AnyHashableSupport.cpp

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,79 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "swift/Runtime/Config.h"
14+
#include "swift/Basic/Lazy.h"
15+
#include "swift/Runtime/Concurrent.h"
1416
#include "swift/Runtime/Debug.h"
1517
#include "swift/Runtime/Metadata.h"
1618
#include "../runtime/Private.h"
1719

1820
using namespace swift;
1921

22+
/// The name demangles to "protocol descriptor for Swift.Hashable".
2023
extern "C" const ProtocolDescriptor _TMps8Hashable;
2124

25+
namespace {
26+
struct HashableConformanceKey {
27+
/// The lookup key, the metadata of a type that is possibly derived
28+
/// from a type that conforms to `Hashable`.
29+
const Metadata *derivedType;
30+
};
31+
32+
struct HashableConformanceEntry {
33+
/// The lookup key, the metadata of a type that is possibly derived
34+
/// from a type that conforms to `Hashable`.
35+
const Metadata *derivedType;
36+
37+
/// The highest (closest to the root) type in the superclass chain
38+
/// that conforms to `Hashable`.
39+
const Metadata *baseTypeThatConformsToHashable;
40+
41+
HashableConformanceEntry(HashableConformanceKey key,
42+
const Metadata *baseTypeThatConformsToHashable)
43+
: derivedType(key.derivedType),
44+
baseTypeThatConformsToHashable(baseTypeThatConformsToHashable) {}
45+
46+
int compareWithKey(const HashableConformanceKey &key) const {
47+
if (key.derivedType != derivedType) {
48+
return (uintptr_t(key.derivedType) < uintptr_t(derivedType) ? -1 : 1);
49+
} else {
50+
return 0;
51+
}
52+
}
53+
54+
static size_t
55+
getExtraAllocationSize(HashableConformanceKey key,
56+
const Metadata *baseTypeThatConformsToHashable) {
57+
return 0;
58+
}
59+
};
60+
} // end unnamed namesapce
61+
62+
static Lazy<ConcurrentMap<HashableConformanceEntry>> HashableConformances;
63+
64+
/// Find the base type that introduces the `Hashable` conformance.
65+
///
66+
/// - Precondition: `type` conforms to `Hashable` (not checked).
67+
static const Metadata *findHashableBaseType(const Metadata *type) {
68+
if (HashableConformanceEntry *entry =
69+
HashableConformances->find(HashableConformanceKey{type})) {
70+
return entry->baseTypeThatConformsToHashable;
71+
}
72+
const Metadata *baseTypeThatConformsToHashable = type;
73+
while (true) {
74+
const Metadata *superclass =
75+
_swift_class_getSuperclass(baseTypeThatConformsToHashable);
76+
if (!superclass)
77+
break;
78+
if (!swift_conformsToProtocol(superclass, &_TMps8Hashable))
79+
break;
80+
baseTypeThatConformsToHashable = superclass;
81+
}
82+
HashableConformances->getOrInsert(HashableConformanceKey{type},
83+
baseTypeThatConformsToHashable);
84+
return baseTypeThatConformsToHashable;
85+
}
86+
2287
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
2388
extern "C" void _swift_stdlib_makeAnyHashableUsingDefaultRepresentation(
2489
const OpaqueValue *value,
@@ -34,22 +99,14 @@ extern "C" void _swift_stdlib_makeAnyHashableUpcastingToHashableBaseType(
3499
const Metadata *type,
35100
const WitnessTable *hashableWT
36101
) {
37-
// FIXME(id-as-any)(performance): cache the result of the lookup.
38102
switch (type->getKind()) {
39103
case MetadataKind::Class:
40104
case MetadataKind::ObjCClassWrapper:
41105
case MetadataKind::ForeignClass: {
42106
// FIXME(id-as-any): handle ForeignClass.
43-
while (true) {
44-
const Metadata *superclass = _swift_class_getSuperclass(type);
45-
if (!superclass)
46-
break;
47-
if (!swift_conformsToProtocol(superclass, &_TMps8Hashable))
48-
break;
49-
type = superclass;
50-
}
51107
_swift_stdlib_makeAnyHashableUsingDefaultRepresentation(
52-
value, anyHashableResultPointer, type, hashableWT);
108+
value, anyHashableResultPointer, findHashableBaseType(type),
109+
hashableWT);
53110
return;
54111
}
55112

0 commit comments

Comments
 (0)