Skip to content
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

Avoid initialization checks when accessing thread locals #44025

Closed
ghost opened this issue Aug 22, 2017 · 6 comments
Closed

Avoid initialization checks when accessing thread locals #44025

ghost opened this issue Aug 22, 2017 · 6 comments
Labels
A-thread-locals Area: Thread local storage (TLS) C-enhancement Category: An issue proposing an enhancement or a PR with one. I-slow Issue: Problems and improvements with respect to performance of generated code. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Comments

@ghost
Copy link

ghost commented Aug 22, 2017

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 improve thread_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 the None 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); where static in the macro means "lazily initialized, but any expression valid" and const 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.

@Mark-Simulacrum Mark-Simulacrum added C-enhancement Category: An issue proposing an enhancement or a PR with one. I-slow Issue: Problems and improvements with respect to performance of generated code. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Aug 22, 2017
@eddyb
Copy link
Member

eddyb commented Aug 22, 2017

We can just do what C(++) compilers do and support the lazy initialization in the compiler, by generating a wrapper function that would get called each time - not sure about reentrance implications but it should be doable.

@matklad
Copy link
Member

matklad commented Jul 15, 2019

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?

An alternative: have a dedicated thread_local_static! macro, with the API exactly like today's thread_local, but restricted to const expressions and without initialization overhead, and implement thread_local! on top of thread_local_static.

I think having explicit separate constructs for compile-time initialization vs lazy initialization fits better with rust overall direction than "sufficiently smart compiler/stdlib optimizes lazy initialization away". If you care about thread_local overhead, you probably want to assert that it is const-initialized.

We probably should have named the current macro lazy_thread_local even....

@eddyb
Copy link
Member

eddyb commented Jul 15, 2019

@matklad At that point, just stabilize #[thread_local] static FOO: T = expr; (#29594).

@matklad
Copy link
Member

matklad commented Jul 15, 2019

I'd be happy to see #2954 stabilized! That would allow once_cell to subsume not only lazy_static, but std::thread_local! as well:

#[thread_local]
pub static FOO: once_cell::unsync::Lazy<RefCell<u32>> = Lazy::new(|| RefCell::new(1));

@jonas-schievink jonas-schievink added A-thread-locals Area: Thread local storage (TLS) T-libs Relevant to the library team, which will review and decide on the PR/issue. and removed T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Oct 4, 2020
@alexcrichton
Copy link
Member

This was implemented in #83416 as:

thread_local! {
    static FOO: u32 = const { 3 };
}

@Mark-Simulacrum
Copy link
Member

The above syntax stabilized in 1.59, I'm going to go ahead and close this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-thread-locals Area: Thread local storage (TLS) C-enhancement Category: An issue proposing an enhancement or a PR with one. I-slow Issue: Problems and improvements with respect to performance of generated code. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

5 participants