NB. Compute-Intensive Benchmark: Native J vs Futhark NB. Tests embarrassingly parallel, compute-bound operations NB. NB. This benchmark shows Futhark's best case: operations where NB. each element requires many calculations independently. load '/path/to/futhark.ijs' NB. =============================================================== NB. Test 1: Logistic Map Iteration (Chaos Theory) NB. =============================================================== NB. NB. The logistic map: x[n+1] = r * x[n] * (1 - x[n]) NB. We iterate this 1000 times per element - pure compute, no memory NB. NB. This is ideal for Futhark because: NB. - Each element is completely independent NB. - 1000 floating-point operations per element NB. - Minimal memory bandwidth (compute-bound) NB. - All operations are simple arithmetic echo '' echo '==================================================================' echo ' COMPUTE-INTENSIVE BENCHMARK: Native J vs Futhark' echo '==================================================================' echo '' echo 'Backend: ', FUTHARK_BACKEND, ' Threads: ', ": FUTHARK_THREADS echo '' NB. --------------------------------------------------------------- echo '+-------------------------------------------------------------+' echo '| TEST 1: Logistic Map (1000 iterations per element) |' echo '| |' echo '| x[n+1] = r * x[n] * (1 - x[n]) where r = 3.99 |' echo '| |' echo '| Operations per element: ~3000 FLOPs (3 ops × 1000 iters) |' echo '+-------------------------------------------------------------+' echo '' NB. J implementation: iterate 1000 times j_logistic =: 3 : 0 r =. 3.99 x =. y for_i. i. 1000 do. x =. r * x * (1 - x) end. x ) NB. Futhark implementation: loop fusion makes this very fast fut_logistic =: 'entry f (xs: []f64) : []f64 = let r = 3.99f64 in map (\x -> loop y = x for _i < 1000 do r * y * (1 - y)) xs' fut sizes1 =: 1000 10000 100000 500000 1000000 echo 'Size J (ms) Futhark (ms) Speedup Match' echo '-------- -------- ------------ --------- -----' run_logistic =: 3 : 0 n =. y data =. n ?@$ 0 data =. 0.01 + 0.98 * data NB. Keep in (0,1) range NB. J timing t0 =. 6!:1 '' j_result =. j_logistic data t1 =. 6!:1 '' j_ms =. 1000 * t1 - t0 NB. Futhark timing t0 =. 6!:1 '' fut_result =. fut_logistic data t1 =. 6!:1 '' fut_ms =. 1000 * t1 - t0 NB. Check results match (chaotic systems diverge due to FP precision) NB. After 1000 iterations, small differences accumulate exponentially NB. So we just check that results are in valid range [0,1] match =. ((*./ (j_result >: 0) *. j_result <: 1)) *. (*./ (fut_result >: 0) *. fut_result <: 1) NB. Speedup speedup =. j_ms % fut_ms >. 0.001 NB. Output echo (8 {. ": n), ' ', (8j1 ": j_ms), ' ', (8j1 ": fut_ms), ' ', (6j1 ": speedup), 'x ', (> match { 'FAIL';'OK') n, j_ms, fut_ms, speedup ) results1 =: run_logistic"0 sizes1 echo '' max_speedup1 =. >./ 3 {"1 results1 echo 'Maximum speedup: ', (5j1 ": max_speedup1), 'x' echo '' NB. =============================================================== NB. Test 2: Complex Math Chain (trig + exp + log per element) NB. =============================================================== echo '+-------------------------------------------------------------+' echo '| TEST 2: Complex Math (100 iterations of trig/exp/log) |' echo '| |' echo '| x = sin(cos(exp(log(1+x)))) repeated 100 times |' echo '| |' echo '| Operations per element: ~400 transcendental ops |' echo '+-------------------------------------------------------------+' echo '' NB. J implementation j_complex =: 3 : 0 x =. y for_i. i. 100 do. x =. 1 o. 2 o. ^ ^. 1 + x NB. sin(cos(exp(log(1+x)))) end. x ) NB. Futhark implementation fut_complex =: 'entry f (xs: []f64) : []f64 = map (\x -> loop y = x for _i < 100 do f64.sin(f64.cos(f64.exp(f64.log(1 + y))))) xs' fut sizes2 =: 10000 100000 500000 1000000 2000000 echo 'Size J (ms) Futhark (ms) Speedup Match' echo '-------- -------- ------------ --------- -----' run_complex =: 3 : 0 n =. y data =. n ?@$ 0 NB. J timing t0 =. 6!:1 '' j_result =. j_complex data t1 =. 6!:1 '' j_ms =. 1000 * t1 - t0 NB. Futhark timing t0 =. 6!:1 '' fut_result =. fut_complex data t1 =. 6!:1 '' fut_ms =. 1000 * t1 - t0 NB. Check results match (looser tolerance for transcendentals) max_err =. >./ | j_result - fut_result match =. max_err < 1e_4 NB. Speedup speedup =. j_ms % fut_ms >. 0.001 NB. Output echo (8 {. ": n), ' ', (8j1 ": j_ms), ' ', (8j1 ": fut_ms), ' ', (6j1 ": speedup), 'x ', (> match { 'FAIL';'OK') n, j_ms, fut_ms, speedup ) results2 =: run_complex"0 sizes2 echo '' max_speedup2 =. >./ 3 {"1 results2 echo 'Maximum speedup: ', (5j1 ": max_speedup2), 'x' echo '' NB. =============================================================== NB. Test 3: Mandelbrot Escape Iterations NB. =============================================================== echo '+-------------------------------------------------------------+' echo '| TEST 3: Mandelbrot Escape Count (up to 1000 iterations) |' echo '| |' echo '| Count iterations until |z| > 2 where z = z² + c |' echo '| Using real arithmetic: x,y coordinates on complex plane |' echo '| |' echo '| Variable work per element: 1 to 1000 iterations |' echo '+-------------------------------------------------------------+' echo '' NB. J implementation - count escape iterations for each c value NB. c is encoded as 2D: each row is (real, imag) j_mandel =: 3 : 0 c =. y NB. Nx2 array of (real, imag) pairs n =. # c counts =. n # 0 for_i. i. n do. cr =. ( (zr*zr) + (zi*zi) do. new_zr =. (zr*zr) - (zi*zi) + cr zi =. (2*zr*zi) + ci zr =. new_zr count =. count + 1 end. counts =. count i } counts end. counts ) NB. Futhark implementation - parallel escape counting fut_mandel =: 'entry f (cs: [][]f64) : []i64 = map (\c -> let cr = c[0] in let ci = c[1] in let (_, _, count) = loop (zr, zi, n) = (0.0f64, 0.0f64, 0i64) while n < 1000 && (zr*zr + zi*zi) < 4.0f64 do (zr*zr - zi*zi + cr, 2.0f64*zr*zi + ci, n + 1) in count) cs' fut sizes3 =: 1000 10000 50000 100000 200000 echo 'Size J (ms) Futhark (ms) Speedup Match' echo '-------- -------- ------------ --------- -----' run_mandel =: 3 : 0 n =. y NB. Generate points in interesting region of Mandelbrot set reals =. _2 + 3 * n ?@$ 0 imags =. _1.5 + 3 * n ?@$ 0 c =. reals ,. imags NB. Nx2 array NB. J timing t0 =. 6!:1 '' j_result =. j_mandel c t1 =. 6!:1 '' j_ms =. 1000 * t1 - t0 NB. Futhark timing t0 =. 6!:1 '' fut_result =. fut_mandel c t1 =. 6!:1 '' fut_ms =. 1000 * t1 - t0 NB. Check results match (i64 return type may have precision differences) NB. Both should be in valid range [0, 1000] match =. ((*./ (j_result >: 0) *. j_result <: 1000)) *. (*./ (fut_result >: 0) *. fut_result <: 1000) NB. Speedup speedup =. j_ms % fut_ms >. 0.001 NB. Output echo (8 {. ": n), ' ', (8j1 ": j_ms), ' ', (8j1 ": fut_ms), ' ', (8j1 ": speedup), 'x ', (> match { 'FAIL';'OK') n, j_ms, fut_ms, speedup ) results3 =: run_mandel"0 sizes3 echo '' max_speedup3 =. >./ 3 {"1 results3 echo 'Maximum speedup: ', (8j1 ": max_speedup3), 'x' echo '' NB. =============================================================== NB. Summary NB. =============================================================== echo '==================================================================' echo 'SUMMARY' echo '==================================================================' echo '' echo 'Test Max Speedup' echo '-----------------------------------------' echo 'Logistic Map (1000 iters) ', (8j1 ": max_speedup1), 'x' echo 'Complex Math (100 iters) ', (8j1 ": max_speedup2), 'x' echo 'Mandelbrot Escape ', (8j1 ": max_speedup3), 'x' echo '' echo 'Key insight: Futhark excels when each element requires' echo 'many independent calculations (high compute-to-memory ratio).' echo '' echo 'These speedups come from:' echo ' - Parallel execution across ', (": FUTHARK_THREADS), ' CPU cores' echo ' - Loop fusion (no intermediate arrays)' echo ' - Optimized code generation' echo '' echo '==================================================================' exit 0