TornadoVM provides two source-level annotations that let you express GPU parallelism by decorating ordinary JavaDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/Deepak-Sangle/TornadoVM/llms.txt
Use this file to discover all available pages before exploring further.
for loops, without writing a single line of OpenCL or CUDA. The @Parallel annotation marks a loop iteration variable as a parallel dimension — the TornadoVM JIT compiler replaces the loop with a GPU thread launch, using the loop bounds to determine the total thread count. The @Reduce annotation marks an accumulator variable as the output of a parallel reduction — the compiler inserts the necessary tree-reduction hardware instructions automatically. Both annotations live in the package uk.ac.manchester.tornado.api.annotations and are retained at runtime so the JIT compiler can inspect them via reflection during code generation.
Annotations and
KernelContext are mutually exclusive programming models. Do not mix @Parallel loop annotations with KernelContext thread-ID reads inside the same task method. If you need explicit thread IDs, use KernelContext exclusively and omit all annotations from that method.@Parallel
@Parallel targets the loop iteration variable (ElementType.LOCAL_VARIABLE) of a simple counted for loop. At JIT-compile time, TornadoVM replaces the loop body with a parallel GPU kernel where each thread handles one iteration independently.
Declaration
Annotation Properties
LOCAL_VARIABLE, TYPE, TYPE_USE, TYPE_PARAMETER — applied to the loop iteration variable declared in the for initializer.RUNTIME — the annotation is retained in bytecode so the TornadoVM JIT compiler can inspect it via reflection during code generation.How TornadoVM uses it
When the compiler encounters afor loop whose iteration variable is annotated with @Parallel, it:
- Uses the loop’s upper bound as the global work size for that dimension.
- Replaces the sequential iteration with a GPU thread index read (
get_global_id(n)in OpenCL;blockIdx.x * blockDim.x + threadIdx.xin CUDA). - Strips the loop structure entirely from the generated kernel — each GPU thread executes the body exactly once.
Restrictions
- The loop must be a simple counted
forloop with a constant or array-length upper bound. - The iteration variable must be of type
intorlong. - The loop increment must be
+1(i.e.,i++ori += 1). - No
break,continue, or non-loop-boundreturninside the annotated loop. - Nesting up to three
@Parallelloops is supported for 1D, 2D, and 3D parallelism.
Usage Parameter
The annotation is placed immediately before the type in the
for loop initializer. The variable type must be int or long. TornadoVM uses the loop’s upper bound expression as the global work size for the corresponding GPU dimension.1D Parallel Example
add into a 1D kernel with a.length threads. Each thread computes a single element of c.
2D Parallel Example
Two nested@Parallel annotations map to a 2D GPU thread grid:
@Parallel loop maps to globalIdy (Y dimension); the inner loop maps to globalIdx (X dimension). The global work size becomes M × N.
3D Parallel Example
@Reduce
@Reduce marks an accumulator variable (parameter, local variable, or field) as the output of a parallel reduction. TornadoVM inspects the loop body to infer the reduction operation (sum, max, or min) from the binary operator applied to the accumulator, then generates a hardware-efficient tree-reduction kernel.
Declaration
Annotation Properties
PARAMETER, LOCAL_VARIABLE, FIELD — applied to the accumulator variable, which must be a single-element primitive array parameter.RUNTIME — retained in bytecode so the JIT compiler can detect and specialise the reduction pattern.How TornadoVM uses it
When the compiler detects@Reduce on an accumulator:
- It generates a two-phase reduction: a parallel phase where each thread reduces a sub-range of the input, followed by a serial phase on the host that combines the per-thread partial results.
- The reduction operation is inferred from the update expression in the loop body (
+=→ sum,= Math.max(…)→ max,= Math.min(…)→ min). - The partial result array written by the kernel has a size determined by the number of thread blocks; the host phase is transparent to the user.
Usage Parameter
Applied to a single-element primitive array that serves as the reduction output. The array must be passed as a method parameter (not a local variable) so TornadoVM can size the per-thread partial-result buffer correctly.
Supported Reduction Operations
Restrictions and Limitations
- Only one
@Reduceaccumulator is allowed per task method in the current TornadoVM release. - The accumulator must be a primitive scalar (
int,long,float,double) held in a single-element array (e.g.,float[] sum = { 0.0f }) passed as a parameter. This enables the JIT to allocate the per-thread partial-results buffer at the correct size. - The reduction loop must also be annotated with
@Parallel; the two annotations work together. - Mixed reduction types (e.g., computing both sum and max in the same loop) are not supported in a single task.
Reduction Examples
Annotations vs. KernelContext — When to Use Which
Use @Parallel / @Reduce when...
- Your kernel is a simple element-wise map or reduction
- You want minimal boilerplate and automatic thread-count derivation
- You do not need explicit control over workgroup size or local memory
- You are porting existing sequential Java code incrementally
Use KernelContext when...
- You need explicit thread IDs (
globalIdx,localIdx, etc.) - You want to allocate and use shared/local memory for tiling
- You require barriers, atomics, or warp-level SIMD intrinsics
- You are targeting Tensor Core MMA operations
- You need precise control over workgroup dimensions via
GridScheduler
Task Graph
Register
@Parallel and @Reduce tasks in a TaskGraph.Kernel Context
Explore the explicit thread-programming alternative to annotations.