Remote Variable Declarations

Warning

This work is under active development. As such, the interface and behavior of remote variables is unstable and expected to change.

Introduction

Remote variable declarations make it possible to store a particular variable on locale different from the one currently executing the code. Unlike plain variable declarations in tandem with the on statement, remote variables do not introduce a new scope. Specifically, consider the following program:

on someOtherLocale {
  var myVar = 42;
}
writeln(myVar); // Error: myVar is not in scope

In the above program an on block is used to declare a variable on a different locale. However, because the on block creates a new scope, this variable is not visible to the writeln statement. The most straightforward way to return execution to the original locale is to use a second, nested on block:

on someOtherLocale {
  var myVar = 42;
  on originalLocale {
    writeln(myVar);
  }
}

Remote variable declarations avoid this problem by allowing variables on different locales to be declared without creating a new scope.

on someOtherLocale var myVar = 42;
writeln(myVar); // Output: 42

Syntax and Semantics

Remote variable declarations are created by prefixing a regular variable declaration with on followed by the target locale. Declarations with only a type, only an initializer, or both are supported:

on loc var myVar1 = 42;
on loc var myVar2: 42;
on loc var myVar3: int = 42;

Remote variables are stored in the heap memory of the target locales. Currently, only var and const declarations are allowed. The semantics of the type-only form are the same as for the type-only form of regular variable declarations: the type’s default value is used.

The intended semantics of the remote variable declaration is to act similarly to two nested on blocks, with the exception of the scope changes. Thus, the following code:

on loc var myVar = 42;
doSomething(myVar);

Should behave roughly the same as:

on loc {
  var myVar = 42;
  on originalLocale {
    doSomething(myVar);
  }
}

Array types and promotion are supported, making the following code valid:

on loc var A: [1..10] int;
on loc var B = A + 1;

A useful pattern with Chapel’s GPU Support is to declare an array on the GPU using remote variable declarations, and copy a portion of a host array into it:

on here.gpus[0] var GpuA = A[GpuPortion];

Since the initialization expression is executed on the target locale, if the array is initialized with a GPU-eligible expression, this expression is transformed into a GPU kernel as it would be in an on block. Thus, the following line of code generates a kernel launch:

on here.gpus[0] var B = foreach i in 1..128 do i*i;

As with on blocks, only GPU-eligible loops are transformed into GPU kernels. To assert that the initializer of a GPU-bound remote variable is transformed into a GPU kernel, you can use the @assertOnGpu attribute:

@assertOnGpu
on here.gpus[0] var B = foreach i in 1..128 do i*i;

Implementation Details

As mentioned above, remote variables are declared in the heap memory of the target locale. This is done by desugaring the remote variable to the allocation of class-based Chapel wrapper. The following statement:

on loc var myVar = 42;

Is desugared to something like the following:

class _remoteVarContainer {
  var containedValue;
}

record _remoteVarWrapper {
  type eltType;
  var tmp: owned _remoteVarContainer(eltType);

  proc ref get() ref {
    return tmp.containedValue;
  }
}

var wrapped_myVar = chpl__buildRemoteVarWrapper(loc, 42);
ref myVar = wrapped_myVar.get();

When building the remote wrapper, the _remoteVarContainer is allocated on the heap in the target locale; since myVar becomes a reference to a part of that container, it also lives on the target locale.

Limitations and Future Work

Remote variable declarations generally work as expected, and support a variety of input types. However, there are still some limitations that are considered future work:

  • The heap-based container for the remote variable is always created, even when the variable is itself a heap-allocated class. This introduces an additional level of indirection in certain cases.

  • Because of the desugaring described in the Implementation Details section, and because records and classes today don’t support ref fields, remote variables cannot be used as fields of records or classes.