libraryextensions/gpu/lib/Gpu.xtl
Gpu: XIR programs run on OpenCL devices (or the reference interpreter) from X_eTaL
Import: "gp:" u_se< "Gpu" Run with xetal-x (X_eTaL-extensions' bridge host): "scripts/xx run prog.xtl" puts this package and this repository's libraries in reach.
The first half binds the native functions (extensions/gpu/rust), one line each, with X_eTaL-extensions' binding macro (Ffi.xtlm): a session per thread, loaded, bound, run, read. The second half is ordinary X_eTaL over them: functions that write XIR programs (the text is data, so these are functions, not macros: docs/macros-and-extensions.md, X4), a product and a dense layer in one call each, and a check for f32 results.
Native functions
ˡd̲evices : Unit -> Char
The OpenCL devices, one line each (opencl:N).
"d_evices : unit -> text" ᶠᶠⁱb̲ind< "gpu/devices"
ˡl̲oad : Char -> Int
Load an XIR program, by its text; the number of inputs.
"l_oad : text -> int" ᶠᶠⁱb̲ind< "gpu/load"
ˡl̲ower : Char -> Char -> Int
A function from its X_eTaL text and "FILE|KINDS" (what gp:a_ccel< expands to; the file gives its imports, the kinds say int or float per argument); its parameter count.
"l_ower : text text -> int" ᶠᶠⁱb̲ind< "gpu/lower"
ˡb̲ind : (Num a, Num b) => a -> b -> Int
n gp:b_ind a: input n (from 1) gets the array a, of its declared shape.
"b_ind : int float -> int" ᶠᶠⁱb̲ind< "gpu/bind"
ˡb̲indFile : Num a => a -> Char -> Int
n gp:b_indFile path: bind a binary array file; only its path crosses the bridge.
"b_indFile : int text -> int" ᶠᶠⁱb̲ind< "gpu/bind_file"
ˡs̲chedule : Char -> Int
The schedule for later runs, as a schedules/*.toml text ("" for the defaults).
"s_chedule : text -> int" ᶠᶠⁱb̲ind< "gpu/schedule"
ˡr̲un : Num a => a -> Int
Run on opencl:d, or on the reference interpreter for -1; the number of outputs.
ᵍᵖ⁼u̲se< "Gpu"
ᵍᵖl̲oad "%a = input i64 [3]\n%k = const i64 [] 2\n%b = map mul %k %a\noutput %b\n" 1
1 ᵍᵖb̲ind 1 2 3 1
ᵍᵖr̲un -1 1
ᵍᵖo̲utputInts 1 2 4 6
"r_un : int -> int" ᶠᶠⁱb̲ind< "gpu/run"
ˡr̲erun : Num a => a -> Int
Rerun on opencl:d with resident kernels and inputs; only rebound inputs upload. Changing the program, schedule or device prepares a new session.
"r_erun : int -> int" ᶠᶠⁱb̲ind< "gpu/rerun"
ˡo̲utput : Num a => a -> Float
Output i (from 1) of the last run, as Floats.
"o_utput : int -> floats" ᶠᶠⁱb̲ind< "gpu/output"
ˡo̲utputInts : Num a => a -> Int
Output i (from 1) of the last run, as Ints (exact to 2^53).
"o_utputInts : int -> ints" ᶠᶠⁱb̲ind< "gpu/output"
ˡe̲xplain : Unit -> Char
The last run's plan in words.
"e_xplain : unit -> text" ᶠᶠⁱb̲ind< "gpu/explain"
Programs, as text
ʰn̲l : Unit -> Char
A newline (private to this file).
ʰn̲l ← { @ → f̲irst "\n" }
ˡp̲roductProgram : a -> b -> Char
sa gp:p_roductProgram sb: the XIR text of a times b ('+ '* i_nner) for an a of the shape sa and a b of the shape sb (Floats).
ᵍᵖ⁼u̲se< "Gpu"
(2 3) ᵍᵖp̲roductProgram 3 %a = input f64 [2 3] %b = input f64 [3] %c = matmul %a %b output %c
ˡp̲roductProgram ← { sa sb → a ← "%a = input f64 [" c̲at (f̲ormat sa) c̲at "]" c̲at ʰn̲l @ b ← "%b = input f64 [" c̲at (f̲ormat sb) c̲at "]" c̲at ʰn̲l @ a c̲at b c̲at "%c = matmul %a %b" c̲at (ʰn̲l @) c̲at "output %c" c̲at ʰn̲l @ }
ˡd̲enseProgram : Int -> a -> Char
sx gp:d_enseProgram swb: the XIR text of the layer x W + b (Accel's ac:d_ense, the bias the last row of wb) for an x of the shape sx and a wb of the shape swb.
ˡd̲enseProgram ← { sx swb → nl ← ʰn̲l @ rows ← f̲irst sx inputs ← "%x = input f64 [" c̲at (f̲ormat sx) c̲at "]" c̲at nl c̲at "%wb = input f64 [" c̲at (f̲ormat swb) c̲at "]" c̲at nl split ← "%w = drop -1 %wb" c̲at nl c̲at "%t = take -1 %wb" c̲at nl c̲at "%v = ravel %t" c̲at nl bias ← "%o = const i64 [" c̲at (f̲ormat rows) c̲at "] " c̲at (f̲ormat o̲ffsets rows) c̲at nl c̲at "%bias = table right %o %v" c̲at nl inputs c̲at split c̲at bias c̲at "%xw = matmul %x %w" c̲at nl c̲at "%y = map add %xw %bias" c̲at nl c̲at "output %y" c̲at nl }
ˡa̲fter : (Any a, Any b) => a -> b -> a
a gp:a_fter b: a, evaluated after b (the right side first, as X_eTaL evaluates); what gp:a_ccel<'s expansion sequences its calls with, so it binds no names it never reads.
ᵍᵖ⁼u̲se< "Gpu"
1 ᵍᵖa̲fter 2 1
ˡa̲fter ← { a b → a }
In one call
ˡp̲roduct : (Num a, Num b) => a -> b -> Float
a gp:p_roduct b: a '+ '* i_nner b computed on opencl:0.
ˡp̲roduct ← { a b → n ← ˡl̲oad (s̲hape a) ˡp̲roductProgram s̲hape b ba ← 1 ˡb̲ind a bb ← 2 ˡb̲ind b r ← ˡr̲un 0 ˡo̲utput 1 }
ˡd̲ense : (Num a, Num b) => a -> b -> Float
x gp:d_ense wb: the layer x W + b (ac:d_ense) computed on opencl:0.
ˡd̲ense ← { x wb → n ← ˡl̲oad (s̲hape x) ˡd̲enseProgram s̲hape wb bx ← 1 ˡb̲ind x bw ← 2 ˡb̲ind wb r ← ˡr̲un 0 ˡo̲utput 1 }
ˡn̲ear : (Num a, Num b) => a -> b -> Char
want gp:n_ear got: "ok" when the shapes match and every item agrees within a relative 1e-5 (absolute below 1), as an f32 device's result agrees with the evaluator's f64; else what differs.
ᵍᵖ⁼u̲se< "Gpu"
(1 2 3) ᵍᵖn̲ear 1.000001 2.000001 3.000001 ok
(1 2 3) ᵍᵖn̲ear 1 2 FAIL: shapes 3 and 2
ˡn̲ear ← { w g → n̲ot (s̲hape w) m̲atch s̲hape g ? "FAIL: shapes " c̲at (f̲ormat s̲hape w) c̲at " and " c̲at f̲ormat s̲hape g d ← a̲bs (f̲loat w) − f̲loat g '∧ r̲/ r̲avel d ≤ 0.00001 × 1.0 m̲ax a̲bs f̲loat w ? "ok" "FAIL: the largest difference is " c̲at f̲ormat 'm̲ax r̲/ r̲avel d }
ˡpackage : Char
The native package behind these functions (a literal l: name also makes this file a library to "xetal type": X_eTaL-extensions' ask E7).
ˡpackage ← "gpu"