Benchmarking and threads

In the bench workspace, I have this function:

function kaftorTime(textLen::Integer,keyLen::Integer) # in nanoseconds
  text=fill(0x69,textLen)
  key=fill(0x96,keyLen)
  trial=@benchmark kaftorEncrypt!($text,$key)
  median(trial).time
end

I have two Julias running, one julia --project -t auto (which is 12) and one julia --project -t 1. (This is on my old tower; my new laptop has 32 threads and is significantly faster.) With 12 threads:

julia> kaftorTime(59049,32)/59049
637.9849531744823

With 1 thread:

julia> kaftorTime(59049,32)/59049
337.4873240867754

I don’t see how that can happen unless @bench is measuring total CPU time on all cores.

I’d like to tune the parallelized parts of the algorithm, with the number of threads depending on the message size, to minimize the elapsed time without greatly increasing the task switching overhead. Can I measure both elapsed time and total time of all threads? Also, is there a way to specify the number of threads that @threads uses?

Did you try with 2, 3, 4 … threads?

Most CPUs now have fast and slow cores. Using the slow cores is often useless and can slow things down, depending on your algorithm.

Threads can slow things down by:

  • causing more GC pressure
  • causing communication/ coordination overhead

The tower has only one kind of core. The laptop has two.

The algorithm does three things per round: jumble!, shufflePairs!, and mix3PartsSeq!; all of which operate on a vector in place, so there shouldn’t be any garbage collection. jumble! is already parallelized, with a worker function, so I can easily change the number of threads. shufflePairs! is parallelized with @threads; this produced a slight speedup. Replacing mix3PartsSeq! with mix3PartsPar! slowed it down.

Profiling it results in most of the time spent in task_done_hook, which doesn’t help me see what’s going on.

There are a lot of ways this can happen.

If your program is abusing the Task system and spawns too many short-running tasks, then you will see this behavior: When single-threaded, the entire task-switching / scheduling apparatus is faster.

You can also see this if your parallelization sucks, i.e. if you have lots of contention, irregardless of whether false sharing (different cores access different parts of the same cache-line) or true sharing (e.g. locks / contested atomics or data races). In such cases single-threaded is much faster.