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

Profile: Faster data dict lookup #43805

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions stdlib/Profile/src/Profile.jl
Original file line number Diff line number Diff line change
Expand Up @@ -349,15 +349,18 @@ function getdict(data::Vector{UInt})
end

function getdict!(dict::LineInfoDict, data::Vector{UInt})
for ip in data
# Lookup is expensive, so do it only once per ip.
haskey(dict, UInt64(ip)) && continue
st = lookup(convert(Ptr{Cvoid}, ip))
# To correct line numbers for moving code, put it in the form expected by
# Base.update_stackframes_callback[]
stn = map(x->(x, 1), st)
try Base.invokelatest(Base.update_stackframes_callback[], stn) catch end
dict[UInt64(ip)] = map(first, stn)
# we don't want metadata here as we're just looking up ips
unique_data_itr = Iterators.unique(has_meta(data) ? strip_meta(data) : data)
Copy link
Member

Choose a reason for hiding this comment

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

Since unique uses hashing which is much of what Dicts do, why is this a speed improvement?

Copy link
Member Author

Choose a reason for hiding this comment

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

That just facilitates identifying the unique ips before going off to parallelize. It's the parallel part that helps most

foreach(ip -> dict[UInt64(ip)] = StackFrame[], unique_data_itr)
@sync for ip in unique_data_itr
Threads.@spawn begin
st = lookup(convert(Ptr{Cvoid}, ip))
# To correct line numbers for moving code, put it in the form expected by
# Base.update_stackframes_callback[]
stn = map(x->(x, 1), st)
try Base.invokelatest(Base.update_stackframes_callback[], stn) catch end
dict[UInt64(ip)] = map(first, stn)
Copy link
Member

Choose a reason for hiding this comment

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

I always forget if Dict is threadsafe. I don't think it is?

Copy link
Member Author

Choose a reason for hiding this comment

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

I thought it was if it's preallocated like it is here?

Copy link
Member Author

Choose a reason for hiding this comment

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

At least experimentally they seem to be?

julia> dict = Dict{Int,Vector{Int}}();

julia> foreach(i -> dict[i] = Int[], 1:10_000)

julia> @sync for i in keys(dict)
           Threads.@spawn begin
               dict[i] = collect(1:i)
           end
       end

julia> for i in keys(dict)
           sum(dict[i]) == sum(1:i) || error()
       end

julia> 

Copy link
Member

Choose a reason for hiding this comment

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

Ah yes as long as we are not reallocating...

Copy link
Member

Choose a reason for hiding this comment

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

No, this is a very bad idea

end
end
return dict
end
Expand Down