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

Allow threadsafe access to buffer of type inference profiling trees #47615

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

vilterp
Copy link
Contributor

@vilterp vilterp commented Nov 17, 2022

This PR allows accessing the results of type inference profiling (via @snoopi_deep) from a separate background thread.

Note that type inference on multiple threads while snooping is enabled is still not threadsafe as of this PR, since starting in 1.9 type inference can happen in parallel on multiple threads. We will fix this in a follow-up PR.

In the meantime, this is a step in the right direction, and fixes thread safety for the specific case of one profiling thread and one inference thread, and this PR could be backported to 1.8.*.

TODO:

  • print deprecation warning (println and @warn are undefined in the compiler)
    • decided not to do this; it's infeasible and it's a compiler internal.

base/compiler/typeinfer.jl Outdated Show resolved Hide resolved
base/compiler/typeinfer.jl Outdated Show resolved Hide resolved
@vilterp
Copy link
Contributor Author

vilterp commented Nov 18, 2022

@vilterp vilterp marked this pull request as ready for review November 18, 2022 20:58
@vilterp
Copy link
Contributor Author

vilterp commented Nov 18, 2022

Mysterious error in the tests:

Package EnglishText errored during testing
  | Stacktrace:
  | [1] pkgerror(msg::String)
  | @ Pkg.Types /cache/build/default-amdci5-0/julialang/julia-master/julia-959ef6c00a/share/julia/stdlib/v1.10/Pkg/src/Types.jl:68
  | [2] test(ctx::Pkg.Types.Context, pkgs::Vector{Pkg.Types.PackageSpec}; coverage::Bool, julia_args::Cmd, test_args::Cmd, test_fn::Nothing, force_latest_compatible_version::Bool, allow_earlier_backwards_compatible_versions::Bool, allow_reresolve::Bool)
  | @ Pkg.Operations /cache/build/default-amdci5-0/julialang/julia-master/julia-959ef6c00a/share/julia/stdlib/v1.10/Pkg/src/Operations.jl:1878
  | [3] test

Can't find the actual error message. Anyone know what this is?

@NHDaly
Copy link
Member

NHDaly commented Nov 18, 2022

Looks like that failing test is coming from here:
https://github.com/JuliaLang/Pkg.jl/blob/d144b12935c56c0a0fc2f730e2960ae02096f131/test/new.jl#L301-L306

I guess the julia tests must pull in the Pkg.jl tests. And those tests are cloning and testing the EnglishText.jl package 😅

It seems probably unrelated, although it's a little weird that those same tests keep failing on multiple commits.. 🤔

@NHDaly NHDaly added backport 1.8 Change should be backported to release-1.8 backport 1.9 Change should be backported to release-1.9 labels Nov 18, 2022
@NHDaly
Copy link
Member

NHDaly commented Nov 18, 2022

I tried pulling down EnglishText locally to see why it's failing, and it turns out that it's because EnglishText transitively depends on SnoopCompile, using @snoopi_deep through SnoopPrecompile to precompile a package!
So we need to merge our fixes to SnoopCompile to update it for this PR first, before these tests will pass, I think!

Pretty wild that SnoopCompile is part of julia build tests like this. But also very cool!

NHDaly pushed a commit to timholy/SnoopCompile.jl that referenced this pull request Nov 22, 2022
Starting in JuliaLang/julia#47615, we are
changing the API for snoopi_deep, to allow for thread safe snooping
in one thread while compiling in another.

This commit changes SnoopPrecompile to work with the new API if it's
available.
NHDaly pushed a commit to vilterp/SnoopCompile.jl that referenced this pull request Nov 22, 2022
Starting in JuliaLang/julia#47615, we are
changing the API for snoopi_deep, to allow for thread safe snooping
in one thread while compiling in another.

This commit changes SnoopPrecompile to work with the new API if it's
available.

Co-Authored-By: Pete Vilter <pete.vilter@gmail.com>
base/compiler/typeinfer.jl Show resolved Hide resolved
base/compiler/typeinfer.jl Show resolved Hide resolved
@vchuravy vchuravy changed the title [wip] allow threadsafe access to buffer of type inference profiling trees Allow threadsafe access to buffer of type inference profiling trees Dec 4, 2022
@NHDaly
Copy link
Member

NHDaly commented Dec 5, 2022

print deprecation warning (println and @warn are undefined in the compiler)

@vchuravy or @pchintalapudi: do you have any advice on how best to mark the old functions as deprecated? I was thinking about possibly moving the implementations to base/deprecated.jl, and @evaling them back into Core.Compiler.Timings. This way, we can interpolate in the depwarn call.

Does that seem like the right approach to you?

@vchuravy
Copy link
Sponsor Member

vchuravy commented Dec 6, 2022

Eh, at that point Core.Compiler.Timings should be closed. So I think that won't work. Arguably it's a compiler internals so subject to change and doesn't need deprecation.

