[wasm] Don't use SyncTextWriter in single-threaded wasm builds#101221
[wasm] Don't use SyncTextWriter in single-threaded wasm builds#101221kg merged 4 commits intodotnet:mainfrom
Conversation
|
Tagging subscribers to 'arch-wasm': @lewing |
|
The fast path should always be taken in ST builds and everything should inline. Why is this necessary? Also, monitor.isentered will always return false this way |
A sizable percentage of startup time is interp codegen, so I've been seeing how many subtrees of useless methods I can prune out easily. The monitor.isentered thing is a good point, I guess that rules this optimization out. |
ah ok. so actually rewriting this part in managed was a bad idea for startup perf. we only measured benchmark (steady state) perf once the interpreter was tiering up the callers. (so Monitor.Enter/Exit loops without contention on ST interp were the same as or a bit faster than the old JIT icall monitor implementation) |
|
Do you think it makes sense to use a simpler lockword representation for ST? (for example we don't have to use Interlocked ops to increment/decrement the count - we can just use normal reads/writes). possibly hand-inline more code from ObjectHeader into Monitor.Enter/Exit? |
I think that makes sense, we could just provide simple implementations of the primitives for that configuration which would prune out most of the dependencies without breaking it. I'll make a note to try that. |
It only gets triggered if you write to the console, yes, but don't we write to the console at least once during startup? I thought I recalled seeing a console message |
This wrapper overhead applies to any synchronized method that is in the startup path, yes |
…hronization code during startup when console is touched
| // Browser bypasses SyncTextWriter for faster startup | ||
| if (!OperatingSystem.IsBrowser()) |
There was a problem hiding this comment.
this should use PlatformDetection.IsThreadingSupported too instead of checking for browser so it applies to single-threaded wasi too
| <Is64Bit Condition="'$(Platform)' == 'arm64' or '$(Platform)' == 'x64' or '$(Platform)' == 's390x' or '$(Platform)' == 'loongarch64' or '$(Platform)' == 'ppc64le' or '$(Platform)' == 'riscv64'">true</Is64Bit> | ||
| <UseMinimalGlobalizationData Condition="'$(TargetsBrowser)' == 'true' or '$(TargetsWasi)' == 'true'">true</UseMinimalGlobalizationData> | ||
| <FeatureWasmManagedThreads Condition="'$(WasmEnableThreads)' == 'true'">true</FeatureWasmManagedThreads> | ||
| <DefineConstants Condition="'$(FeatureWasmManagedThreads)' == 'true'">$(DefineConstants);FEATURE_WASM_MANAGED_THREADS</DefineConstants> |
There was a problem hiding this comment.
this duplicates the conditions from src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj, I don't think we need them here
| { | ||
| ArgumentNullException.ThrowIfNull(writer); | ||
|
|
||
| #if !TARGET_BROWSER || FEATURE_WASM_MANAGED_THREADS |
Follow-up to dotnet#101221 so we match behavior between single-threaded Browser and WASI.
Follow-up to #101221 so we match behavior between single-threaded Browser and WASI.
(New description, PR was revised)
We normally wrap custom console streams with SyncTextWriter, which forces the mono interpreter/jit/aot compiler to use synchronized wrappers for all calls through the class. Wrappers have optimization force-enabled, which means all the methods they call get inlined into the wrapper every time, increasing the size of the wrapper's code and the amount of time we spend in codegen for it.
The default wasm configuration right now is single-threaded, so we can skip using SyncTextWriter and as a result skip the synchronized wrapper. While this doesn't fully prevent monitors from becoming involved in startup, it removes a bunch of inlined monitor API calls, which should be an improvement to startup time for any wasm application that touches the console.