From f8b70e613650b9ade095ce85d95f403478b3f19a Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 21 Oct 2026 10:25:11 +0200 Subject: [PATCH 1/2] Pass the output type of `tforeach` positionally Passed through keyword arguments, `outputtype = Nothing` is stored as a `DataType`, so `_tmapreduce` was compiled for every output type at once and a scheduler's implementation compiled the reduction path for every `tforeach` body, a loop without a result. `tforeach` now calls a positional entry point that `tmapreduce` shares, with `Nothing` as a constant. With ThreadTeams' team scheduler this takes the first loop of a new body from about 100 to 80 ms. --- src/implementation.jl | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/implementation.jl b/src/implementation.jl index 95af5c2..0c663a9 100644 --- a/src/implementation.jl +++ b/src/implementation.jl @@ -91,6 +91,16 @@ end outputtype::Type = Any, init = NotGiven(), kwargs...) where {F, OP} + return tmapreduce_positional(f, op, Arrs, outputtype, scheduler, init, kwargs) +end + +# `tmapreduce` with the output type as a positional argument: passed through keyword +# arguments, a type is stored as a `DataType`, and the parallel implementation is then +# compiled for every output type at once (for `tforeach`, also the reduction it never does). +# No type parameter: an output type only known at run time would then be dispatched on +# before the serial fallback; `_tmapreduce` specializes on it instead. +@inline function tmapreduce_positional(f::F, op::OP, Arrs, outputtype::Type, + scheduler, init, kwargs) where {F, OP} mapreduce_kwargs = isgiven(init) ? (; init) : (;) _scheduler = _scheduler_from_userinput(scheduler; kwargs...) @@ -462,8 +472,10 @@ end #------------------------------------------------------------- -function tforeach(f::F, A...; kwargs...)::Nothing where {F} - tmapreduce(f, (l, r) -> l, A...; kwargs..., init = nothing, outputtype = Nothing) +function tforeach(f::F, A...; scheduler::MaybeScheduler = NotGiven(), + init = nothing, outputtype::Type = Nothing, kwargs...)::Nothing where {F} + tmapreduce_positional(f, (l, r) -> l, A, Nothing, scheduler, nothing, kwargs) + return nothing end #------------------------------------------------------------- From f0e43eba1ee98fc3094b1b61941e4ebb5e310f01 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 21 Oct 2026 10:38:45 +0200 Subject: [PATCH 2/2] Add a changelog entry, bump the version --- CHANGELOG.md | 5 +++++ Project.toml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a42e105..f4ca116 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ OhMyThreads.jl Changelog ========================= +Version 0.8.9 +------------ +- ![Enhancement][badge-enhancement] `tforeach` no longer compiles a scheduler's implementation for every output type: its output type (`Nothing`) is now passed on as a constant, instead of a `DataType` in the keyword arguments. With schedulers that compile a reduction only when there is a result, this saves the compilation of the reduction for every loop body [#177][gh-pr-177]. + Version 0.8.8 ------------ - ![Enhancement][badge-enhancement] `tforeach`, `treduce`, `treducemap`, `tmap`, `tmap!`, and `tmapreduce` now always specialize on the function arguments. Before, a call could run through an instance compiled for `f::Function`, and dispatch every call of `f` dynamically, e.g. `tforeach(_ -> work(n), xs)` with a non-inlined `work` and a run-time `n` [#176][gh-pr-176]. @@ -196,3 +200,4 @@ Version 0.2.0 [gh-pr-173]: https://github.com/JuliaFolds2/OhMyThreads.jl/pull/173 [gh-pr-174]: https://github.com/JuliaFolds2/OhMyThreads.jl/pull/174 [gh-pr-176]: https://github.com/JuliaFolds2/OhMyThreads.jl/pull/176 +[gh-pr-177]: https://github.com/JuliaFolds2/OhMyThreads.jl/pull/177 diff --git a/Project.toml b/Project.toml index b8df230..df7d19c 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "OhMyThreads" uuid = "67456a42-1dca-4109-a031-0a68de7e3ad5" authors = ["Carsten Bauer ", "Mason Protter "] -version = "0.8.8" +version = "0.8.9" [deps] BangBang = "198e06fe-97b7-11e9-32a5-e1d131e6ad66"