Newshot
Account

Futhark-J Bridge

Original text(via Lobsters)

        ^ ^                 ^ ^
=(o.o)=  ~byakuren  =(o.o)=
  m m                 m m
      

Futhark-J Bridge

High-performance parallel array computing for J via Futhark

Write Futhark code inline in your J programs and get automatic parallel execution on all CPU cores.

Download: futhark.ijs (v1.7.0)

What is this?

The Futhark-J Bridge lets you write Futhark code directly in your J source files. Futhark is a purely functional data-parallel programming language that compiles to highly efficient parallel code. This bridge handles compilation, caching, and FFI calls automatically.


        NB. Define a Futhark function inline
double =: 'entry f (xs: []f64) : []f64 = map (*2) xs' fut

NB. Use it like any J verb
double 1 2 3 4 5
   2 4 6 8 10
      

Why use Futhark from J?

J is already excellent at array programming, but some workloads benefit from parallel execution across multiple CPU cores. Futhark excels at:

  • Compute-intensive operations - When you have many FLOPs per element
  • Large arrays - Parallel overhead is amortized over millions of elements
  • Nested parallelism - Operations that J executes sequentially

Performance Results

The library includes benchmarks comparing native J to Futhark. Here are some highlights:

WorkloadMax SpeedupNotes
Mandelbrot escape1525xVariable iteration count per element
Logistic map (1000 iters)14xHigh compute per element
Standard deviation2xMultiple passes over data
Dot product0.3xMemory-bound; J wins

Key insight: Futhark shines when computation dominates memory access. For simple operations like dot product, native J is faster due to FFI overhead.

Quick Start


        NB. Load the library
load 'futhark.ijs'

NB. Array transformations
square =: 'entry f (xs: []f64) : []f64 = map (\x -> x * x) xs' fut
square 1 2 3 4 5
   1 4 9 16 25

NB. Reductions to scalar
total =: 'entry f (xs: []f64) : f64 = reduce (+) 0 xs' fut
total 1 2 3 4 5
   15

NB. Two-input (dyadic) functions
dot =: 'entry f (xs: []f64) (ys: []f64) : f64 = reduce (+) 0 (map2 (*) xs ys)' fut
(1 2 3) dot (4 5 6)
   32

NB. Scalar input functions (v1.6.0) - use 0 : 0 for multiline Futhark
factors_src =: 0 : 0
  entry f (n: i64) : []i64 =
    let (_, _, result) =
      loop (x, i, acc) = (n, 2i64, ([] : []i64))
      while x > 1 do
        if x % i == 0
        then (x / i, i, acc ++ [i])
        else (x, i + 1, acc)
    in result
)
factors =: factors_src fut

factors 12
   2 2 3
factors 100
   2 2 5 5

NB. Fixed-size array dimensions (v1.7.0)
NB. Output has fixed inner dimension [3]
triple =: 'entry f (ns: []i64) : [][3]i64 = map (\n -> [n, n*2, n*3]) ns' fut
triple 1 2 3 4 5
   1  2  3
   2  4  6
   3  6  9
   4  8 12
   5 10 15
      

Features

  • Inline Futhark code - Write Futhark as string literals or multiline 0 : 0 blocks
  • Automatic compilation - First use compiles; subsequent uses are cached
  • Persistent context - Futhark context created once, reused for all calls
  • Content-based caching - SHA-256 hash ensures identical code shares libraries
  • Scalar and array inputs - Functions can take scalar or array inputs (v1.6.0)
  • Fixed-size dimensions - Support for [][32]i64 style types (v1.7.0)
  • Multi-dimensional arrays - 1D, 2D, 3D arrays all work seamlessly
  • Dynamic output sizes - Output can differ in shape from input
  • Helper functions - Use def to define reusable helper functions (v1.7.0)
  • Dyadic verbs - Two-input functions work naturally as x verb y
  • Clear error messages - Full Futhark compiler output on failures (v1.6.0)

Futhark Syntax Primer

Futhark entry points follow this pattern:


        entry <name> (<param>: <type>) : <return_type> = <body>
      

Common operations:

