1 # bend-blas: cuBLAS on Array, one def per routine, plus device 2 # buffers. Generated by gen/effs.py: edit the table there, not this file. 3 # 4 # The same conventions as blas.bend. cuBLAS is opened at runtime with 5 # dlopen (libcudart, libcublas), so this file builds anywhere; on a 6 # machine without CUDA every routine answers Fail{(1, ..)}, and 7 # Cublas.available answers False. A device buffer is uploaded once 8 # (Cublas.upload), referenced by its U32 id, never consumed, and freed 9 # with Cublas.free; Cublas.sgemm_w reads one as its right operand, so a 10 # model's weights cross the bus once. On the interpreter the JS twins 11 # run on the host: plain loops, correct and slow. Not yet run on an 12 # NVIDIA machine: the C is checked against the cuBLAS signatures only. 13 import Base 14 15 # True when cuBLAS opens on this machine: the answer of every other routine is Fail{(1, ..)} otherwise. Never fails. 16 def Cublas.available() -> IO(Result<&1, &1, U32 & String, Bool>): 17 import "./effs/cublas_available.c" 18 import "./effs/cublas_available.js" 19 20 def Cublas.available.try() -> IO(Bool): 21 IO.try(Bool, Cublas.available()) 22 23 # The first n floats of an array copied to the device; answers the buffer id. The array is consumed. 24 def Cublas.upload(n: U32, a: Array) -> IO(Result<&1, &1, U32 & String, U32>): 25 import "./effs/cublas_upload.c" 26 import "./effs/cublas_upload.js" 27 28 def Cublas.upload.try(n: U32, a: Array) -> IO(U32): 29 IO.try(U32, Cublas.upload(n, a)) 30 31 # The n floats of a device buffer back in a fresh array; the buffer stays. 32 def Cublas.download(id: U32) -> IO(Result<&1, &1, U32 & String, Array>): 33 import "./effs/cublas_download.c" 34 import "./effs/cublas_download.js" 35 36 def Cublas.download.try(id: U32) -> IO(Array): 37 IO.try(Array, Cublas.download(id)) 38 39 # Frees a device buffer; its id is dead after. 40 def Cublas.free(id: U32) -> IO(Result<&1, &1, U32 & String, Unit>): 41 import "./effs/cublas_free.c" 42 import "./effs/cublas_free.js" 43 44 def Cublas.free.try(id: U32) -> IO(Unit): 45 IO.try(Unit, Cublas.free(id)) 46 47 # C = alpha op(A) op(B) + beta C on the GPU, the operands copied to the 48 # device for the call and the result back; the same arguments as 49 # Blas.sgemm. 50 def Cublas.sgemm(ta: U32, tb: U32, m: U32, n: U32, k: U32, alpha: F32, a: Array, b: Array, beta: F32, c: Array) -> IO(Result<&1, &1, U32 & String, Array>): 51 import "./effs/cublas_sgemm.c" 52 import "./effs/cublas_sgemm.js" 53 54 def Cublas.sgemm.try(ta: U32, tb: U32, m: U32, n: U32, k: U32, alpha: F32, a: Array, b: Array, beta: F32, c: Array) -> IO(Array): 55 IO.try(Array, Cublas.sgemm(ta, tb, m, n, k, alpha, a, b, beta, c)) 56 57 # Cublas.sgemm with the right operand a device buffer (uploaded once with 58 # Cublas.upload, never consumed): the shape of a model's weights. 59 def Cublas.sgemm_w(ta: U32, tb: U32, m: U32, n: U32, k: U32, alpha: F32, a: Array, b: U32, beta: F32, c: Array) -> IO(Result<&1, &1, U32 & String, Array>): 60 import "./effs/cublas_sgemm_w.c" 61 import "./effs/cublas_sgemm_w.js" 62 63 def Cublas.sgemm_w.try(ta: U32, tb: U32, m: U32, n: U32, k: U32, alpha: F32, a: Array, b: U32, beta: F32, c: Array) -> IO(Array): 64 IO.try(Array, Cublas.sgemm_w(ta, tb, m, n, k, alpha, a, b, beta, c))