Git Product home page Git Product logo

nitrous's Introduction

Nitrous

Build Status

Nitrous provides a run-time LLVM-based compiler for CPython functions.

The project is already quite functional, however is still in very early stages of development. Read the docs to get started.

Requirements

  • Python 2.7
  • LLVM + clang 3.2 (can be acquired from the official download page)
  • nose and coverage for running tests
  • faulthandler if you want to get meaningful tracebacks from low-level crashes.

Similar Projects

nitrous's People

Contributors

dtcaciuc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

prannamalai

nitrous's Issues

Support C-style integer division

API choices here are Cython-style decorator

@module.function(Long, x=Long, y=Long)
@nitrous.cdivision
def foo(x, y)
    z = x / y

or a context manager

@module.function(Long, x=Long, y=Long)
def foo(x, y):
    with nitrous.cdivision:
        z = x / y

Create profiling facilities

It would be convenient to have a way to get basic profiling data for a module. Eg,

@function(x=Pointer(Double))
def foo(x):
    ...

m = module([foo], profile=True)

x = (Double.c_type * 1000)(...)
m.foo(x)

from nitrous.profile import stats, callgraph

# Print basic stats
p = stats(m.foo)
print p.calls, p.cumulative

# Show profile graph
callgraph(m.foo)

Add support for structures

Similar to scalar types,

from nitrous.module import Module
from nitrous.types import Double, Structure

m = Module(__name__)

# * Tuples rather than kwargs since order is important.
Coord = Structure(("x", Double), ("y", Double), ("z", Double))

...

# Allocate space for 100 coordinates
coords = (Coord.c_type * 100)()

Rework Pointers/Arrays

There are a couple of issues with how array system is set up.

  • Pointers have N-dimensional indexing which is weird
    1. The only reason is a rather specific performance edge case where we have N-dimensional index with all dimensions known at compile time except the major one. The current "DynamicArray" incurs overhead with its struct and "StaticArray" needs to have all dimensions known.
    2. Ideally we'd like to keep pointers for C interop but disallow any sort of element access or arithmetic
  • Dynamic/Static arrays are bad nomenclature. The word "Dynamic" here really means "selected dimensions are static, but only known at compile time".

The following is the attempt to borrow from Go language Array/Slice system with two exceptions:

  1. Slices/arrays get to have multiple dimensions.
  2. Slices/arrays will be passed by reference. While making few things seductively consistent (ie. assignment always means copy of the right hand side expression result), it would be a large divergence from how Python objects and data behave. Additionally, it would be a bet on how well LLVM optimizer eliminates unnecessary copies during function inlining.

From rough benchmarks, using dynamic arrays in performance edge case mentioned above, with pointer aliasing disallowed, results in ~ 5% hit, which is tolerable. There are a number of things that can be done, for instance allowing Array to have its first dimension unknown (still a hack, but at least a better nomenclature than Pointer)

Aggregate return values

This particular function in spectral norm benchmark code looks less than ideal

def eval_AtA_times_u(u, out, tmp):
    eval_A_times_u(u, tmp)
    eval_At_times_u(tmp, out)

Aggregate return values would turn it back into more familiar

def eval_AtA_times_u(u):
    return eval_At_times_u(eval_A_times_u(u))

The current candidates are arrays of fully defined shape and structs.

Experiment with JIT

It may be possible to avoid writing temporary .so files altogether by using JIT execution engine. Find a way to convert the result of ExecutionEngine::getPointerToFunction() to ctypes function object.

Create debugging facilities

At the very minimum, it should be possible to to emit debug symbols with

m = module([a, b, c], debug=True)

so that we can meaningfully run python process through GDB.

Resolve constant attributes

Should be able to use constant attributes without assigning them to a temporary first. Example from vector module:

def store(T):

    # FIXME only directly named constants are currently resolved.
    N = T.n

    @function(v=T, p=Pointer(T.element_type))
    def store_(v, p):
        for i in range(N):
            p[i] = get_element(T)(v, i)

    return store_

Basic support for vector types

Provide support for configurable width and element type. Should support pointers to vectors as well. Provide a way to access vector elements with [] notation.

from nos.types import Vector, Double

# Type declaration
Coord = Vector(Double, 3)

# Inside function
pos = Coord(1.0, 2.0, 3.0)
pos[0] += 5.0

Decouple source functions from module object

Currently, decorating a function leaves a number of __n2o_* attributes attached to it. Instead, leave the function unchanged and return a separate wrapper object.

The plan is to have the following

from nitrous.module import module
from nitrous.function import function, options


@function(Long, a=Long, b=Long)
def add(a, b):
  return a + b


@function(Long, a=Long, b=Long, c=Long)
def add3(a, b, c):
  return add(a, add(b, c))


m = module([add3])

print m.add3(4, 2, 10)

Only functions passed to module() builder will get interfaced to Python; the rest are not regularly callable, but will be privately pulled and compiled in. This decoupling provides a good model to build libraries.

Dynamic dimension-aware arrays

Implement an interface that would be familiar to numpy.array user

@module.function(None, a=Array(Long, 2), b=Array(Long))
def flatten2d(a, b):
    # a.ndim == 2
    # b.ndim == 1
    k = 0
    for i in range(a.shape[0]):
        for j in range(a.shape[1]):
            b[k] = a[i, j]
            k += 1

Resolve constant expression conditionals at compile time

In the following example

add_5 = False
add_any = True

@m.function(Long, a=Long)
def f(a):
    if add_any and add_5:
        a += 5
    return a * 2

the if block should be eliminated from the resulting function altogether since the test result can be resolved to False at compile time.

Array/struct allocation within compiled functions

Makes sense to start with stack allocated fixed-size arrays of scalars and structures.

from nitrous import Float, Pointer

Vec3f = Pointer(Float, shape=(3,))

@m.function(e0=Float)
def func(e0):
    v = Vec3f()
    v[0] = e0

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.