FutharkJ EquivalentDescription
map (*2) xs2 * xsElement-wise transform
reduce (+) 0 xs+/ xsFold to scalar
scan (+) 0 xs+/\ xsRunning sum
map2 (*) xs ysxs * ysElement-wise binary op
transpose xs\│: xsMatrix transpose
flatten xs, xsRavel to 1D

Requirements

  • J 9.x with FFI support
  • Futhark compiler (install guide)
  • GCC for compiling generated C code
  • POSIX system: Linux, FreeBSD, or macOS

How It Works

  • Hash - Source code is hashed with SHA-256
  • Cache check - Return cached .so if exists
  • Compile - futhark multicore --library generates C code
  • Build - GCC compiles to shared library
  • Initialize - Futhark context created with thread pool
  • Execute - J calls library via FFI, passes arrays, retrieves results

The context is created at definition time and persists, so there’s no “warmup” penalty on first execution.

Files

FileDescription
futhark.ijsMain library - load this
futhark_test.ijsTest suite (123 tests)
futhark_demo.ijsInteractive demonstration
compute_bench.ijsCompute-intensive benchmarks
stddev_bench.ijsStandard deviation benchmark
dot_product_bench.ijsDot product benchmark
README.mdUser documentation
DOCUMENTATION.mdTechnical deep-dive

Sample Outputs

  • Test suite output - 123/123 tests passing
  • Demo output - Side-by-side J vs Futhark comparison
  • Compute benchmark - Logistic map, complex math, Mandelbrot
  • StdDev benchmark - Standard deviation performance
  • Dot product benchmark - Memory-bound operation

Example: Statistical Functions


        NB. Variance
f_variance =: 'entry f (xs: []f64) : f64 = let mean = reduce (+) 0 xs / f64.i64 (length xs) in let sq_diffs = map (\x -> (x - mean) ** 2) xs in reduce (+) 0 sq_diffs / f64.i64 (length xs)' fut

NB. Standard deviation
f_stddev =: 'entry f (xs: []f64) : f64 = let mean = reduce (+) 0 xs / f64.i64 (length xs) in let sq_diffs = map (\x -> (x - mean) ** 2) xs in f64.sqrt (reduce (+) 0 sq_diffs / f64.i64 (length xs))' fut

NB. Softmax
f_softmax =: 'entry f (xs: []f64) : []f64 = let max_val = reduce f64.max f64.lowest xs in let exps = map (\x -> f64.exp (x - max_val)) xs in let sum_exps = reduce (+) 0 exps in map (\e -> e / sum_exps) exps' fut

NB. Z-score normalization
f_zscore =: 'entry f (xs: []f64) : []f64 = let n = length xs in let mean = reduce (+) 0 xs / f64.i64 n in let sq_diffs = map (\x -> (x - mean) ** 2) xs in let std = f64.sqrt (reduce (+) 0 sq_diffs / f64.i64 n) in if std == 0.0 then replicate n 0.0 else map (\x -> (x - mean) / std) xs' fut
      

Example: Chaining J and Futhark

Data flows seamlessly between J and Futhark:


        input =: 100 200 300 400 500
   100 200 300 400 500

step1 =: f_normalize input      NB. Futhark
   0 0.25 0.5 0.75 1

step2 =: 100 * step1            NB. J
   0 25 50 75 100

step3 =: f_square step2         NB. Futhark
   0 625 2500 5625 10000

step4 =: <. step3               NB. J floor
   0 625 2500 5625 10000

step5 =: f_cumsum step4         NB. Futhark
   0 625 3125 8750 18750
      

Version History

VersionChanges
1.7.0Fixed-size array dimensions ( [][32]i64 ), correct parsing with def helper functions, 123 tests
1.6.0Scalar input support, improved error reporting, 96 tests
1.5.0SHA-256 hashing (128!:6) for cache keys
1.4.0Persistent Futhark context (no warmup needed)
1.3.0Dyadic verb support (two inputs)
1.2.0Dynamic n-dimensional array support
1.1.02D array support
1.0.0Initial release

License

Links

  • Futhark Language
  • J Programming Language
  • Futhark User’s Guide

The text above was extracted automatically from the source page and may miss images, tables or code blocks. The original is authoritative.

Published by Lobsters
This site only summarises and aggregates; copyright stays with the original author. Headlines and summaries follow the source — where they differ, the original wins.
Read the original ↗