@NHDaly NHDaly removed the backport 1.8 Change should be backported to release-1.8 label Dec 8, 2022
@NHDaly
Copy link
Member

NHDaly commented Dec 8, 2022

K, thanks, that's helpful. 👍

@NHDaly
Copy link
Member

NHDaly commented Dec 8, 2022

Maybe we can still log a deprecation warning via Core.println(), just for good measure. we have a PR out to update SnoopCompile to the new API as well.

@NHDaly
Copy link
Member

NHDaly commented Dec 8, 2022

or maybe that's worse than no log at all, since you can't turn it off via the no-deprecation-warnings commandline flag. 🤷 no strong opinions here

@NHDaly
Copy link
Member

NHDaly commented Dec 14, 2022

👍 let us know when this is good for final review

@vilterp
Copy link
Contributor Author

vilterp commented Jan 4, 2023

Yep, this is ready for review, except that I'm seeing a lot of errors like

Expression: resp isa Response
  | Evaluated: Downloads.RequestError("https://untrusted-root.badssl.julialang.org", 28, "Failed to connect to untrusted-root.badssl.julialang.org port 443 after 15016 ms: Operation timed out", Downloads.Response(nothing, "https://untrusted-root.badssl.julialang.org/", 0, "", Pair{String, String}[])) isa Downloads.Response
  | Error in testset Downloads:
  | Error During Test at /cache/build/default-amdci5-3/julialang/julia-master/julia-b34a42122c/share/julia/stdlib/v1.10/Downloads/test/runtests.jl:515
  | Test threw exception
  | Expression: resp.status == 200
  | type RequestError has no field status

Rebasing on master just in case that fixes it…

Also make check-whitespace seems to fail in CI but not locally, and I didn't see any trailing whitespace on the line it said 🤔 (typeinfer.jl:91)

@NHDaly
Copy link
Member

NHDaly commented Jan 4, 2023

Thanks! Will do a final review pass

@NHDaly NHDaly self-requested a review January 4, 2023 17:57
base/compiler/typeinfer.jl Outdated Show resolved Hide resolved
base/compiler/typeinfer.jl Outdated Show resolved Hide resolved
Copy link
Member

@NHDaly NHDaly left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @vilterp, this LGTM!

Can you address the last open comments? Then this looks good to merge

vilterp and others added 2 commits January 18, 2023 16:41
Co-authored-by: Nathan Daly <NHDaly@gmail.com>
Starting in 1.9, julia does not hold the codegen lock for all of
typeinference! This means that we cannot use a single shared
scratch-space for profiling type inference.

This PR changes the scratch space to use Task Local Storage, so that we
build a separate inference profile tree per Task doing inference, and
then report them to the global results buffer at the end.
if t.storage === nothing
t.storage = IdDict()
end
return (t.storage)::IdDict{Any,Any}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vchuravy / @pchintalapudi / @kpamnany: I finally got around to testing this, and indeed it doesn't work. :(

It turns out that Base.IdDict and Core.Compiler.IdDict are not the same thing. 😢

Does anyone know why that is? Why doesn't Base just reexport the IdDict from Core.Compiler?
I.e. here:

include("iddict.jl")

Why doesn't it just reexport the IdDict from Core.Compiler?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BUT anyway, the error we encounter is that we're not allowed to set the Task's .storage field to a Core.Compiler.IdDict; it's expected to be a Base.IdDict.

So is there just no way to use task-local storage from Core.Compiler?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly a more robust solution here, which might also last longer if we parallelize inference, would be to store this on the AbstractInterpreter or InferenceState objects? Does anyone know if that's plausible? We essentially want some object that lives for the lifetime of the inference invocation, and is local to this invocation, where we can store the stack of timers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or we'd have to thread it through every function call, but that seems like possibly a nonstarter.

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So is there just no way to use task-local storage from Core.Compiler?

Nobody needed this before it seems :)

Does anyone know why that is? Why doesn't Base just reexport the IdDict from Core.Compiler?

Likely to isolate the compiler code from Base.

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can do:

if isdefined(Core, :Main)
  Core.Main.Base.get_task_tls(current_task())
end

to get around the bootstrapping issue.

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is because the compiler is forbidden from accessing TLS. Since we hijack the current task to run codegen, it could corrupt the running task if it does anything to interact with it.

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(instead, you could perhaps replace the TLS on entry with one from Core.Compiler, and restore it on return)

@KristofferC KristofferC mentioned this pull request Feb 20, 2023
50 tasks
@KristofferC KristofferC mentioned this pull request Mar 7, 2023
52 tasks
@KristofferC KristofferC mentioned this pull request Apr 9, 2023
26 tasks
@NHDaly
Copy link
Member

NHDaly commented Apr 26, 2023

