From 3bfe5e4568cf38bf1f46014a90b6f0b7034b5f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Thu, 29 Jan 2026 18:07:30 +0200 Subject: [PATCH] Fix a +200% Windows performance regression caused by PR #4897. On Windows with Visual Studio compiler, the constructor of a std::stringstream object is extremely slow, as it incurs a call to a std::locale ctor(), which in turn causes an access to some kind of process global locale mutex lock. (maybe to get the current system locale?). Work around this issue on the very hot function PassRunner::runPassOnFunction() that increased wasm-opt link times from ~20 seconds to ~60 seconds on Windows when wasm-opt -O2 optimizing a large 33MB .wasm file. std::string ctor does not share this performance problem on Windows. --- src/passes/pass.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp index 23667aeecbf..564c743bb79 100644 --- a/src/passes/pass.cpp +++ b/src/passes/pass.cpp @@ -867,9 +867,11 @@ void PassRunner::run() { for (auto& pass : passes) { // ignoring the time, save a printout of the module before, in case this // pass breaks it, so we can print the before and after - std::stringstream moduleBefore; + std::string moduleBefore; if (passDebug == 2 && !isNested) { - moduleBefore << *wasm << '\n'; + std::stringstream ss; + ss << *wasm << '\n'; + moduleBefore = ss.str(); } // prepare to run std::cerr << "[PassRunner] running pass: " << pass->name << "... "; @@ -896,7 +898,7 @@ void PassRunner::run() { if (passDebug >= 2) { Fatal() << "Last pass (" << pass->name << ") broke validation. Here is the module before: \n" - << moduleBefore.str() << "\n"; + << moduleBefore << "\n"; } else { Fatal() << "Last pass (" << pass->name << ") broke validation. Run with BINARYEN_PASS_DEBUG=2 " @@ -1026,9 +1028,12 @@ void PassRunner::runPassOnFunction(Pass* pass, Function* func) { // useful - leave it to the entire module to fail validation in that case. bool extraFunctionValidation = passDebug == 2 && options.validate && !pass->name.empty(); - std::stringstream bodyBefore; + + std::string bodyBefore; if (extraFunctionValidation) { - bodyBefore << *func->body << '\n'; + std::stringstream ss; + ss << *func->body << '\n'; + bodyBefore = ss.str(); } // Function-parallel passes get a new instance per function @@ -1042,7 +1047,7 @@ void PassRunner::runPassOnFunction(Pass* pass, Function* func) { Fatal() << "Last nested function-parallel pass (" << pass->name << ") broke validation of function " << func->name << ". Here is the function body before:\n" - << bodyBefore.str() << "\n\nAnd here it is now:\n" + << bodyBefore << "\n\nAnd here it is now:\n" << *func->body << '\n'; } }