Century

A strict, compiled language that runs your loops on every core and every GPU, and refuses a loop that could race.

Read the manual

Windows · Linux · macOS  ·  one download, no installer  ·  all downloads

heavy.cy
pub fn heavy(n: u32) -> f64
    mut a = array[f64](n)
    cpu do                          -- every thread at once
        mut i = u32(0)
        while i < n do
            mut x = f64(i) * 0.000001
            mut k = u32(0)
            while k < u32(64) do
                x = sqrt(x * x + 1.0)
                k = k + u32(1)
            end
            a[i] = x
            i = i + u32(1)
        end
    end
    return a[n / u32(2)] + a[n - u32(1)]
end
1 thread
0.662 s
16 threads
0.075 s
8.8× faster, same answer. heavy(4000000), best of five warm runs, Intel i7-13620H laptop. From the manual, section 4.21.

Why Century?

Native speed, with a compiler that would rather refuse your program than guess about it.

Fast

Compiles straight to native x86-64 machine code. Mark a loop cpu do and it runs on every thread; mark it gpu do and it runs on any Vulkan GPU from NVIDIA, AMD or Intel.

Strict

Nothing converts implicitly: u32 + u64 is an error until you write u64(x). The compiler proves every parallel iteration independent, or refuses the program.

Exact

Floating-point maths runs in exactly the order you wrote. Give a sum a budget with ~ 1e-6 and it speeds up only when the worst case fits that budget.

Simple

One download and no installer. century --device tells you what your machine will run where, and the same compiler builds itself on all three systems.

Four things to try

Every example is from the manual, with its real output. The tour walks through ten.

Run it in one line

--jit compiles to machine code in memory and runs the function you name.

-- hello.cy: the sum of the squares 1..n
pub fn main(n: u32) -> u64
    mut total = u64(0)
    mut i = u32(1)
    while i <= n do
        total = total + u64(i * i)
        i = i + u32(1)
    end
    return total
end
$ century hello.cy --jit main --arg 10
hello.cy: ok - 0 types, 1 functions, 52 tokens, 26 nodes, 46 interned
jit main: 256 bytes (module 256)
jit main = 385

Send a loop to the GPU

Without a Vulkan driver the same code still runs, on the CPU, with the same answer.

pub fn scale(n: u32) -> u64
    mut a = array[f32](n)
    gpu do
        mut i = u32(0)
        while i < n do
            a[i] = f32(i) * f32(2.0)
            i = i + u32(1)
        end
    end
    mut s = u64(0)
    for x in a do
        s = s + u64(x)
    end
    return s
end

Give your maths an error budget

The sum may be reordered only if its worst case moves the result by at most one part in a million.

mut acc ~ 1e-6 = 0.0
mut i = u32(0)
while i < len(a) do
    acc = acc + a[i] * a[i]
    i = i + u32(1)
end

-- one million f64 values: 0.43 ms as written,
-- 0.15 ms with the budget (section 6.3)

Compile code while you run

The compiler is a library: compile source text and call it in the same process, about 0.11 ms per compile.

mut C = require("../Century/CenturyWindows/Source/century.cy")

pub fn main(z: u32) -> u32
    hp = C.lib_init()
    j = C.lib_compile(hp, bytes("pub fn sq(x: i64) -> i64\n    return x * x\nend\n"), bytes("sq.cy"), i32(1))
    if not C.lib_ok(j) then
        return u32(1)
    end
    C.lib_use(hp, j)
    mut args = array[u64](0)
    push(args, u64(12))
    return u32(C.lib_callfn(hp, j, C.lib_fn(hp, j, bytes("sq")), args))
end

Built with Century

Programs that exist today. More about each.

The compiler itself

Century compiles itself, and the second and third stages of the build must come out byte for byte identical.

Desktop apps

A calculator, notes and paint, using the system's own windows through extern.

Command-line tools

--build makes a standalone program, the way a C compiler does.