Skip to content

Avoid initialization checks when accessing thread locals #44025

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

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.

# for free to join this conversation on GitHub. Already have an account? # 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