
Description
Thread locals are often slower than they need to be. For example, if we have:
thread_local! {
static FOO: i32 = 777;
}
Every access of the form FOO.with(|foo| ...)
will first check whether FOO
is initialized, and then initialize it if this is the first access. On some (most?) platforms we can statically initialize thread locals with a constant expression (in this case the constant expression is 777
).
A check on every access can be fairly costly, and we should try to avoid checks whenever possible. It is possible to avoid them by using #[thread_local]
instead of thread_local!
, but that is an unstable feature without a clear stabilization plan...
In #17954 @alexcrichton said:
I do not think we should strive to stabilize
#[thread_local]
. It's incredibly not portable which makes it not too useful for most software. We should strive to improvethread_local!
. The feature to implement is for the compiler to understand whether the initialization expression is a constant expression or not. If it's a constant expression then we can bypass theNone
storage and "am I initialized checks", making it equivalent to raw#[thread_local]
@eddyb answered:
That's easy in the compiler but
thread_local!
is not implemented in the compiler.
@arielb1 suggests:
... But we could add an
eager_thread_local!
macro for that case.
@alexcrichton adds:
As for how to implement a "const expr detection" in a macro I'm not entirely sure. We could either move the implementation into the compiler (which I'd prefer to avoid) or take @arielb1's suggestion of a new macro or a variant of the current macro's syntax.
For example we could "perverse" the meaning via:
thread_local!(const A: i32 = 3);
wherestatic
in the macro means "lazily initialized, but any expression valid" andconst
means "must be a constant expression". I don't think this is a good idea, but an example of what we might do.
My question after all that would be:
Can we perhaps make the thread_local!
macro expand to a special lang item that provides two implementations - one for the lazy initialization case and one for the static initialization zero-checks case? Then the compiler would choose one of them, depending on whether the initialization expression is a constant expression.