@@ -195,11 +195,11 @@ function time_print(io::IO, elapsedtime, bytes=0, gctime=0, allocs=0, lock_confl
195195 nothing
196196end
197197
198- function timev_print (elapsedtime, diff:: GC_Diff , compile_times; msg:: Union{String,Nothing} = nothing )
198+ function timev_print (elapsedtime, diff:: GC_Diff , lock_conflicts, compile_times; msg:: Union{String,Nothing} = nothing )
199199 allocs = gc_alloc_count (diff)
200200 compile_time = first (compile_times)
201201 recompile_time = last (compile_times)
202- time_print (stdout , elapsedtime, diff. allocd, diff. total_time, allocs, compile_time, recompile_time, true ; msg)
202+ time_print (stdout , elapsedtime, diff. allocd, diff. total_time, allocs, lock_conflicts, compile_time, recompile_time, true ; msg)
203203 padded_nonzero_print (elapsedtime, " elapsed time (ns)" )
204204 padded_nonzero_print (diff. total_time, " gc time (ns)" )
205205 padded_nonzero_print (diff. allocd, " bytes allocated" )
231231A macro to execute an expression, printing the time it took to execute, the number of
232232allocations, and the total number of bytes its execution caused to be allocated, before
233233returning the value of the expression. Any time spent garbage collecting (gc), compiling
234- new code, or recompiling invalidated code is shown as a percentage.
234+ new code, or recompiling invalidated code is shown as a percentage. Any lock conflicts
235+ where a lock had to wait are shown as a count.
235236
236237Optionally provide a description string to print before the time report.
237238
@@ -252,6 +253,9 @@ See also [`@showtime`](@ref), [`@timev`](@ref), [`@timed`](@ref), [`@elapsed`](@
252253
253254 Recompilation time being shown separately from compilation time was introduced in Julia 1.8
254255
256+ !!! compat "Julia 1.11"
257+ The reporting of any lock conflicts was added in Julia 1.11.
258+
255259```julia-repl
256260julia> x = rand(10,10);
257261
@@ -373,18 +377,22 @@ end
373377macro timev (msg, ex)
374378 quote
375379 Experimental. @force_compile
380+ Threads. lock_profiling (true )
381+ local lock_conflicts = Threads. LOCK_CONFLICT_COUNT[]
376382 local stats = gc_num ()
377383 local elapsedtime = time_ns ()
378384 cumulative_compile_timing (true )
379385 local compile_elapsedtimes = cumulative_compile_time_ns ()
380386 local val = @__tryfinally ($ (esc (ex)),
381387 (elapsedtime = time_ns () - elapsedtime;
382388 cumulative_compile_timing (false );
383- compile_elapsedtimes = cumulative_compile_time_ns () .- compile_elapsedtimes)
389+ compile_elapsedtimes = cumulative_compile_time_ns () .- compile_elapsedtimes;
390+ lock_conflicts = Threads. LOCK_CONFLICT_COUNT[] - lock_conflicts;
391+ Threads. lock_profiling (false ))
384392 )
385393 local diff = GC_Diff (gc_num (), stats)
386394 local _msg = $ (esc (msg))
387- timev_print (elapsedtime, diff, compile_elapsedtimes; msg= _msg)
395+ timev_print (elapsedtime, diff, lock_conflicts, compile_elapsedtimes; msg= _msg)
388396 val
389397 end
390398end
@@ -478,6 +486,41 @@ macro allocations(ex)
478486 end
479487end
480488
489+ """
490+ @lock_conflicts
491+
492+ A macro to evaluate an expression, discard the resulting value, and instead return the
493+ total number of lock conflicts during evaluation, where a lock attempt resulted in a wait
494+ because the lock was already held.
495+
496+ See also [`@time`](@ref), [`@timev`](@ref) and [`@timed`](@ref).
497+
498+ ```julia-repl
499+ julia> @lock_conflicts begin
500+ l = ReentrantLock()
501+ Threads.@threads for i in 1:Threads.nthreads()
502+ lock(l) do
503+ sleep(1)
504+ end
505+ end
506+ end
507+ 5
508+ ```
509+
510+ !!! compat "Julia 1.11"
511+ This macro was added in Julia 1.11.
512+ """
513+ macro lock_conflicts (ex)
514+ quote
515+ Experimental. @force_compile
516+ Threads. lock_profiling (true )
517+ local lock_conflicts = Threads. LOCK_CONFLICT_COUNT[]
518+ $ (esc (ex))
519+ Threads. lock_profiling (false )
520+ Threads. LOCK_CONFLICT_COUNT[] - lock_conflicts
521+ end
522+ end
523+
481524"""
482525 @timed
483526
@@ -513,15 +556,23 @@ julia> stats.gcstats.total_time
513556
514557!!! compat "Julia 1.5"
515558 The return type of this macro was changed from `Tuple` to `NamedTuple` in Julia 1.5.
559+
560+ !!! compat "Julia 1.11"
561+ The field `lock_conflicts` was added to the returned NamedTuple in Julia 1.11.
516562"""
517563macro timed (ex)
518564 quote
519565 Experimental. @force_compile
566+ Threads. lock_profiling (true )
567+ local lock_conflicts = Threads. LOCK_CONFLICT_COUNT[]
520568 local stats = gc_num ()
521569 local elapsedtime = time_ns ()
522- local val = $ (esc (ex))
523- elapsedtime = time_ns () - elapsedtime
570+ local val = @__tryfinally ($ (esc (ex)),
571+ (elapsedtime = time_ns () - elapsedtime;
572+ lock_conflicts = Threads. LOCK_CONFLICT_COUNT[] - lock_conflicts;
573+ Threads. lock_profiling (false ))
574+ )
524575 local diff = GC_Diff (gc_num (), stats)
525- (value= val, time= elapsedtime/ 1e9 , bytes= diff. allocd, gctime= diff. total_time/ 1e9 , gcstats= diff)
576+ (value= val, time= elapsedtime/ 1e9 , bytes= diff. allocd, gctime= diff. total_time/ 1e9 , gcstats= diff, lock_conflicts = lock_conflicts )
526577 end
527578end
0 commit comments