This still has backport-1.9, but we never managed to finish it up. :(

I think we shouldn't hold up 1.9 on this. Should I remove the backport label? CC: @vchuravy

It means that users will need to be careful not to run @snoopi_deep on a process with multiple threads.. :/ Maybe we should at least backport a warning to the docs?

@KristofferC KristofferC mentioned this pull request May 8, 2023
51 tasks
@KristofferC KristofferC mentioned this pull request Jun 6, 2023
36 tasks
@KristofferC KristofferC mentioned this pull request Jul 11, 2023
35 tasks
NHDaly added a commit to JuliaPerf/ProfileEndpoints.jl that referenced this pull request Aug 14, 2023
… upstream:

JuliaLang/julia#47615 was never merged, so this
feature cannot be used yet anyway. :(

And the compat bounds are currently too strict, and are causing issues
in RelationalAI's build
NHDaly added a commit to JuliaPerf/ProfileEndpoints.jl that referenced this pull request Aug 14, 2023
… upstream:

JuliaLang/julia#47615 was never merged, so this
feature cannot be used yet anyway. :(

And the compat bounds are currently too strict, and are causing issues
in RelationalAI's build
NHDaly added a commit to JuliaPerf/ProfileEndpoints.jl that referenced this pull request Aug 14, 2023
… upstream:

JuliaLang/julia#47615 was never merged, so this
feature cannot be used yet anyway. :(

And the compat bounds are currently too strict, and are causing issues
in RelationalAI's build
NHDaly added a commit to JuliaPerf/ProfileEndpoints.jl that referenced this pull request Aug 14, 2023
… upstream. (#25)

* Remove type inference profiling, since thread safety was never merged upstream:

JuliaLang/julia#47615 was never merged, so this
feature cannot be used yet anyway. :(

And the compat bounds are currently too strict, and are causing issues
in RelationalAI's build

* Bump to version v1.1.0
KristofferC added a commit that referenced this pull request Aug 18, 2023
Backported PRs:
- [x] #47782 <!-- Generalize Bool parse method to AbstractString -->
- [x] #48634 <!-- Remove unused "deps" mechanism in internal sorting
keywords [NFC] -->
- [x] #49931 <!-- Lock finalizers' lists at exit -->
- [x] #50064 <!-- Fix numbered prompt with input only with comment -->
- [x] #50474 <!-- docs: Fix a `!!! note` which was miscapitalized -->
- [x] #50516 <!-- Fix visibility of assert on GCC12/13 -->
- [x] #50635 <!-- `versioninfo()`: include build info and unofficial
warning -->
- [x] #49915 <!-- Revert "Remove number / vector (#44358)" -->
- [x] #50781 <!-- fix `bit_map!` with aliasing -->
- [x] #50845 <!-- fix #50438, use default pool for at-threads -->
- [x] #49031 <!-- Update inference.md -->
- [x] #50289 <!-- Initialize prev_nold and nold in gc_reset_page -->
- [x] #50559 <!-- Expand kwcall lowering positional default check to
vararg -->
- [x] #49582 <!-- Update HISTORY.md for `DelimitedFiles` -->
- [x] #50341 <!-- invokelatest docs should say not exported before 1.9
-->
- [x] #50525 <!-- only check that values are finite in `generic_lufact`
when `check=true` -->
- [x] #50444 <!-- Optimize getfield lowering to avoid boxing in some
cases -->
- [x] #50523 <!-- Avoid generic call in most cases for getproperty -->
- [x] #50860 <!-- Add `Base.get_extension` to docs/API -->
- [x] #50164 <!-- codegen: handle dead code with unsafe_store of FCA
pointers -->
- [x] #50568 <!-- `Array(::AbstractRange)` should return an `Array` -->
- [x] #50871 <!-- macOS: Don't inspect dead threadtls during exception
handling. -->

Need manual backport:
- [ ] #48542 <!-- Add docs on task-specific buffering using
multithreading -->
- [ ] #50591 <!-- build: fix various makefile bugs -->


Non-merged PRs with backport label:
- [ ] #50842 <!-- Avoid race conditions with recursive rm -->
- [ ] #50823 <!-- Make ranges more robust with unsigned indexes. -->
- [ ] #50663 <!-- Fix Expr(:loopinfo) codegen -->
- [ ] #49716 <!-- Update varinfo() docstring signature -->
- [ ] #49713 <!-- prevent REPL from erroring in numbered mode in some
situations -->
- [ ] #49573 <!-- Implement jl_cpu_pause on PPC64 -->
- [ ] #48726 <!-- fix macro expansion of property destructuring -->
- [ ] #48642 <!-- Use gc alloc instead of alloc typed in lowering -->
- [ ] #48183 <!-- Don't use pkgimage for package if any includes fall in
tracked path for coverage or alloc tracking -->
- [ ] #48050 <!-- improve `--heap-size-hint` arg handling -->
- [ ] #47615 <!-- Allow threadsafe access to buffer of type inference
profiling trees -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport 1.9 Change should be backported to release-1.9
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants