Index Tasks

Regent provides a number of optimizations to ensure that tasks execute in parallel and with maximum efficiency. One of the most important of these is the index launch optimization. An index launch encapsulates a parallel loop and ensures that Regent is able to amortize the cost of analyzing the enclosed tasks. This is an important optimization that is used in nearly all production codes, and is critical for scaling most Regent programs to large numbers of nodes.

As with nearly all of Regent’s optimizations, this is applied automatically to Regent programs. Any loop that matches Regent’s criteria will be converted into an index launch, without any specific action by the user. However, it is still helpful for users to be aware of when their programs are being optimized, and what types of programs can generally be expected to work well in Regent. Regent also provides a feature, described below, to ensure that loops are being optimized the way that the user expects.

Optimizing Index Launches

Recall that tasks execute in parallel only if they do not interfere. Regent must expend effort analyzing each task to determine non-interference with respect to the other children of the same parent task. The index launch optimization amortizes this analysis over a loop of task calls, increasing efficiency when the number of tasks is large.

The following code calls a task double_of in a loop, and accumulates the results into the variable total. This loop will be index launched by Regent.

var total = 0
__demand(__index_launch)
for i = 0, num_points do
  total += double_of(i, i + 10)
end

The annotation __demand(__index_launch) ensures that Regent performs the index launch as expected. Note that this is primarily a defensive programming feature. Regent will optimize this code, regardless of whether the user adds the annotation or not. The effect of the annotation is not to “force” Regent to optimize the code (Regent will refuse to do so if it is not safe), but instead to produce an error if the optimization is not performed. This can help, especially in large codebases, to ensure that important parts of the program are not deoptimized unexpectedly. It is considered good code hygiene to mark loops with __demand(__index_launch) because it helps users confirm their understanding of how the code will behave. This also helps to improve the error messages generated by Regent, as the compiler can point to specifically those parts of the program (if any) that inhibit optimization.

(This style of annotation is in particular contrast to an OpenMP-style #pragma, which applies the optimization even if it is unsound.)

Final Code

import "regent"

local format = require("std/format")

task double_of(i : int, x : int)
  format.println("Hello world from task {}", i)
  return 2*x
end

task main()
  var num_points = 4

  var total = 0
  __demand(__index_launch)
  for i = 0, num_points do
    total += double_of(i, i + 10)
  end
  regentlib.assert(total == 92, "check failed")
end
regentlib.start(main)

Next Up

Continue to the next tutorial to read about some features that are not allowed in Regent.