bend parallel programming language tutorial

Bend: A Basic Introduction

Bend is a high-level, massively parallel programming language. It feels like Python but scales like CUDA. Your code runs on CPUs and GPUs, and it automatically parallelizes anything that isn’t inherently sequential, using thousands of threads effortlessly.

While Bend is promising, it has limitations. It isn’t as fast as state-of-the-art compilers like GCC or GHC, and it has its share of instability and bugs. However, it delivers on its promise of horizontal scaling with cores. For those interested in the language, this guide will teach you it’s basics based on information available in their github.

Installation

First, install Rust nightly, as the language is implemented in it:

rustup install nightly

Then, install HVM2 and Bend:

cargo +nightly install hvm
cargo +nightly install bend-lang

To test if it worked, type:

bend --help

For GPU support, you also need CUDA version 12.X(Support for AMD and Intel GPUs to be added soon). Windows is currently not supported, but you may use Windows Subsystem for Linux(WSL) as a workaround.

Hello, World!

Bend looks a lot like Python but has some differences. Here’s a “Hello, world!” program:

def main():
  return "Hello, world!"

Note that Bend doesn’t have IO yet, so you use return instead of print. To run the program, open the terminal and run:

bend run main.bend

This will display the hello world message using the interpreter. To execute your code using multiple cores instead, use bend run-c main.bend , to use your GPU run bend run-cu main.bend

Basic Functions and Datatypes

Functions in Bend are “pure”: they have an input and return an output. Here’s a function that checks if you’re an adult:

# creates function using "def", uses if else to verify input variable "age"
def am_i_old(age):
  if age < 18:
    return "you're a kid"
  else:
    return "you're an adult"

def main():
  return am_i_old(32)

Here’s a function to calculate the distance between two points:

def distance(ax, ay, bx, by):
  dx = bx - ax
  dy = by - ay
  return (dx * dx + dy * dy) ** 0.5

def main():
  return distance(10.0, 10.0, 20.0, 20.0)

You can use tuples for cleaner code:

# simple tuples using ()
def distance(a, b):
  (ax, ay) = a
  (bx, by) = b
  dx = bx - ax
  dy = by - ay
  return (dx * dx + dy * dy) ** 0.5

def main():
  return distance((10.0, 10.0), (20.0, 20.0))

Example using objects:

object V2 { x, y }

def distance(a, b):
  open V2: a
  open V2: b
  dx = b.x - a.x
  dy = b.y - a.y
  return (dx * dx + dy * dy) ** 0.5

def main():
  return distance(V2 { x: 10.0, y: 10.0 }, V2 { x: 20.0, y: 20.0 })

Bend has three built-in numeric types: u24, i24, f24.

You can also create datatypes:

type Shape:
  Circle { radius }
  Rectangle { width, height }

def area(shape):
  match shape:
    case Shape/Circle:
      return 3.14 * shape.radius ** 2.0
    case Shape/Rectangle:
      return shape.width * shape.height

def main:
  return area(Shape/Circle { radius: 10.0 })

Lists in Bend are datatypes:

type List:
  Nil
  Cons { head, tail }

def main:
  my_list = [1, 2, 3]
  return my_list

You can match lists:

def main:
  my_list = [1, 2, 3]
  match my_list:
    case List/Cons:
      return my_list.head
    case List/Nil:
      return 0

Immutability

Variables in Bend are immutable. Instead of this mutable Python code:

def parity(x):
  result = "odd"
  if x % 2 == 0:
    result = "even"
  return result

You write this in Bend:

def is_even(x):
  if x % 2 == 0:
    return "even"
  else:
    return "odd"

def main:
  return is_even(7)

Folds and Bends

Recursive Datatypes

Define a binary tree:

type Tree:
  Node { ~lft, ~rgt }
  Leaf { val }

tree = Tree/Node {
  lft: Tree/Node { lft: Tree/Leaf { val: 1 }, rgt: Tree/Leaf { val: 2 } },
  rgt: Tree/Node { lft: Tree/Leaf { val: 3 }, rgt: Tree/Leaf { val: 4 } }
}

Fold: Consuming Recursive Datatypes

Sum the elements of a tree:

def sum(tree):
  fold tree:
    case Tree/Node:
      return tree.lft + tree.rgt
    case Tree/Leaf:
      return tree.val

def main:
  tree = Tree/Node {
    lft: Tree/Node { lft: Tree/Leaf { val: 1 }, rgt: Tree/Leaf { val: 2 } },
    rgt: Tree/Node { lft: Tree/Leaf { val: 3 }, rgt: Tree/Leaf { val: 4 } }
  }
  return sum(tree)

Bend: Generating Recursive Datatypes

Generate a binary tree:

def main():
  bend x = 0:
    when x < 3:
      tree = Tree/Node { lft: fork(x + 1), rgt: fork(x + 1) }
    else:
      tree = Tree/Leaf { val: 7 }
  return tree

Parallel “Hello, World”

Parallelize code naturally in Bend by writing non-sequential algorithms. Here’s a parallel sum:

def main():
  bend d = 0, i = 0:
    when d < 28:
      sum = fork(d+1, i*2+0) + fork(d+1, i*2+1)
    else:
      sum = i
  return sum

That’s the parallel “Hello, World” in Bend! If you’d like to learn more about the language, head to their github guide page where you can find more explanations and examples.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *