Weld is a runtime for improving the performance of data-intensive applications. It optimizes across libraries and functions by expressing the core computations in libraries using a small common intermediate representation, similar to CUDA and OpenCL.
Using Weld
Weld is a small programming language that supports parallel loops and builders, which are declarative objects that specify how to build results. The parallel loops can be used in conjunction with the builders to build a result in parallel.
This crate contains the Weld compiler and runtime, though users only interact with the compiler. Users use Weld by constructing a Weld program (currently as a string), compiling the string into a runnable module, and then running the module with in-memory data.
Weld JITs code into the current process using LLVM. As a result, Weld users must have a version of LLVM installed on their machine (currently, Weld uses LLVM 6).
Example
The following program shows a minimal Weld program that adds two numbers:
# extern crate weld;
#
# use *;
#
let code = "|a: i32, b: i32| a + b";
let conf = & new;
let mut module = compile.unwrap;
// Weld accepts a packed C struct as an argument.
let args = &MyArgs ;
let input = & new_from_data;
// A context manages memory.
let context = &mut new.unwrap;
// Running a Weld module and reading a value out of it is unsafe!
unsafe
Users write a Weld program as a string, compile it into a module, and then pass packed arguments into it to run the JITed code. The result is a pointer that represents the output of the Weld program: we can cast that to the appropriate pointer type and read it by dereferencing.
Modules
The WeldModule
is the main entry point into Weld. Users can compile Weld programs using
WeldModule::compile
, and then run compiled programs using WeldModule::run
.
The module functions can be configured in several ways. This configuration is controlled using
the WeldConf
struct, which is effectively a dictionary of String
key/value pairs that
control how a Weld program is compiled and run.
Values
Since Weld JITs code and implements a custom runtime, data passed in and out of it must be in a specific, C-compatible packed format. The Weld Github contains a plethora of information on how data should be formatted when passed into Weld, but in short, it is not safe to simply pass Rust objects into Weld.
WeldModule
accepts and returns a wrapper struct called WeldValue
, which wraps an opaque
*const void
that Weld reads depending on the argument and return types of the Weld program.
Weld's main run
function is thus unsafe
: users need to guarantee that the data passed into
Weld is properly formatted!
Passing Rust Values into Weld
Currently, users need to manually munge Rust values into a format that Weld understands, as specified here. Eventually, we may add a module in this crate that contains wrappers for some useful types. The current Rust types can be passed safely into Weld already:
- Primitive types such as
i8
,i16
, andf32
. These have a 1-1 correspondance with Weld. - Rust structs with
repr(C)
.
Notably, Vec<T>
cannot be passed without adhering to the custom Weld format. Currently,
that format is defined as:
There is thus a straightforward conversion from Vec<T>
to a WeldVec<T>
.
The data
module defines layouts of Weld-compatible types, and also contains some methods for
converting Rust values into Weld values.
Contexts
A context manages state such as allocation information. A context is passed into
WeldModule::run
and updated by the compiled Weld program.
The WeldContext
struct wraps a context. Contexts are internally reference counted because
values produced by Weld hold references to the context in which they are allocated. The memory
backing a WeldContext
is freed when all references to the context are dropped.