Skip to content

Commit b3d79f4

Browse files
authored
Performance optimizations for Type (#2733)
Cache type sizes in unused bits from the type ID and rewrite some Type methods to avoid unnecessary calls to `expand` when the type is known to be a basic non-tuple type. This eliminates most of the locking traffic and reduces wall clock time by 52% and CPU time by 73% percent for one real-world program on my machine.
1 parent 0dcb689 commit b3d79f4

File tree

3 files changed

+57
-30
lines changed

3 files changed

+57
-30
lines changed

src/wasm-type.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@
2424
namespace wasm {
2525

2626
class Type {
27-
uint32_t id;
27+
// enough for the limit of 1000 function arguments
28+
static constexpr unsigned SIZE_BITS = 10;
29+
static constexpr unsigned ID_BITS = 32 - SIZE_BITS;
30+
unsigned id : ID_BITS;
31+
unsigned _size : SIZE_BITS;
2832
void init(const std::vector<Type>&);
2933

3034
public:
@@ -50,10 +54,10 @@ class Type {
5054
Type() = default;
5155

5256
// ValueType can be implicitly upgraded to Type
53-
constexpr Type(ValueType id) : id(id){};
57+
constexpr Type(ValueType id) : id(id), _size(id == none ? 0 : 1){};
5458

5559
// But converting raw uint32_t is more dangerous, so make it explicit
56-
constexpr explicit Type(uint32_t id) : id(id){};
60+
explicit Type(uint32_t id);
5761

5862
// Construct from lists of elementary types
5963
Type(std::initializer_list<Type> types);

src/wasm/wasm-type.cpp

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ void Type::init(const std::vector<Type>& types) {
9292
}
9393
#endif
9494

95+
if (types.size() >= (1 << SIZE_BITS)) {
96+
WASM_UNREACHABLE("Type too large");
97+
}
98+
_size = types.size();
99+
95100
auto lookup = [&]() {
96101
auto indexIt = indices.find(types);
97102
if (indexIt != indices.end()) {
@@ -115,6 +120,9 @@ void Type::init(const std::vector<Type>& types) {
115120
if (lookup()) {
116121
return;
117122
}
123+
if (typeLists.size() >= (1 << ID_BITS)) {
124+
WASM_UNREACHABLE("Too many types!");
125+
}
118126
id = typeLists.size();
119127
typeLists.push_back(std::make_unique<std::vector<Type>>(types));
120128
indices[types] = id;
@@ -125,7 +133,15 @@ Type::Type(std::initializer_list<Type> types) { init(types); }
125133

126134
Type::Type(const std::vector<Type>& types) { init(types); }
127135

128-
size_t Type::size() const { return expand().size(); }
136+
Type::Type(uint32_t _id) {
137+
id = _id;
138+
{
139+
std::shared_lock<std::shared_timed_mutex> lock(mutex);
140+
_size = typeLists[id]->size();
141+
}
142+
}
143+
144+
size_t Type::size() const { return _size; }
129145

130146
const std::vector<Type>& Type::expand() const {
131147
std::shared_lock<std::shared_timed_mutex> lock(mutex);
@@ -146,28 +162,34 @@ bool Type::operator<(const Type& other) const {
146162

147163
unsigned Type::getByteSize() const {
148164
// TODO: alignment?
149-
unsigned size = 0;
150-
for (auto t : expand()) {
165+
auto getSingleByteSize = [](Type t) {
151166
switch (t.getSingle()) {
152167
case Type::i32:
153168
case Type::f32:
154-
size += 4;
155-
break;
169+
return 4;
156170
case Type::i64:
157171
case Type::f64:
158-
size += 8;
159-
break;
172+
return 8;
160173
case Type::v128:
161-
size += 16;
162-
break;
174+
return 16;
163175
case Type::funcref:
164176
case Type::anyref:
165177
case Type::nullref:
166178
case Type::exnref:
167179
case Type::none:
168180
case Type::unreachable:
169-
WASM_UNREACHABLE("invalid type");
181+
break;
170182
}
183+
WASM_UNREACHABLE("invalid type");
184+
};
185+
186+
if (isSingle()) {
187+
return getSingleByteSize(*this);
188+
}
189+
190+
unsigned size = 0;
191+
for (auto t : expand()) {
192+
size += getSingleByteSize(t);
171193
}
172194
return size;
173195
}
@@ -197,25 +219,26 @@ Type Type::reinterpret() const {
197219
}
198220

199221
FeatureSet Type::getFeatures() const {
200-
FeatureSet feats = FeatureSet::MVP;
201-
const auto& elements = expand();
202-
if (elements.size() > 1) {
203-
feats = FeatureSet::Multivalue;
204-
}
205-
for (Type t : elements) {
222+
auto getSingleFeatures = [](Type t) {
206223
switch (t.getSingle()) {
207224
case Type::v128:
208-
feats |= FeatureSet::SIMD;
209-
break;
225+
return FeatureSet::SIMD;
210226
case Type::anyref:
211-
feats |= FeatureSet::ReferenceTypes;
212-
break;
227+
return FeatureSet::ReferenceTypes;
213228
case Type::exnref:
214-
feats |= FeatureSet::ExceptionHandling;
215-
break;
229+
return FeatureSet::ExceptionHandling;
216230
default:
217-
break;
231+
return FeatureSet::MVP;
218232
}
233+
};
234+
235+
if (isSingle()) {
236+
return getSingleFeatures(*this);
237+
}
238+
239+
FeatureSet feats = FeatureSet::Multivalue;
240+
for (Type t : expand()) {
241+
feats |= getSingleFeatures(t);
219242
}
220243
return feats;
221244
}

src/wasm/wasm.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -992,11 +992,11 @@ Index Function::getLocalIndex(Name name) {
992992
Index Function::getVarIndexBase() { return sig.params.size(); }
993993

994994
Type Function::getLocalType(Index index) {
995-
const std::vector<Type>& params = sig.params.expand();
996-
if (index < params.size()) {
997-
return params[index];
995+
auto numParams = sig.params.size();
996+
if (index < numParams) {
997+
return sig.params.expand()[index];
998998
} else if (isVar(index)) {
999-
return vars[index - params.size()];
999+
return vars[index - numParams];
10001000
} else {
10011001
WASM_UNREACHABLE("invalid local index");
10021002
}

0 commit comments

Comments
 (0)