CUDA.jl is slowed down after some number of iterations

Hello!
When looping simple math operations it was found that CUDA.jl is slowed down after some number of iterations. In this case, most of the execution time is consumed by CuLaunchKernel procedure. I thought that a custom CUDA kernel will cure this problem. However, i got the similar performance result when running the kernel. The code is as follows:

using CUDA
function math2!(D, E, F)
@.    F = D * E + D / E - D * E + D^2 - E^2 + D / D - E / E
    return
end
F       = CUDA.zeros(4096, 4096)
D       = CUDA.rand(4096, 4096)
E       = CUDA.rand(4096, 4096)
CUDA.@profile for iter = 1:10000
math2!(D, E, F)
end
Profiler ran for 63.42 s, capturing 3060002 events.

Host-side activity: calling CUDA APIs took 47.2 s (74.42% of the trace)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Time (%) β”‚ Total time β”‚ Calls β”‚ Time distribution                    β”‚ Name           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚   74.10% β”‚     47.0 s β”‚ 10000 β”‚    4.7 ms Β± 17.59  (   0.0 β€₯ 390.86) β”‚ cuLaunchKernel β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

And the custom CUDA kernel is as follows:

function math1!(D, E, F)
    ix = (blockIdx().x-1) * blockDim().x + threadIdx().x
    iy = (blockIdx().y-1) * blockDim().y + threadIdx().y
    F[ix,iy] = D[ix,iy] * E[ix,iy] + D[ix,iy] / E[ix,iy] - D[ix,iy] * E[ix,iy] +
               D[ix,iy]^2 - E[ix,iy]^2 + D[ix,iy] / D[ix,iy] - E[ix,iy] / E[ix,iy]
    return
end
threads = (32, 32)
blocks  = (128, 128)
nx, ny  = threads[1]*blocks[1], threads[2]*blocks[2]
F       = CUDA.zeros(nx, ny)
D       = CUDA.rand(nx, ny)
E       = CUDA.rand(nx, ny)
CUDA.@profile for iter = 1:10000
@cuda blocks=blocks threads=threads math1!(D, E, F)
end
rofiler ran for 28.22 s, capturing 180002 events.

Host-side activity: calling CUDA APIs took 16.5 s (58.47% of the trace)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Time (%) β”‚ Total time β”‚ Calls β”‚ Time distribution                    β”‚ Name           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚   58.44% β”‚    16.49 s β”‚ 10000 β”‚   1.65 ms Β± 9.28   (   0.0 β€₯ 303.43) β”‚ cuLaunchKernel β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Note please that low number of iterations is executed in no time. For example, it takes around 7 ms to compute 1000 iterations.
The software and hardware are as follows:

CUDA runtime 12.6, artifact installation
CUDA driver 12.2
NVIDIA driver 536.23.0

CUDA libraries: 
- CUBLAS: 12.6.3
- CURAND: 10.3.7
- CUFFT: 11.3.0
- CUSOLVER: 11.7.1
- CUSPARSE: 12.5.4
- CUPTI: 2024.3.2 (API 24.0.0)
- NVML: 12.0.0+536.23

Julia packages: 
- CUDA: 5.5.2
- CUDA_Driver_jll: 0.10.3+0
- CUDA_Runtime_jll: 0.15.3+0

Toolchain:
- Julia: 1.11.1
- LLVM: 16.0.6

1 device:
  0: NVIDIA GeForce GTX 960 (sm_52, 1.850 GiB / 4.000 GiB available)

Can someone give me a hint in accordance with this issue?

Hi,

Not really an answer, but by adding CUDA.@sync the individual kernel launches take approximately as long when using 1000 and 10000 iterations. So maybe there is some implicit synchronisation going on, perhaps because the scheduler has difficulty managing resources at 10000 (not necessarily sequential) iterations.

Profiling
julia> CUDA.@profile for iter = 1:1000
           @cuda blocks=blocks threads=threads math1!(D, E, F)
       end
Profiler ran for 1.06 s, capturing 30022 events.

Host-side activity: calling CUDA APIs took 6.12 ms (0.58% of the trace)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Time (%) β”‚ Total time β”‚ Calls β”‚ Time distribution                  β”‚ Name                β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    0.30% β”‚    3.18 ms β”‚  1000 β”‚   3.18 Β΅s Β± 2.44   (   2.3 β€₯ 31.8) β”‚ cuLaunchKernel      β”‚
β”‚    0.08% β”‚   848.9 Β΅s β”‚     1 β”‚                                    β”‚ cuModuleLoadDataEx  β”‚
β”‚    0.00% β”‚    35.7 Β΅s β”‚     1 β”‚                                    β”‚ cuModuleGetFunction β”‚
β”‚    0.00% β”‚     5.6 Β΅s β”‚     1 β”‚                                    β”‚ cuCtxSynchronize    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Device-side activity: GPU was busy for 763.61 ms (72.12% of the trace)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Time (%) β”‚ Total time β”‚ Calls β”‚ Time distribution                       β”‚ Name                                       β‹―
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚   72.12% β”‚  763.61 ms β”‚  1000 β”‚ 763.61 Β΅s Β± 1862.44 (542.91 β€₯ 28205.17) β”‚ _Z6math1_13CuDeviceArrayI7Float32Li2ELi1EE β‹―
└──────────┴────────────┴───────┴─────────────────────────────────────────┴─────────────────────────────────────────────
                                                                                                        1 column omitted


