Skip to content

Commit f0ec9b9

Browse files
update other time macros and add @lock_conflicts
1 parent 9b4106a commit f0ec9b9

File tree

5 files changed

+73
-8
lines changed

5 files changed

+73
-8
lines changed

NEWS.md

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ New language features
2121
a `Manifest-v1.11.toml` would be used by v1.11 and `Manifest.toml` by every other julia
2222
version. This makes managing environments for multiple julia versions at the same time
2323
easier ([#43845]).
24+
* `@time` now reports a count of any lock conflicts where a lock had to wait, plus a new macro
25+
`@lock_conflicts` which returns that count ([#52883]).
2426

2527
Language changes
2628
----------------

base/exports.jl

+1
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,7 @@ export
10361036
@elapsed,
10371037
@allocated,
10381038
@allocations,
1039+
@lock_conflicts,
10391040

10401041
# tasks
10411042
@sync,

base/timing.jl

+59-8
Original file line numberDiff line numberDiff line change
@@ -195,11 +195,11 @@ function time_print(io::IO, elapsedtime, bytes=0, gctime=0, allocs=0, lock_confl
195195
nothing
196196
end
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")
@@ -231,7 +231,8 @@ end
231231
A macro to execute an expression, printing the time it took to execute, the number of
232232
allocations, and the total number of bytes its execution caused to be allocated, before
233233
returning 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
236237
Optionally 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
256260
julia> x = rand(10,10);
257261
@@ -373,18 +377,22 @@ end
373377
macro 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
390398
end
@@ -478,6 +486,41 @@ macro allocations(ex)
478486
end
479487
end
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 evalution, 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
"""
517563
macro 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
527578
end

doc/src/base/base.md

+1
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ Base.@timed
352352
Base.@elapsed
353353
Base.@allocated
354354
Base.@allocations
355+
Base.@lock_conflicts
355356
Base.EnvDict
356357
Base.ENV
357358
Base.Sys.STDLIB

test/misc.jl

+10
Original file line numberDiff line numberDiff line change
@@ -1391,6 +1391,16 @@ end
13911391
@test (@allocations "a") == 0
13921392
@test (@allocations "a" * "b") == 0 # constant propagation
13931393
@test (@allocations "a" * Base.inferencebarrier("b")) == 1
1394+
1395+
lc = @lock_conflicts begin
1396+
l = ReentrantLock()
1397+
Threads.@threads for i in 1:Threads.nthreads()
1398+
lock(l) do
1399+
sleep(0.5)
1400+
end
1401+
end
1402+
end
1403+
@test lc = Threads.nthreads() - 1
13941404
end
13951405

13961406
@testset "in_finalizer" begin

0 commit comments

Comments
 (0)