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

Detect multiple concurent writes to Dict + remove dead code #44778

Merged
merged 6 commits into from
Apr 4, 2022

Conversation

petvana
Copy link
Member

@petvana petvana commented Mar 28, 2022

Thanks to #38180, the removed code seems to be dead because no finalizers should be used anymore to modify dictionaries (it was dangerous). Furthermore, it may help users to detect illegal concurrent writes, since it doesn't recurse and have new error message. There should be no, or even a positive, performance effect.

Btw, this seems to be the last missing step to allow manual shrinkage at sizehint!

julia/base/dict.jl

Lines 251 to 256 in 89a613b

if newsz <= oldsz
# todo: shrink
# be careful: rehash!() assumes everything fits. it was only designed
# for growing.
return d
end

Master

$ julia -t 2

julia> d = Dict()
Dict{Any, Any}()

julia> Threads.@threads for i = 1:1000; d[i] = i; end
#... Hangs for ... so long ... and I terminated the loop

PR

$ julia -t 2

julia> d = Dict()
Dict{Any, Any}()

julia> Threads.@threads for i = 1:1000; d[i] = i; end
ERROR: TaskFailedException
Stacktrace:
 [1] wait
   @ ./task.jl:345 [inlined]
 [2] threading_run(fun::var"#23#threadsfor_fun#5"{var"#23#threadsfor_fun#4#6"{UnitRange{Int64}}}, static::Bool)
   @ Base.Threads ./threadingconstructs.jl:38
 [3] top-level scope
   @ ./threadingconstructs.jl:89

    nested task error: AssertionError: Muliple concurent writes to Dict detected!
    Stacktrace:
     [1] rehash!(h::Dict{Any, Any}, newsz::Int64)
       @ Base ./dict.jl:235
     [2] _setindex!
       @ ./dict.jl:382 [inlined]
     [3] setindex!(h::Dict{Any, Any}, v::Any, key::Int64)
       @ Base ./dict.jl:419
     [4] macro expansion
       @ ./REPL[2]:1 [inlined]
     [5] (::var"#23#threadsfor_fun#5"{var"#23#threadsfor_fun#4#6"{UnitRange{Int64}}})(tid::Int64; onethread::Bool)
       @ Main ./threadingconstructs.jl:84
     [6] #23#threadsfor_fun
       @ ./threadingconstructs.jl:52 [inlined]
     [7] (::Base.Threads.var"#1#2"{var"#23#threadsfor_fun#5"{var"#23#threadsfor_fun#4#6"{UnitRange{Int64}}}, Int64})()
       @ Base.Threads ./threadingconstructs.jl:30

base/dict.jl Outdated
@@ -237,7 +232,7 @@ end
h.count = count
h.ndel = 0
h.maxprobe = maxprobe
@assert h.age == age0
@assert h.age == age0 "Muliple concurent writes to Dict detected!"
Copy link
Member

Choose a reason for hiding this comment

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

move this above the assignments, so we only do the mutation if things are not broken?

Copy link
Member Author

@petvana petvana Mar 28, 2022

Choose a reason for hiding this comment

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

It's only a sanity check for now and thus can be moved anywhere. I've also added one aging for more safety.

@vtjnash
Copy link
Member

vtjnash commented Mar 28, 2022

This seems good. Does it improve TTFP too? This call used to cause a lot of inference latency.

@vtjnash
Copy link
Member

vtjnash commented Mar 28, 2022

(I am fairly sure that comment is also very old, and refers to a time when we assumed that doubling the size meant eliminating hash collisions–a false assumption we deleted long ago)

@petvana
Copy link
Member Author

petvana commented Mar 28, 2022

This seems good. Does it improve TTFP too? This call used to cause a lot of inference latency.

Unfortunately not, TTFP seems unchanged.

@petvana petvana force-pushed the pv-dict-check-age branch from a6f8e01 to d18b4c2 Compare March 28, 2022 20:31
@petvana petvana marked this pull request as ready for review March 28, 2022 20:33
@vtjnash vtjnash added the merge me PR is reviewed. Merge when all tests are passing label Mar 28, 2022
@aviatesk
Copy link
Member

@nanosoldier runbenchmarks("inference", vs=":master")

@nanosoldier
Copy link
Collaborator

Your benchmark job has completed - no performance regressions were detected. A full report can be found here.

@petvana petvana marked this pull request as draft March 30, 2022 08:06
test/dict.jl Outdated
end
else
# Multiple threads not enabled.
@test false
Copy link
Member Author

@petvana petvana Mar 30, 2022

Choose a reason for hiding this comment

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

Not sure, if multiple threads are enabled in CI. Just to make sure, it will be deleted.

Copy link
Member Author

@petvana petvana Mar 30, 2022

Choose a reason for hiding this comment

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

Any idea on how to enable CI with Threads.nthreads() != 1? Going through other tests and not finding the right way.

Copy link
Member Author

Choose a reason for hiding this comment

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

Does it mean that the following test is disabled now in CI?

# basic lock check
if nthreads() > 1
let lk = Base.Threads.SpinLock()

@simeonschaub simeonschaub removed the merge me PR is reviewed. Merge when all tests are passing label Apr 1, 2022
@simeonschaub
Copy link
Member

The test seems fundamentally flawed since there is no guarantee you actually will run into a data race, so you might get nondeterministic test failures.

@petvana
Copy link
Member Author

petvana commented Apr 2, 2022

The test seems fundamentally flawed since there is no guarantee you actually will run into a data race, so you might get nondeterministic test failures.

Fair point, thanks. Although the probability would be very low for the test to fail for larger dictionaries (based on how long rehashing takes), it may still happen. So, I removed the test, and PR is ready to be merged.

@petvana petvana marked this pull request as ready for review April 2, 2022 17:35
@simeonschaub simeonschaub added the merge me PR is reviewed. Merge when all tests are passing label Apr 2, 2022
base/dict.jl Outdated Show resolved Hide resolved
base/dict.jl Outdated Show resolved Hide resolved
Co-authored-by: Mosè Giordano <giordano@users.noreply.github.com>
@giordano giordano self-requested a review April 2, 2022 21:37
@vtjnash vtjnash merged commit 51271e0 into JuliaLang:master Apr 4, 2022
@giordano giordano removed the merge me PR is reviewed. Merge when all tests are passing label Apr 4, 2022
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.

6 participants