Closures and Capturing - Part 1

The operator call() operator is used to invoke a C++ function with Intel® ArBB code.

To execute a function using arbb::call(), use the following syntax:

arbb::call(function)(argument1, argument2/*, more arguments */);

This expression contains two sets of parentheses because arbb::call() can only take one parameter, that is the function being called. The second set of parentheses containing the arguments passed to the function is actually an invocation of the call operator ( operator()() ) on the object returned by arbb::call(). The return type of arbb::call() is an instance of the arbb::closure class template. 

 A call()expression works like this:

  • If it's never seen the function passed in before, it captures the function into a closure, then executes it.
  • Otherwise it executes the previously captured closure.

Closures are functions introduced to Intel® Array Building Blocks (Intel® ArBB) by a process called closure capture, or capture for short. Once captured, a closure can be re-executed many times without incurring any compilation overhead.

As closures are lightweight references to the actual object code corresponding to a closure capture, copying them around does not affect performance, allowing arbb::call() to be used in inner loops. 

Take a look at this illustrative example

The first half of the expression above captures/retrieves the closure. The second part executes it. Capturing simply executes the function. Any operations on Intel ArBB types will be captured. Any non-ArBB C++ operations just execute immediately.

The function capture() explicitly captures a given function:

  • call() only captures a function the first time it sees it.
  • capture() re-executes a function every time.

These facts are best demonstrated by the example below. Let's walkthrough this code.



  1. Starting the first capture.


  2. Inside a capture.


  3. Capturing Intel ArBB operations.


  4. Starting the first call.


  5. Capturing due to call. Because my_function was previously captured using the explicit capture() rather than the "capture if we haven't seen this function before" provided by call(), the call results in an entirely new closure.


  6. Capturing Intel ArBB operations.


  7. Second call - no capture. This time we've already done a call() to my_function, so call() doesn't do any capturing, it just retrieves the result. This is the final state.

    Once a closure has been compiled for an ordered set of arguments with particular attributes, the closure will not be recompiled for arguments with the same attributes. To avoid implicit dynamic recompilation as part of closure execution, use a closure's compile() member function.

    compile() internally prepares a closure to execute with given arguments, or arguments with the same attributes, so that implicit dynamic recompilation does not occur during execution. compile() can be called many times to prepare a closure for more than one combination of attributes. All compilation happens at run time so the generated code can be optimized for the exact machine configuration in use.

    template<typename FunctionType > void arbb::closure< FunctionType >::compile ( FunctionParams... arguments ) const

    - Compiles an internal version of the closure that accepts arguments with the same attributes as arguments.

    The attributes that affect compilation depend on the argument type and relate to how that argument was created as follows:

    - For any container argument, whether or not the argument was created using a non-null binding specification.
    - For two- and three-dimensional bound container arguments, whether or not the binding specification was created with byte_pitch values indicating data striding.

    This function may be called several times on the same closure for arguments with different attributes. Calling this function more than once for a given set of arguments has no effect. Executing the closure with the same set of arguments or different arguments with the same attributes results in no compilation overhead.


For more complete information about compiler optimizations, see our Optimization Notice.