julia> CUDA.@profile for iter = 1:10000
           @cuda blocks=blocks threads=threads math1!(D, E, F)
       end
Profiler ran for 5.88 s, capturing 300002 events.

Host-side activity: calling CUDA APIs took 3.74 s (63.66% of the trace)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Time (%) β”‚ Total time β”‚ Calls β”‚ Time distribution                       β”‚ Name           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚   63.22% β”‚     3.72 s β”‚ 10000 β”‚ 371.75 Β΅s Β± 3344.27 (   2.3 β€₯ 205377.5) β”‚ cuLaunchKernel β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Device-side activity: GPU was busy for 5.86 s (99.67% of the trace)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Time (%) β”‚ Total time β”‚ Calls β”‚ Time distribution                      β”‚ Name                                        β‹―
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚   99.67% β”‚     5.86 s β”‚ 10000 β”‚  586.1 Β΅s Β± 758.69 (542.49 β€₯ 28883.05) β”‚ _Z6math1_13CuDeviceArrayI7Float32Li2ELi1EES β‹―
└──────────┴────────────┴───────┴────────────────────────────────────────┴──────────────────────────────────────────────
                                                                                                        1 column omitted


julia> CUDA.@profile for iter = 1:1000
           CUDA.@sync @cuda blocks=blocks threads=threads math1!(D, E, F)
       end
Profiler ran for 952.09 ms, capturing 805355 events.

Host-side activity: calling CUDA APIs took 132.22 ms (13.89% of the trace)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Time (%) β”‚ Total time β”‚ Calls β”‚ Time distribution                      β”‚ Name                β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚   52.61% β”‚  500.88 ms β”‚  1000 β”‚ 500.88 Β΅s Β± 1977.04 (   1.5 β€₯ 14872.2) β”‚ cuStreamSynchronize β”‚
β”‚    0.92% β”‚    8.75 ms β”‚  1000 β”‚   8.75 Β΅s Β± 4.92   (   6.7 β€₯ 109.8)    β”‚ cuLaunchKernel      β”‚
β”‚    0.00% β”‚     1.9 Β΅s β”‚     2 β”‚ 949.95 ns Β± 212.05 (800.01 β€₯ 1099.89)  β”‚ cuCtxSetCurrent     β”‚
β”‚    0.00% β”‚   400.0 ns β”‚     2 β”‚  200.0 ns Β± 0.0    ( 200.0 β€₯ 200.0)    β”‚ cuCtxGetDevice      β”‚
β”‚    0.00% β”‚  300.12 ns β”‚     2 β”‚ 150.06 ns Β± 70.63  (100.12 β€₯ 200.0)    β”‚ cuDeviceGetCount    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Device-side activity: GPU was busy for 841.61 ms (88.40% of the trace)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Time (%) β”‚ Total time β”‚ Calls β”‚ Time distribution                       β”‚ Name                                       β‹―
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚   88.40% β”‚  841.61 ms β”‚  1000 β”‚ 841.61 Β΅s Β± 1987.07 (543.07 β€₯ 15180.51) β”‚ _Z6math1_13CuDeviceArrayI7Float32Li2ELi1EE β‹―
└──────────┴────────────┴───────┴─────────────────────────────────────────┴─────────────────────────────────────────────
                                                                                                        1 column omitted


julia> CUDA.@profile for iter = 1:10000
           CUDA.@sync @cuda blocks=blocks threads=threads math1!(D, E, F)
       end
Profiler ran for 6.64 s, capturing 8040585 events.

Host-side activity: calling CUDA APIs took 982.98 ms (14.81% of the trace)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Time (%) β”‚ Total time β”‚ Calls β”‚ Time distribution                     β”‚ Name                  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚   34.72% β”‚      2.3 s β”‚ 10000 β”‚ 230.43 Β΅s Β± 721.81 (   1.1 β€₯ 37695.7) β”‚ cuStreamSynchronize   β”‚
β”‚    1.37% β”‚   90.72 ms β”‚ 10000 β”‚   9.07 Β΅s Β± 15.9   (   6.9 β€₯ 1500.6)  β”‚ cuLaunchKernel        β”‚
β”‚    0.00% β”‚     9.0 Β΅s β”‚     1 β”‚                                       β”‚ cuMemGetInfo          β”‚
β”‚    0.00% β”‚     1.1 Β΅s β”‚     2 β”‚ 550.06 ns Β± 636.32 (100.12 β€₯ 1000.01) β”‚ cuMemPoolGetAttribute β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Device-side activity: GPU was busy for 5.76 s (86.81% of the trace)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Time (%) β”‚ Total time β”‚ Calls β”‚ Time distribution                      β”‚ Name                                        β‹―
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚   86.81% β”‚     5.76 s β”‚ 10000 β”‚ 576.04 Β΅s Β± 721.27 (541.05 β€₯ 37987.78) β”‚ _Z6math1_13CuDeviceArrayI7Float32Li2ELi1EES β‹―
└──────────┴────────────┴───────┴────────────────────────────────────────┴──────────────────────────────────────────────
                                                                                                        1 column omitted

