Skip to content

Commit e8572ab

Browse files
committed
add a throttle macro
1 parent 7be1ca7 commit e8572ab

File tree

1 file changed

+55
-7
lines changed

1 file changed

+55
-7
lines changed

src/utils.jl

+55-7
Original file line numberDiff line numberDiff line change
@@ -551,8 +551,9 @@ _eltype(::AbstractArray{T}) where T = T
551551
"""
552552
throttle(f, timeout; leading=true, trailing=false)
553553
554-
Return a function that when invoked, will only be triggered at most once
555-
during `timeout` seconds.
554+
Return a function that when called, will only call the given `f` at most
555+
once during `timeout` seconds. Any arguments passed to this new function
556+
are passed to `f`.
556557
557558
Normally, the throttled function will run as much as it can, without ever
558559
going more than once per `wait` duration; but if you'd like to disable the
@@ -561,17 +562,27 @@ the trailing edge, pass `trailing=true`.
561562
562563
# Examples
563564
```jldoctest
564-
julia> a = Flux.throttle(() -> println("Flux"), 2);
565+
julia> noarg = Flux.throttle(() -> println("Flux"), 2);
565566
566-
julia> for i = 1:4 # a called in alternate iterations
567-
a()
567+
julia> for i in 1:4
568+
noarg() # println called in alternate iterations
568569
sleep(1)
569570
end
570571
Flux
571572
Flux
573+
574+
julia> onearg = Flux.throttle(i -> println("step = ", i), 1);
575+
576+
julia> for i in 1:10
577+
onearg(i)
578+
sleep(0.3)
579+
end
580+
step = 1
581+
step = 5
582+
step = 9
572583
```
573584
"""
574-
function throttle(f, timeout; leading=true, trailing=false)
585+
function throttle(f, timeout::Real; leading=true, trailing=false)
575586
cooldown = true
576587
later = nothing
577588
result = nothing
@@ -603,6 +614,44 @@ function throttle(f, timeout; leading=true, trailing=false)
603614
end
604615
end
605616

617+
"""
618+
@throttle timeout expr
619+
620+
Evaluates the given expression at most once every `timeout` seconds.
621+
622+
Internally, it uses [`throttle`](@ref Flux.throttle). But instead of
623+
defining a function outside the loop, it lets you place the code inside
624+
the loop.
625+
626+
# Example
627+
```jldoctest
628+
julia> for i in 1:20
629+
j = 100i
630+
sleep(0.2)
631+
Flux.@throttle 0.9 if iseven(i)
632+
println("i = ", i, ", and j = ", j)
633+
else
634+
println("i = ", i)
635+
end
636+
end
637+
i = 1
638+
i = 6, and j = 600
639+
i = 11
640+
i = 16, and j = 1600
641+
```
642+
"""
643+
macro throttle(timeout::Real, ex)
644+
expr = macroexpand(__module__, ex)
645+
vars = unique(_allsymbols(expr))
646+
@gensym fast slow
647+
Base.eval(__module__, :($fast($(vars...)) = $expr))
648+
Base.eval(__module__, :(const $slow = $throttle($fast, $timeout)))
649+
:($slow($(vars...))) |> esc
650+
end
651+
652+
_allsymbols(s::Symbol) =[s]
653+
_allsymbols(other) = Symbol[]
654+
_allsymbols(ex::Expr) = vcat(_allsymbols.(ex.args)...)
606655

607656
"""
608657
modules(m)
@@ -675,7 +724,6 @@ julia> loss() = rand();
675724
676725
julia> trigger = Flux.patience(() -> loss() < 1, 3);
677726
678-
679727
julia> for i in 1:10
680728
@info "Epoch \$i"
681729
trigger() && break

0 commit comments

Comments
 (0)