Skip to content

Commit e072849

Browse files
committed
[wasm-split] Remove module elements in bulk (NFC)
Previously we removed module elements one by one within a loop. But because `Module` stores a module element in both a map and a vector, removing a single module element using `removeModuleElement` is O(N), because it needs to shift all vector elements after it: https://github.com/WebAssembly/binaryen/blob/302396a676433152a32375a81d71e74687c97a1b/src/wasm/wasm.cpp#L1970-L1979 This removes module elements in bulk using `removeModuleElements`, which does the shifting only once. https://github.com/WebAssembly/binaryen/blob/302396a676433152a32375a81d71e74687c97a1b/src/wasm/wasm.cpp#L2004-L2018 Combining with #8966, acx_gallery's running time improved by 50.3% (30s -> 15s), and essentials by 60.8% (230s -> 90s). (for Jul 2026 version) I guess the main reason for the running time increase in #8441 was this O(N) `removeModuleElement` called within a loop after all.
1 parent 8939ccd commit e072849

1 file changed

Lines changed: 31 additions & 36 deletions

File tree

src/ir/module-splitting.cpp

Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,15 +1103,15 @@ void ModuleSplitter::shareImportableItems() {
11031103
// the primary and secondary modules), export the item from the primary and
11041104
// import it from the using secondary modules.
11051105

1106-
std::vector<Name> memoriesToRemove;
1106+
std::unordered_set<Name> memoriesToRemove;
11071107
for (auto& memory : primary.memories) {
11081108
if (tracker.useEmpty(memory->name, tracker.memories)) {
1109-
memoriesToRemove.push_back(memory->name);
1109+
memoriesToRemove.insert(memory->name);
11101110
} else if (tracker.usedBySingleSecondary(memory->name, tracker.memories)) {
11111111
auto* secondary =
11121112
tracker.getUsingSecondaries(memory->name, tracker.memories)[0];
11131113
ModuleUtils::copyMemory(memory.get(), *secondary);
1114-
memoriesToRemove.push_back(memory->name);
1114+
memoriesToRemove.insert(memory->name);
11151115
} else {
11161116
for (auto* secondary :
11171117
tracker.getUsingSecondaries(memory->name, tracker.memories)) {
@@ -1122,20 +1122,19 @@ void ModuleSplitter::shareImportableItems() {
11221122
}
11231123
}
11241124
}
1125-
for (auto& name : memoriesToRemove) {
1126-
primary.removeMemory(name);
1127-
}
1125+
primary.removeMemories(
1126+
[&](Memory* memory) { return memoriesToRemove.count(memory->name); });
11281127

1129-
std::vector<Name> tablesToRemove;
1128+
std::unordered_set<Name> tablesToRemove;
11301129
for (auto& table : primary.tables) {
11311130
if (tracker.useEmpty(table->name, tracker.tables)) {
1132-
tablesToRemove.push_back(table->name);
1131+
tablesToRemove.insert(table->name);
11331132
} else if (tracker.usedBySingleSecondary(table->name, tracker.tables)) {
11341133
auto* secondary =
11351134
tracker.getUsingSecondaries(table->name, tracker.tables)[0];
11361135
assert(!secondary->getTableOrNull(table->name));
11371136
ModuleUtils::copyTable(table.get(), *secondary);
1138-
tablesToRemove.push_back(table->name);
1137+
tablesToRemove.insert(table->name);
11391138
} else {
11401139
for (auto* secondary :
11411140
tracker.getUsingSecondaries(table->name, tracker.tables)) {
@@ -1144,24 +1143,23 @@ void ModuleSplitter::shareImportableItems() {
11441143
}
11451144
}
11461145
}
1147-
for (auto& name : tablesToRemove) {
1148-
primary.removeTable(name);
1149-
}
1146+
primary.removeTables(
1147+
[&](Table* table) { return tablesToRemove.count(table->name); });
11501148

1151-
std::vector<Name> globalsToRemove;
1149+
std::unordered_set<Name> globalsToRemove;
11521150
for (auto& global : primary.globals) {
11531151
if (global->mutable_) {
11541152
assert(primary.features.hasMutableGlobals() &&
11551153
"TODO: add wrapper functions for disallowed mutable globals");
11561154
}
11571155

11581156
if (tracker.useEmpty(global->name, tracker.globals)) {
1159-
globalsToRemove.push_back(global->name);
1157+
globalsToRemove.insert(global->name);
11601158
} else if (tracker.usedBySingleSecondary(global->name, tracker.globals)) {
11611159
auto* secondary =
11621160
tracker.getUsingSecondaries(global->name, tracker.globals)[0];
11631161
ModuleUtils::copyGlobal(global.get(), *secondary);
1164-
globalsToRemove.push_back(global->name);
1162+
globalsToRemove.insert(global->name);
11651163
} else {
11661164
for (auto* secondary :
11671165
tracker.getUsingSecondaries(global->name, tracker.globals)) {
@@ -1172,18 +1170,17 @@ void ModuleSplitter::shareImportableItems() {
11721170
}
11731171
}
11741172
}
1175-
for (auto& name : globalsToRemove) {
1176-
primary.removeGlobal(name);
1177-
}
1173+
primary.removeGlobals(
1174+
[&](Global* global) { return globalsToRemove.count(global->name); });
11781175

1179-
std::vector<Name> tagsToRemove;
1176+
std::unordered_set<Name> tagsToRemove;
11801177
for (auto& tag : primary.tags) {
11811178
if (tracker.useEmpty(tag->name, tracker.tags)) {
1182-
tagsToRemove.push_back(tag->name);
1179+
tagsToRemove.insert(tag->name);
11831180
} else if (tracker.usedBySingleSecondary(tag->name, tracker.tags)) {
11841181
auto* secondary = tracker.getUsingSecondaries(tag->name, tracker.tags)[0];
11851182
ModuleUtils::copyTag(tag.get(), *secondary);
1186-
tagsToRemove.push_back(tag->name);
1183+
tagsToRemove.insert(tag->name);
11871184
} else {
11881185
for (auto* secondary :
11891186
tracker.getUsingSecondaries(tag->name, tracker.tags)) {
@@ -1192,45 +1189,43 @@ void ModuleSplitter::shareImportableItems() {
11921189
}
11931190
}
11941191
}
1195-
for (auto& name : tagsToRemove) {
1196-
primary.removeTag(name);
1197-
}
1192+
primary.removeTags([&](Tag* tag) { return tagsToRemove.count(tag->name); });
11981193

11991194
// Move segments that are exclusively used in a secondary module. If not, do
12001195
// nothing. (Segments cannot be imported / exported. They will be handled in
12011196
// indirectReferencesToSecondaryFunctions.)
12021197

1203-
std::vector<Name> dataSegmentsToRemove;
1198+
std::unordered_set<Name> dataSegmentsToRemove;
12041199
for (auto& dataSegment : primary.dataSegments) {
12051200
if (tracker.useEmpty(dataSegment->name, tracker.dataSegments)) {
1206-
dataSegmentsToRemove.push_back(dataSegment->name);
1201+
dataSegmentsToRemove.insert(dataSegment->name);
12071202
} else if (tracker.usedBySingleSecondary(dataSegment->name,
12081203
tracker.dataSegments)) {
12091204
auto* secondary =
12101205
tracker.getUsingSecondaries(dataSegment->name, tracker.dataSegments)[0];
12111206
ModuleUtils::copyDataSegment(dataSegment.get(), *secondary);
1212-
dataSegmentsToRemove.push_back(dataSegment->name);
1207+
dataSegmentsToRemove.insert(dataSegment->name);
12131208
}
12141209
}
1215-
for (auto& name : dataSegmentsToRemove) {
1216-
primary.removeDataSegment(name);
1217-
}
1210+
primary.removeDataSegments([&](DataSegment* dataSegment) {
1211+
return dataSegmentsToRemove.count(dataSegment->name);
1212+
});
12181213

1219-
std::vector<Name> elementSegmentsToRemove;
1214+
std::unordered_set<Name> elementSegmentsToRemove;
12201215
for (auto& elementSegment : primary.elementSegments) {
12211216
if (tracker.useEmpty(elementSegment->name, tracker.elementSegments)) {
1222-
elementSegmentsToRemove.push_back(elementSegment->name);
1217+
elementSegmentsToRemove.insert(elementSegment->name);
12231218
} else if (tracker.usedBySingleSecondary(elementSegment->name,
12241219
tracker.elementSegments)) {
12251220
auto* secondary = tracker.getUsingSecondaries(elementSegment->name,
12261221
tracker.elementSegments)[0];
12271222
ModuleUtils::copyElementSegment(elementSegment.get(), *secondary);
1228-
elementSegmentsToRemove.push_back(elementSegment->name);
1223+
elementSegmentsToRemove.insert(elementSegment->name);
12291224
}
12301225
}
1231-
for (auto& name : elementSegmentsToRemove) {
1232-
primary.removeElementSegment(name);
1233-
}
1226+
primary.removeElementSegments([&](ElementSegment* elementSegment) {
1227+
return elementSegmentsToRemove.count(elementSegment->name);
1228+
});
12341229
}
12351230

12361231
void ModuleSplitter::indirectReferencesToSecondaryFunctions() {

0 commit comments

Comments
 (0)