I’ve also moved the post from New to Julia to GPU, as I think you’re more likely to get responses there.

Hello,
Thanks for your help! I have also tested the code with CUDA.@sync
I don’t understand why the synchronization reduces the CuLaunchKernel time whereas the overall time remains the same due to additional synchronization procedure. When searching for the info, i found the same problem in CUDA/C++. In this topic, the slowdown of Kernel was healed by copying the data from the device to the host and vice versa with the reset of GPU after some number of iterations. I tried device_reset! in Julia but there was no any effect on performance. Also, if i run the code consecutively multiple times with 1000 iterations, the Kernel slow down is appeared

Kernel launches are asynchronous, this works by enqueuing the kernel in some sort of command queue structure. That buffer isn’t unbounded, so at some point you have too many pending asynchronous launches, resulting in the launch becoming β€œsynchronous” (or at least waiting for a new entry in the command queue structure). That is why after a number of iteration, the performance characteristics change. If you sync across every iteration, you ensure the queue never fills up completely, without that affecting the performance characteristics of the application as a whole.

1 Like

Many thanks for the explanation. But is there a way to fix this performance issue? I wonder why the language developed for high performance scientific computing has this type of problem since, for example, computational physics tasks are mostly iterative.

In which sense did this solve the slowdown? Such a copy will force synchronisation, but it should make things slower. Note for example that the code for 1000 iterations with CUDA.@sync runs slower than without it. Adding it just levels the playing field with 10000 iterations, so to speak.

You could launch fewer, but larger kernels, i.e. group multiple iterations in a single kernel by looping inside of it.

Note that this also applies to CPU code.

CPU example
using .Threads, ChunkSplitters, BenchmarkTools

function foo!(C, A, B)  # length(C) small tasks
    tasks = map(eachindex(C)) do i
        @spawn C[i] = A[i] + B[i]
    end
    wait.(tasks)
end

function foo2!(C, A, B)  # nthreads() big tasks
    tasks = map(chunks(eachindex(C), n=nthreads())) do is
        @spawn begin
            for i in is
                C[i] = A[i] + B[i]
            end
        end
    end
    wait.(tasks)
end

A = rand(1000); B = rand(1000); C = similar(A);
display(@benchmark foo!($C, $A, $B))   # median: 330.900 ΞΌs
display(@benchmark foo2!($C, $A, $B))  # median: 6.829 ΞΌs

A = rand(10000); B = rand(10000); C = similar(A);
display(@benchmark foo!($C, $A, $B))   # median: 3.560 ms
display(@benchmark foo2!($C, $A, $B))  # median: 7.883 ΞΌs 

So foo! becomes 10x slower, while foo2! sees only a mild increase. I would interpret this as the task scheduling incurring a significant overhead over the actual (trivial) calculations (as is also evident from comparing foo! and foo2! at the same data length, of course).

By the way, I would consider the term β€˜iterations’ in your example somewhat of a misnomer, as in my eyes iterations should be run sequentially, which is not the case here: the output F is not used as input for a later iteration.

You said before that C/C++ CUDA had the same issue, so you should probably blame it on GPU architectures instead of Julia. :slight_smile:

As stated in cuda kernal slows down after some iterations - #2 by little_jimmy - CUDA Programming and Performance - NVIDIA Developer Forums

I fix the problem by copying GPU data to host and do gpu device reset after 1000 iterations, then move data back to GPU and continue with iteration 1001. Maybe GPU gets β€œtired” after large iteration and it needs reset. My method involved extra copy, but still faster than not using reset.

I forgot to mention that in the CUDA/C++ case, it was more likely hardware problem since another GPU worked fine. In my case, it is most likely software problem since i tried three different GPU (GTX 960, GTX 1060 and Tesla T4)

You could launch fewer, but larger kernels, i.e. group multiple iterations in a single kernel by looping inside of it.

I thought about it too. But i don’t think that i can implement it in my main program where a huge amount of linear algebra operations are performed. Also one iteration in my main program (without a custom kernel) is performed in no time. The slowdown is increased with iterations as in the present example.

Note that this also applies to CPU code.

CPU code works fine. No slowdown was observed in one of my main programs

By the way, I would consider the term β€˜iterations’ in your example somewhat of a misnomer, as in my eyes iterations should be run sequentially, which is not the case here: the output F is not used as input for a later iteration.

Yes, you are right. I just gave a simple example describing my problem.