Skip to content

safer access to Julia's type inference #8

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

Merged
merged 2 commits into from
Mar 24, 2025

Conversation

nsajko
Copy link
Contributor

@nsajko nsajko commented Mar 24, 2025

  • Only access an implementation detail of Julia when it's available, falling back to Any as an accurate but possibly imprecise approximation of the return type.

Relevant links to the Julia code base:

@nsajko
Copy link
Contributor Author

nsajko commented Mar 24, 2025

Yeah, it seems an explicit world age parameter is required for Base.infer_return_type. Will figure that out a bit later.

@nsajko
Copy link
Contributor Author

nsajko commented Mar 24, 2025

NB: the Julia nightly CI job also fails on PR #9, which doesn't change any source code. I guess it might perhaps be possible to fix the test failure by providing the correct world age explicitly, however it also might be OK to leave that for a future PR, given that it doesn't regress here.

@MasonProtter
Copy link
Member

MasonProtter commented Mar 24, 2025

My concern about this is that it's very slow.

julia> @btime Base.infer_return_type(sin, Tuple{Float64})
  454.411 ns (14 allocations: 784 bytes)
Float64

That's twice as long as the time it takes to spawn a task

julia> @btime Threads.@spawn 1
  253.153 ns (4 allocations: 480 bytes)
Task (done) @0x00007fd60c07c010

meaning that this change would cause substantial shifts in the window where various problems become amenable to multithreading, and also makes stuff like load balancing through the launching of many tasks unsustainable.

The above benchmark was on v1.11, but I just tried on master and it's now another 10x slower than v1.11:

julia> @btime Base.infer_return_type(sin, Tuple{Float64})
  4.785 μs (54 allocations: 4.00 KiB)
Float64

whereas Core.Compiler.return_type has a tfunc and is computed at compile time:

julia> @btime Core.Compiler.return_type(sin, Tuple{Float64})
  1.082 ns (0 allocations: 0 bytes)
Float64

I'd be in favour of accepting this change though just without the infer_return_type branch.

isdefined(Core.Compiler, :return_type) &&
applicable(Core.Compiler.return_type, identity, Tuple{})
)
infer_return_type(@nospecialize f::Any) = Core.Compiler.return_type(f, Tuple{})
Copy link
Member

Choose a reason for hiding this comment

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

one last question, is there much reason for the @nospecialize here? Won't that just potentially block inference?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@nospecialize doesn't block inference, that's @nospecializeinfer. My reasoning is that the computation only happens at macro instantiation time, so there's not much reason to specialize.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

Ah I see. I was always a bit shaky on what things @nospecialize blocked and what it didn't, but I see now the docstring actually has some good info on this, specifically about the lack of inference blocking.

Copy link
Member

@MasonProtter MasonProtter left a comment

Choose a reason for hiding this comment

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

I've fixed the CI on master, could you rebase your changes onto master / pull master just so I can recheck that the tests still pass?

After that I'll merge.

nsajko added 2 commits March 24, 2025 20:33
* Only access an implementation detail of Julia when it's available,
  falling back to `Any` as an accurate but possibly imprecise
  approximation of the return type.
* Prefer `Base.infer_return_type` to `Core.Compiler.return_type`
    * The former is more public than the latter:
        * JuliaLang/julia#53105 (comment)
    * Warning/TODO: both of these accept a world age as an optional
      parameter, but differ (?) in the default value choice. Thus it
      might be necessary to provide a world age explicitly, not sure.

Relevant links to the Julia code base:
* `Core.Compiler.return_type`:
    * https://github.com/JuliaLang/julia/blob/99a764750ef4e53214f2247d186ff4aac4f91983/Compiler/src/typeinfer.jl#L1416-L1434
* `Base.infer_return_type`
    * https://github.com/JuliaLang/julia/blob/99a764750ef4e53214f2247d186ff4aac4f91983/base/reflection.jl#L534-L601
@nsajko nsajko force-pushed the infer_return_type branch from 2b3c02c to cfa00d3 Compare March 24, 2025 19:34
@MasonProtter
Copy link
Member

Thanks!

@MasonProtter MasonProtter merged commit 1c0fe1e into JuliaFolds2:master Mar 24, 2025
4 checks passed
@nsajko nsajko deleted the infer_return_type branch March 24, 2025 19:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants