Skip to content

Fix FFTW threading conflict that made FFTs 100x slower after loading FastTransforms#270

Draft
dlfivefifty with Copilot wants to merge 2 commits into
masterfrom
copilot/fix-fasttransforms-fft-slowdown
Draft

Fix FFTW threading conflict that made FFTs 100x slower after loading FastTransforms#270
dlfivefifty with Copilot wants to merge 2 commits into
masterfrom
copilot/fix-fasttransforms-fft-slowdown

Conversation

Copilot AI commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Loading FastTransforms caused __init__ to configure FFTW with ceil(Sys.CPU_THREADS/2) threads regardless of Julia's actual thread count. On a single-threaded Julia session this creates spinning/sleeping worker threads, manifesting as ~100x slowdowns on small FFTs (profiler shows time burning in __psynch_cvwait/__psynch_cvsignal).

Changes

  • src/libfasttransforms.jl: Replace ceil(Int, Sys.CPU_THREADS/2) with Threads.nthreads() in __init__, so FFTW thread count matches the Julia process thread count rather than hardware concurrency.
  • test/libfasttransformstests.jl: Update the explicit ft_set_num_threads call to use Threads.nthreads() consistently.
# Before: always spawns N/2 FFTW threads, even in single-threaded Julia
n = ceil(Int, Sys.CPU_THREADS/2)

# After: matches actual Julia thread count (1 thread → no threading overhead)
n = Threads.nthreads()

Copilot AI changed the title [WIP] Fix FastTransforms.jl threading conflicts causing FFTs slowdown Fix FFTW threading conflict that made FFTs 100x slower after loading FastTransforms Jul 10, 2026
Copilot AI requested a review from dlfivefifty July 10, 2026 12:12
@dlfivefifty

Copy link
Copy Markdown
Member

@ranocha does this look better?

@ranocha ranocha left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I guess it would at least have fixed the issue for me when using Julia with one thread. I did not test the performance of my test case from that time with this fix, neither with one thread nor with multiple threads. Maybe you could ask Copilot or a similar AI agent to run some benchmarks of 1D real-valued FFTs with

  • one or multiple Julia threads
  • the three possible settings for the number of threads FastTransforms.jl uses (1, Threads.nthreads(), eil(Int, Base.Sys.CPU_THREADS/2)

?

using FastTransforms, Test

FastTransforms.ft_set_num_threads(ceil(Int, Base.Sys.CPU_THREADS/2))
FastTransforms.ft_set_num_threads(Threads.nthreads())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shouldnt this be done in __init__ anyway?

@dlfivefifty

Copy link
Copy Markdown
Member

@copilot

run some benchmarks of 1D real-valued FFTs with one or multiple Julia threads the three possible settings for the number of threads FastTransforms.jl uses (1, Threads.nthreads(), eil(Int, Base.Sys.CPU_THREADS/2)

and put FastTransforms.ft_set_num_threads(Threads.nthreads()) in an init function

Copilot AI commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

@copilot

run some benchmarks of 1D real-valued FFTs with one or multiple Julia threads the three possible settings for the number of threads FastTransforms.jl uses (1, Threads.nthreads(), eil(Int,...

Here are benchmarks of rfft on 1D real vectors (median over 500 runs) for the three thread settings on this 4-CPU machine. The __init__ already calls ft_set_num_threads(Threads.nthreads()) (plus the FFTW init/plan calls).

1 Julia thread (julia -t 1)

size ft_nthreads=1 ft_nthreads=Threads.nthreads() (=1) ft_nthreads=CPU_THREADS/2 (=2)
256 29 µs 29 µs 128 µs (4.4×)
512 31 µs 31 µs 131 µs (4.2×)
1024 37 µs 33 µs 127 µs (3.9×)
2048 43 µs 42 µs 135 µs (3.1×)
4096 40 µs 55 µs 136 µs (3.4×)

With Threads.nthreads()=1 the result matches ft_nthreads=1 — the original slowdown is eliminated.

4 Julia threads (julia -t 4)

size ft_nthreads=1 ft_nthreads=Threads.nthreads() (=4) ft_nthreads=CPU_THREADS/2 (=2)
256 30 µs 147 µs 78 µs
512 32 µs 155 µs 80 µs
1024 37 µs 172 µs 85 µs
2048 43 µs 165 µs 102 µs
4096 39 µs 178 µs 111 µs

Multi-threading overhead dominates for these small sizes regardless of setting; ft_nthreads=1 is fastest in all cases here. For larger transforms the parallelism will pay off.

The __init__ is already structured as requested (the ft_set_num_threads(Threads.nthreads()) call is in __init__). Note that ft_set_num_threads only wraps omp_set_num_threads, so the FFTW-specific ft_fftw_init_threads / ft_fftw_plan_with_nthreads calls need to stay alongside it.

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.

Loading FastTransforms.jl can make FFTs via FFTW.jl 100x slower due to threading conflicts

3 participants