Multithreaded Task Partitioning
AcceleratedKernels.TaskPartitioner — Type
Partitioning num_elems elements / jobs over maximum max_tasks tasks with minimum min_elems elements per task.
Methods
TaskPartitioner(num_elems, max_tasks=Threads.nthreads(), min_elems=1)Base.getindex(tp::TaskPartitioner, itask::Integer)Base.firstindex(tp::TaskPartitioner)Base.lastindex(tp::TaskPartitioner)Base.length(tp::TaskPartitioner)Fields
num_elems::Int: (user-defined) number of elements / jobs to partition.max_tasks::Int: (user-defined) maximum number of tasks to use.min_elems::Int: (user-defined) minimum number of elements per task.num_tasks::Int: (computed) number of tasks actually needed.task_istarts::Vector{Int}: (computed) element starting index for each task.tasks::Vector{Task}: (computed) array of tasks; can be reused.
Examples
using AcceleratedKernels: TaskPartitioner# Divide 10 elements between 4 taskstp = TaskPartitioner(10, 4)for i in 1:tp.num_tasks @show tp[i]end# outputtp[i] = 1:3tp[i] = 4:6tp[i] = 7:8tp[i] = 9:10using AcceleratedKernels: TaskPartitioner# Divide 20 elements between 6 tasks with minimum 5 elements per task.# Not all tasks will be requiredtp = TaskPartitioner(20, 6, 5)for i in 1:tp.num_tasks @show tp[i]end# outputtp[i] = 1:5tp[i] = 6:10tp[i] = 11:15tp[i] = 16:20The TaskPartitioner is used internally by task_partition and itask_partition; you can construct one manually if you want to reuse the same partitioning for multiple tasks - this also reuses the tasks array and minimises allocations.
AcceleratedKernels.task_partition — Function
task_partition(f, num_elems, max_tasks=Threads.nthreads(), min_elems=1)
task_partition(f, tp::TaskPartitioner)Partition num_elems jobs across at most max_tasks parallel tasks with at least min_elems per task, calling f(start_index:end_index), where the indices are between 1 and num_elems.
Examples
A toy example showing outputs:
num_elems = 4task_partition(println, num_elems)# Output, possibly in a different order due to threading order1:14:42:23:3This function is probably most useful with a do-block, e.g.:
task_partition(4) do irange some_long_computation(param1, param2, irange)endThe TaskPartitioner form allows you to reuse the same partitioning for multiple tasks - this also reuses the tasks array and minimises allocations:
tp = TaskPartitioner(4)task_partition(tp) do irange some_long_computation(param1, param2, irange)end# Reuse same partitioning and tasks arraytask_partition(tp) do irange some_other_long_computation(param1, param2, irange)endAcceleratedKernels.itask_partition — Function
itask_partition(f, num_elems, max_tasks=Threads.nthreads(), min_elems=1)
itask_partition(f, tp::TaskPartitioner)Partition num_elems jobs across at most max_tasks parallel tasks with at least min_elems per task, calling f(itask, start_index:end_index), where the indices are between 1 and num_elems.
Examples
A toy example showing outputs:
num_elems = 4itask_partition(num_elems) do itask, irange @show (itask, irange)end# Output, possibly in a different order due to threading order(itask, irange) = (3, 3:3)(itask, irange) = (1, 1:1)(itask, irange) = (2, 2:2)(itask, irange) = (4, 4:4)This function is probably most useful with a do-block, e.g.:
itask_partition(4) do itask, irange some_long_computation_needing_itask(param1, param2, irange)endThe TaskPartitioner form allows you to reuse the same partitioning for multiple tasks - this also reuses the tasks array and minimises allocations:
tp = TaskPartitioner(4)itask_partition(tp) do itask, irange some_long_computation_needing_itask(param1, param2, irange)end# Reuse same partitioning and tasks arrayitask_partition(tp) do itask, irange some_other_long_computation_needing_itask(param1, param2, irange)end