Ordered derivative extraction with cire-mode

A large stencil computation can require enough registers to limit GPU occupancy. Computing selected finite-difference expressions in separate stages can reduce that pressure and reuse repeated expressions. The tradeoff is extra temporary storage, memory traffic, and kernel launches. cire-mode lets you tune this decomposition for your computation and backend.

cire-mode controls which finite-difference expressions DevitoPRO considers for computation into temporaries. A comma-separated string requests extraction passes in the order written:

op = Operator(equations, opt=('advanced', {
    'expand': False,
    'cire-mode': 'x|y|z:stag,y|z:center,x|y|z:interp'
}))

Each comma-separated entry requests exactly one extraction pass. The example requests three passes on each original computation:

  1. Select staggered derivatives along x, y, or z.
  2. Select centered derivatives along y or z in the remaining expressions.
  3. Select interpolations along x, y, or z in the remaining expressions.

The selector path operates on three-dimensional computations with unexpanded finite-difference expressions (IndexDerivatives). On backends that expand derivatives by default, use expand=False as above. This requires the OSS Devito support for IndexDerivative.staggering.

Selecting stencils

A selector consists of entries separated by |. Within an entry, x, y, and z name axes. A positive integer after an axis specifies differential order:

Selector Candidates
x First derivatives and interpolations along x
x2 Second derivatives along x
x|y|z Separate first derivatives and interpolations along any axis
xy A nested mixed stencil along x and y
xy2 A first derivative along x nested with a second derivative along y

xy and yx denote the same mixed selector. Entries and atoms are canonicalized; their written order does not prescribe extraction order. Commas prescribe pass order. For example, x|y requests one pass with two alternatives, whereas x,y requests two passes.

Append a kind after the complete selector to restrict its candidates:

Suffix Required metadata
:stag Positive differential order and staggering == 'staggered'
:center Positive differential order and staggering == 'centered'
:interp Differential order zero

Staggering describes the evaluation location relative to the input lattice. centered means on that lattice, and staggered means halfway between its points. This is independent of stencil bias: a centered evaluation does not imply symmetric coefficients. It also differs from the absolute staggering of the input Function and from byte alignment.

Interpolation accuracy is independent of differential order. An IO16 interpolation has differential order zero and matches :interp. x16 instead selects a sixteenth derivative; it does not specify interpolation accuracy.

An interpolation can match :interp even when its staggering is unknown. Unknown differential order cannot match a typed selector, and unknown staggering cannot match :stag or :center. Untyped selectors retain their broader matching behavior.

For mixed selectors, every nested stencil must satisfy the kind. Thus xy:interp can select a bilinear interpolation, while xy:stag requires positive-order, staggered derivatives on both axes. A mixed derivative/interpolation nest needs separate passes to extract its different kinds.

How passes proceed

Each pass sees the consumer left by its predecessors. Its producer computations are retained and are not revisited by later passes. For example, suppose the consumer contains this stencil nest:

I_z(D_y_centered(D_x_staggered(u)))

The three-pass example above can extract:

r0 = D_x_staggered(u)
r1 = D_y_centered(r0)
r2 = I_z(r1)
consumer = ... r2 ...

Reversing the passes can produce a different decomposition. Initially the outer interpolation contains derivatives of other kinds, so it does not match a pure interpolation selector. Extracting the inner derivative makes further candidates available to later passes. An earlier pass is never retried because a later pass exposed a new candidate.

A pass that finds no eligible candidates, or produces no aliases, leaves the consumer available to the next pass. For example, x:stag,y:center still tries the centered pass when the staggered pass extracts nothing. It does not fall back to extracting expressions outside the selector.

Derivative flipping, when enabled, uses the first configured pass to choose its initial axis priority. A standalone 1pass uses the canonical-ordering heuristic. Flipping runs before extraction and can change which nests are available; it does not run again between passes. Set gpu-flip-derivs=False when comparing extraction from a fixed nesting order.

The cost model can choose between individual derivatives and eligible sums of derivatives, including sums inside a stencil such as (u.dx + v.dy).dz and sums multiplied by coefficients such as c*(u.dx + v.dy). A compound candidate must contain only derivatives selected by that pass. Reuse elsewhere can make separate temporaries cheaper than one temporary for a sum. CIRE also considers combining only the sums whose derivatives need no separate temporaries, so a sum with reuse elsewhere does not prevent other sums from being combined.

A pass count is not a kernel count. A pass can produce several temporaries, produce none, or leave multiple computations for later scheduling. Subsequent compiler passes also determine the final kernel boundaries. Inspect the generated code before assigning tuning options that depend on final kernel numbering.

Passes versus tuple entries

A tuple assigns separate mode entries to successive original Clusters, which are the compiler’s approximation of the computations being split. All the passes in one string belong to that original Cluster:

'cire-mode': (
    '1pass',
    '1pass',
    'x|y|z:stag,y|z:center,x|y|z:interp'
)

Here the first two original Clusters use 1pass. The third uses the ordered three-pass sequence. As with other per-computation options, the final tuple entry is reused for any remaining original Clusters. Newly generated producers do not consume tuple entries.

The two 1pass tuple entries apply to two separate computations, each unsplit. They do not request two passes within one computation. In a single mode string, '1pass,1pass' is invalid.

In contrast, this tuple requests one pass per original Cluster:

'cire-mode': ('x|y|z:stag', 'y|z:center', 'x|y|z:interp')

Repetition, presets, and validation

Repeat a selector explicitly when successive passes should have the same filter:

'cire-mode': 'x|y|z:stag,x|y|z:interp,x|y|z:interp'

This requests three passes: one staggered-derivative extraction and two successive interpolation extractions. Every written entry is visited, including repeated entries after a pass that extracts nothing. There is no separate repetition count. The former (mode):N syntax is no longer accepted; write each pass explicitly.

1pass requests the unsplit formulation for one original computation. It must be that computation’s complete mode: 1pass,1pass, 1pass,x:stag, and x:stag,1pass are all rejected at construction. It cannot serve as an empty pass inside an extraction sequence. This also applies to legacy threshold forms such as 1pass:200 when combined with other passes.

Other presets, such as any and automatic:200, can appear in pass sequences. automatic:200 uses the existing register threshold. Automatic mode remains unsupported on CPU backends, including when it appears inside a sequence.

The numeric suffix in automatic:200 is a threshold, not a pass count. Existing numeric selector suffixes retain their legacy threshold interpretation where accepted; use explicit comma-separated passes for repetition.

Whitespace around passes is ignored. Empty passes, unknown kind suffixes, and parenthesized repetition are invalid. For example, x:stag,,y:interp, x:staggered, and (x:interp):2 are rejected. The supported kind spellings are exactly stag, center, and interp.

The runnable regression examples are in TestDerivSelector, including kind filtering, compound candidates, and tuples of pass sequences. The test_cire_* tests in Test2PassAdvanced check generated kernel dependencies and numerical results for ordered passes, kind filtering, and whole or staged mixed interpolation, including derivative flipping.

Back to top