GPU with one word
Put @GPU above a function. Matrix maths and parallel loops inside it become GPU kernels for NVIDIA, AMD, Intel and Apple graphics; without a GPU the same code runs on the CPU.
Write it once. Run it on every CPU core and the GPU.
A fast, compiled language with a calm, readable syntax. Tasks, parallel loops and GPU functions are part of the language, so the same source runs on your CPU and your graphics card.
curl -fsSL https://github.com/MehmetEminKaymaz/marlin/releases/latest/download/install.sh | sh
Marlin keeps the syntax small and moves the hard parts into the compiler: scheduling, GPU kernels, memory and safety checks.
Put @GPU above a function. Matrix maths and parallel loops inside it become GPU kernels for NVIDIA, AMD, Intel and Apple graphics; without a GPU the same code runs on the CPU.
marl f(x) starts a task, await(t) takes its result. Waiting never blocks a thread, so thousands of tasks can be in flight.
parallel for spreads iterations over every core. The compiler rejects loops that could race, so the ones it accepts are correct.
std.http ships an HTTP/1.1 server with routing, query and form helpers. One call serves every request in its own task.
Parameterised queries, transactions and connection pools. scan fills a list of structs straight from the result rows.
matrix<T, N> is a first-class type. a + b * 2 - c runs as one fused loop and matmul is tiled for speed.
Every example below was compiled with marlin -O2 and run. The output is exactly what it printed.
struct Point {
float64 x;
float64 y;
}
func length(Point p) -> float64 {
return sqrt(p.x * p.x + p.y * p.y);
}
func main() -> void {
list<Point> points = [Point(3, 4), Point(6, 8)];
let square = (int n) -> int | n * n;
for p in points {
print("length:", length(p));
}
print("square(7) =", square(7));
free(points);
}
length: 5.0 length: 10.0 square(7) = 49
// Split the work, run it on every core.
func fib(int n) -> int64 {
if n < 25 {
return fib_seq(n);
}
task<int64> left = marl fib(n - 1); // a new task
int64 right = fib(n - 2); // keep working here
return await(left) + right;
}
func fib_seq(int n) -> int64 {
if n < 2 {
return n;
}
return fib_seq(n - 1) + fib_seq(n - 2);
}
func main() -> void {
task<int64> t = marl fib(32);
print("fib(32) =", await(t));
}
fib(32) = 2178309
// Iterations are spread over all cores.
// The compiler rejects loops that could race.
func main() -> void {
int64 n = 10000000;
matrix<float64, 1> v = zeros(n);
parallel for i in 0 .. n {
v[i] = sqrt(i * 1.0); // each iteration writes only v[i]
}
print("sum of roots:", sum(v));
}
sum of roots: 21081849486.439312
// One annotation moves a function to the GPU.
@GPU
func relax(matrix<float64> a, matrix<float64> b, int n) -> matrix<float64> {
matrix<float64> x = a;
for k in 0 .. n {
x = x * 0.5 + b; // runs as a GPU kernel
}
return x;
}
func main() -> void {
matrix<float64> a = ones(2000, 2000);
matrix<float64> b = fill(2000, 2000, 2.0);
matrix<float64> c = relax(a, b, 100); // data stays on the GPU for 100 steps
print("sum:", sum(c));
}
sum: 16000000.0
import std.http as http;
func home(http.Request req) -> http.Response {
return http.html(200, "<h1>Hello from Marlin</h1>");
}
func hello(http.Request req) -> http.Response {
return http.text(200, "hello " + http.query_param(req, "name") + "\n");
}
func main() -> void {
list<http.Route> routes = [];
push(routes, http.get("/", home));
push(routes, http.get("/hello", hello));
http.serve_concurrent(8080, routes); // each request runs in its own task
}
hello Ada
import std.sqlite as sql;
struct User {
int64 id;
string name;
int age;
}
func main() -> void {
sql.Conn db = sql.open(":memory:");
sql.clear(sql.exec(db, "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INT)"));
sql.clear(sql.query(db, "INSERT INTO users (name, age) VALUES ($1, $2)",
[sql.arg_text("Ada"), sql.arg_int(36)]));
sql.clear(sql.query(db, "INSERT INTO users (name, age) VALUES ($1, $2)",
[sql.arg_text("Alan"), sql.arg_int(41)]));
sql.Result r = sql.query(db, "SELECT id, name, age FROM users WHERE age > $1",
[sql.arg_int(30)]);
list<User> users = scan(r.rows); // every row becomes a User
sql.clear(r);
for u in users {
print(u.id, u.name, u.age);
}
free(users);
sql.close(db);
}
1 Ada 36 2 Alan 41
.marl to machine codeMarlin is a front end for MLIR, the compiler framework behind many modern ML and HPC tools. Each step can be inspected with --emit.
Small, typed syntax. Modules with import.
Types, race-freedom of parallel loops, task and GPU rules.
Loops, tensors and tasks as high-level IR.
CPU code through LLVM IR. GPU kernels as PTX for CUDA and SPIR-V for Vulkan.
One file for Linux, macOS or Windows. No VM, no interpreter.
When a GPU is there, the same source uses it. Without one, it runs on every CPU core.
2048 × 2048 float matmul, five times
4096 × 4096 float on the GPU
The first card is one @GPU program, run once with MARLIN_GPU=cpu and once on the graphics card (copies to and from the GPU included). Measured on the same laptop; your numbers will differ.
Run the command for your system. It downloads the compiler, verifies it, puts it in your home folder and adds marlin to your PATH. Run it again to update.
marlin doctor compiles a few small programs and tells you exactly what is missing, with the command that installs it.
Save a .marl file and compile it. You get a native executable; add -O2 for speed.
# needs a C compiler, e.g. sudo apt install build-essential $ curl -fsSL https://github.com/MehmetEminKaymaz/marlin/releases/latest/download/install.sh | sh $ marlin doctor # hello.marl: func main() -> void { print("hello, ocean"); } $ marlin hello.marl -O2 -o hello $ ./hello hello, ocean
# needs the Command Line Tools: xcode-select --install $ curl -fsSL https://github.com/MehmetEminKaymaz/marlin/releases/latest/download/install.sh | sh $ marlin doctor # hello.marl: func main() -> void { print("hello, ocean"); } $ marlin hello.marl -O2 -o hello $ ./hello hello, ocean
# needs Visual Studio Build Tools (C++ and Clang); marlin doctor shows how PS> irm https://github.com/MehmetEminKaymaz/marlin/releases/latest/download/install.ps1 | iex PS> marlin doctor # hello.marl: func main() -> void { print("hello, ocean"); } PS> marlin hello.marl -O2 -o hello.exe PS> .\hello.exe hello, ocean
The Marlin extension adds syntax colouring, snippets, live errors from the real compiler (through its built-in language server) and a one-key Compile & Run.