-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow stack size to be configured for native AOT #110455
base: main
Are you sure you want to change the base?
Allow stack size to be configured for native AOT #110455
Conversation
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs
Outdated
Show resolved
Hide resolved
We have (undocumented) |
const string stackSizeVar = "DefaultStackSize"; | ||
const string stackSizeEnvVar = "DOTNET_DefaultStackSize"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shortcut (making the AppContext switch name same as the environment variable name) makes things inconsistent with how we normally name AppContext switches. See e.g. documented GC switches where DOTNET_gcServer
environment variable is a synonym for System.GC.Server
host config option (i.e. these are not prefixes and AppContext switches adhere to a naming convention).
It is a bit more work to do this correctly. Sample how it's done on the managed side (also notice the naming convention):
runtime/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs
Lines 66 to 70 in 8fca0a1
int timeoutMs = | |
AppContextConfigHelper.GetInt32Config( | |
"System.Threading.ThreadPool.ThreadTimeoutMs", | |
"DOTNET_ThreadPool_ThreadTimeoutMs", | |
DefaultThreadPoolThreadTimeoutMs); |
On the native side we do this (it's what the GC uses to read both env variable settings and host config options):
runtime/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp
Lines 770 to 784 in 8fca0a1
uint64_t uiValue; | |
if (g_pRhConfig->ReadConfigValue(privateKey, &uiValue)) | |
{ | |
*value = uiValue; | |
return true; | |
} | |
if (publicKey) | |
{ | |
if (g_pRhConfig->ReadKnobUInt64Value(publicKey, &uiValue)) | |
{ | |
*value = uiValue; | |
return true; | |
} | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, it would be nice to keep the configuration capabilities between NAOT and regular CoreCLR the same - make the configuration switch work the same way for both NAOT and CoreCLR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this feedback is addressed. Are there any other changes you would like to see?
This change would help one of the teams that I support.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this feedback is addressed. Are there any other changes you would like to see?
I don't see this comment addressed:
- The switch name should be something starting with
System.Threading.Thread
. MaybeSystem.Threading.Thread.DefaultStackSize
? This is a naming convention respected across the entire framework. - If I'm reading it right,
PalStartBackgroundWork
will look at the environment variable, but will not look at RuntimeHostConfigurationOption (the environment variable settings and AppContext/RuntimeHostConfiguration settings are from disjoint namespaces). Is that expected?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the confusion, I pushed 2975db0 awhile ago, but Github wasn't showing the commit in this diff. I followed jkotas's advice, rebasing and force pushed fixed the state.
Thanks. This works on Windows, it is not working for me on Linux. On windows I checked against kernel32 GetCurrentThreadStackLimits. On linux I checked against strace. |
It works for musl libc (musl libc is used e.g. by Alpine Linux). glibc that is used by the most popular Linux distros like Ubuntu does not respect value set by |
Github status is stuck in "Checking for ability to merge automatically…". Could you please rebase against main and force push? Hopefully, it is going to reset the status. |
|
||
private static int StackSizeFromConfig | ||
{ | ||
get => _stackSizeFromConfig ??= GetDefaultStackSize(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This pattern with Nullable<int>
is not thread safe. Nullable is a struct with two fields. This pattern has a race condition caused by tearing.
I do not think you need to cache the value on the managed side. The value is cached on the unmanaged side. You can call every time.
// the AppContext class. | ||
// To have similar behavior as RhConfig, also read from environment variables, which have priority. | ||
|
||
string? valueFromConfig = Environment.GetEnvironmentVariable(stackSizeEnvVar) ?? AppContext.GetData(stackSizeVar)?.ToString(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can use AppContextHelper.GetInt32ComPlusOrDotNetConfig so that we only have one copy of the policy around parsing these.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, this can call into the native runtime to reuse the value parsed there.
94b687c
to
b6fd3f7
Compare
If a native AOT compiled application can lower its allocated memory, more scenarios that are memory sensitive can use it.
The default stack size can be excessive for simple applications and can significantly contribute to the RSS.
For linux, there is the ulimit command, and this is respected, but it can be too broad.
With this trivial code:
On linux with the default stack size of 8mb, RSS measured at 5916.
With setting the stack size to 1mb, the RSS measured at 3428.
Roughly a 40% decrease.