exaloop / codon Goto Github PK
View Code? Open in Web Editor NEWA high-performance, zero-overhead, extensible Python compiler using LLVM
Home Page: https://docs.exaloop.io/codon
License: Other
A high-performance, zero-overhead, extensible Python compiler using LLVM
Home Page: https://docs.exaloop.io/codon
License: Other
Codon is awesome. works really well with the stuff so far I have tried.
I also did some benchmarking, it's performing close to CFFI with python.
Only place I am stuck right now is testing async code.
Is async support is in roadmap?
e.g. in module module.codon
:
foo: Static[str] = 'foo'
and then:
from module import foo
print(foo)
gives
Assert failed: cannot find 'foo' [scratch.codon:2:7]
Expression: val
Source: /Users/arshajii/Documents/workspace/codon/codon/parser/visitors/typecheck/access.cpp:37
[1] 34325 abort build/codon run -release build/scratch.codon
x = []
for i in range(10):
all(x[i] < 0 for i in [0])
x.append(i)
gives
scratch.codon:92:9: error: cannot infer the type of x[i] < 0
scratch.codon:92:8: error: cannot typecheck the program
scratch.codon:92:8: error: while realizing ._lambda_126:0 (arguments ._lambda_126:0)
import networkx as nx
import sys
import codon
from time import time
def graph_to_codon_repr(G):
ref = {}
H = []
def node_ref(n, ref):
x = ref.get(n, -1)
if x == -1:
x = len(ref)
ref[n] = x
return x
for e in G.edges():
k = node_ref(e[0], ref)
v = node_ref(e[1], ref)
while len(H) <= max(k, v):
H.append([])
H[k].append(v)
H[v].append(k)
return H
@codon.jit
def betweenness_centrality(G):
def _single_source_shortest_path_basic(G, s):
S = []
P = {}
for v in range(len(G)): #G:
P[v] = []
sigma = dict.fromkeys(range(len(G)), 0.0) #dict.fromkeys(G, 0.0) # sigma[v]=0 for v in G
D = {}
sigma[s] = 1.0
D[s] = 0
Q = deque([s])
while Q: # use BFS to find shortest paths
v = Q.popleft()
S.append(v)
Dv = D[v]
sigmav = sigma[v]
for w in G[v]:
if w not in D:
Q.append(w)
D[w] = Dv + 1
if D[w] == Dv + 1: # this is a shortest path, count paths
sigma[w] += sigmav
P[w].append(v) # predecessors
return S, P, sigma, D
def _accumulate_basic(betweenness, S, P, sigma, s):
delta = dict.fromkeys(S, 0.)
while S:
w = S.pop()
coeff = (1 + delta[w]) / sigma[w]
for v in P[w]:
delta[v] += sigma[v] * coeff
if w != s:
betweenness[w] += delta[w]
return betweenness, delta
def _rescale(betweenness, n, normalized, directed=False, k: Optional[float] = None, endpoints=False):
if normalized:
if endpoints:
if n < 2:
scale = None # no normalization
else:
# Scale factor should include endpoint nodes
scale = 1 / (n * (n - 1))
elif n <= 2:
scale = None # no normalization b=0 for all nodes
else:
scale = 1 / ((n - 1) * (n - 2))
else: # rescale by 2 for undirected graphs
if not directed:
scale = 0.5
else:
scale = None
if scale is not None:
if k is not None:
scale = scale * n / k
for v in betweenness:
betweenness[v] *= scale
return betweenness
betweenness = dict.fromkeys(range(len(G)), 0.0) # b[v]=0 for v in G
for s in range(len(G)):
S, P, sigma, _ = _single_source_shortest_path_basic(G, s)
betweenness, _ = _accumulate_basic(betweenness, S, P, sigma, s)
betweenness = _rescale(betweenness, len(G), normalized=True)
return betweenness
t0 = time()
g = nx.read_edgelist(sys.argv[1], delimiter="\t", nodetype=str, create_using=nx.Graph)
t1 = time()
print('read took', t1 - t0)
t0 = time()
h = graph_to_codon_repr(g)
t1 = time()
print('conv took', t1 - t0)
t0 = time()
bc = betweenness_centrality(h)
t1 = time()
print('bc took', t1 - t0)
read took 0.0837399959564209
conv took 0.03612804412841797
Assert failed: cannot find 's' [test.py:41:20]
Expression: val
Source: /Users/arshajii/Documents/workspace/codon/codon/parser/visitors/typecheck/access.cpp:37
[1] 34373 abort python3 test.py WormNet.v3.benchmark.txt
When I run the following code:
print(int("3\n"))
The expected output is 3, as it is when run with the regular python interpreter. However, when run with codon, I get the following error message:
ValueError: invalid literal for int() with base 10: 3
is None
not evaluated statically sometimesdef foo(x = None):
if x is not None:
return x + 1
return -1
print(foo()) # <-- unsupported operand type(s) for +: 'NoneType' and 'int'
print(foo(10)) # <-- works; 11
import x
class X(x.A): # error: name '' is not defined
pass
kwargs
Need general way to access kwargs
names etc.
def f(x: list[int] = []):
x.append(1)
print(x)
f()
f() # Codon gives [1] ; Python gives [1, 1]
class A:
def foo(self):
return B() # error: name 'B' is not defined
class B(A):
pass
B().foo()
Disable this:
List[Union]
x: Union = 1
(Empty Union is allowed as a return type--- this indicates an auto-deducible union).
Union[T]
equal to T
isinstance
and constructorsT.__mro__
for polymorphic typesPer python standard, when specifying type for positional or keyword arguments only type of one such argument should be supplied:
def sum_pos(*args: int) -> int:
def sum_kw(**kwargs: int) -> int:
However, in codon, type for positional or keyword arguments wrapper must be supplied:
def sum_pos(*values: Generator[int]) -> int:
This is especially difficult for keyword arguments, as codon wraps them into a tuple:
# this works only if there are exactly 3 keyword arguments, otherwise throws compile error
def sum_kw(**values: Tuple[int, int, int]) -> int:
Hi, Do you have plan to support windows?
For example:
def i2i_1(x: int) -> int:
return x + 1
def i2i_2(x: int) -> int:
return x + 2
fn = i2i_1 if some_boolean else i2i_2
Raises error: cannot unify i2i_1:0[int] and i2i_2:0[int]
from python import foo
if foo() > 1:
pass
gives
Trunc only operates on integer
%262 = trunc i8* %261 to i1, !dbg !24241
Assert failed: module broken
Expression: !broken
Source: /Users/arshajii/Documents/workspace/codon/codon/sir/llvm/optimize.cpp:232
zsh: abort build/codon run -release build/scratch.codon
print(sorted(foo.items(), key=str))
does not work
Hi, thanks for your awesome work. I followed the example in https://docs.exaloop.io/codon/#codon-at-a-glance and got an error.
$ codon run -release fib.py
terminate called after throwing an instance of 'fmt::v9::format_error'
what(): argument not found
[1] 971 abort codon run -release fib.py
I'd love to see if Codon could compile graphql-core.
I tried before with rpython but the code adaptations required to make it work were not trivial.
Why compiling graphql-core? It could open up a lot of applications and speed up in other codebases.
The cool thing is that graphql-core has no dependencies on external packages, so it would have a defined scope.
Happy to offer a generous sponsorship to make it possible :)
Note: GraphQL right now has some async code only required for execution, but that could be skipped if it's not currently supported by codon
I built codon from source with gpu support and try to run a simple gpu code but got following error. How should i fix it? thanks.
$ ./build/codon run gpu.py
CUDA error at /home/t/repos/codon/codon/runtime/gpu.cpp:42: a PTX JIT compilation failed
Aborted
gpu.py code:
import gpu
@gpu.kernel
def hello(a, b, c):
i = gpu.thread.x
c[i] = a[i] + b[i]
a = [i for i in range(16)]
b = [2*i for i in range(16)]
c = [0 for _ in range(16)]
hello(a, b, c, grid=1, block=16)
It can generate .ptx file, the content is at https://gist.github.com/tiendung/c0d97a89cf3d07332e4c41aae3abd72e
The Python decorator part mentions the codon library can be installed via pip install.
The example only shows a workaround on macOS via
python3 -m pip install codon-0.13.0-cp39-cp39-macosx_12_0_arm64.whl
It doesn't seem to work on linux yet.
A direct python3 -m pip install codon
throws the following error
ERROR: Command errored out with exit status 1:
command: /opt/anaconda3/bin/python -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-xek5nan9/cogent/setup.py'"'"'; __file__='"'"'/tmp/pip-install-xek5nan9/cogent/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base /tmp/pip-pip-egg-info-27df80_7
cwd: /tmp/pip-install-xek5nan9/cogent/
Complete output (6 lines):
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/tmp/pip-install-xek5nan9/cogent/setup.py", line 61
print "Failed to build html due to ImportErrors for sphinx"
^
SyntaxError: Missing parentheses in call to 'print'. Did you mean print("Failed to build html due to ImportErrors for sphinx")?
----------------------------------------
ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.
A high-performance, zero-overhead, extensible Python compiler using LLVM
import gpu
@test
def test_mandelbrot():
MAX = 1000 # maximum Mandelbrot iterations
N = 256 # width and height of image
pixels = [0 for _ in range(N * N)]
def scale(x, a, b):
return a + (x/N)*(b - a)
@gpu.kernel
def kernel(pixels):
idx = (gpu.block.x * gpu.block.dim.x) + gpu.thread.x
i, j = divmod(idx, N)
c = complex(scale(j, -2.00, 0.47), scale(i, -1.12, 1.12))
z = 0j
iteration = 0
while abs(z) <= 2 and iteration < MAX:
z = z**2 + c
iteration += 1
pixels[idx] = int(255 * iteration/MAX)
kernel(pixels, grid=(N*N)//1024, block=1024)
test_mandelbrot()
gives
Process 3356300 stopped
* thread #1, name = 'codon', stop reason = signal SIGSEGV: invalid address (fault address: 0x0)
frame #0: 0x00007fffee7f0ff3 libcodonc.so`codon::ast::TypecheckVisitor::transformInplaceUpdate(this=0x00007ffffffe8f08, stmt=0x000000000581f460) at assign.cpp:200:25
197 std::pair<bool, ExprPtr> TypecheckVisitor::transformInplaceUpdate(AssignStmt *stmt) {
198 // Case: in-place updates (e.g., `a += b`).
199 // They are stored as `Update(a, Binary(a + b, inPlace=true))`
-> 200 auto bin = stmt->rhs->getBinary();
201 if (bin && bin->inPlace) {
202 transform(bin->lexpr);
203 transform(bin->rexpr);
Making MAX
and N
global fixes the issue.
Hi, downloaded using specified command from readme: /bin/bash -c "$(curl -fsSL https://exaloop.io/install.sh)"
.
But getting this error while building fibonacci program(from project readme):
➜ tmp codon build -release -exe fib.py
dyld: Symbol not found: __ZNKSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE3strEv
Referenced from: /Users/freakynit/.codon/lib/codon/libcodonrt.dylib (which was built for Mac OS X 12.0)
Expected in: /usr/lib/libc++.1.dylib
System:
Mac: 11.5.1 (MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports))
Hi I'm trying to see if codon
production is faster than directly run codes with CPython
intepreter.
And happened to find it wrong in some cases.
It seems that when calling some std functions, codon
's performance is worse than CPython
but I don't know what was wrong.
I'll put my codes and benchmark bash script below, hope someone would like to give it a look or run it on their own to see if that only happens on me.
text.codon does not exist:
codon run text.codon
libc++abi: terminating with uncaught exception of type fmt::v9::format_error: argument not found
[1] 15634 abort codon run text.codon
https://stackoverflow.com/questions/73473074/speed-up-set-partition-generation-by-skipping-ones-with-subsets-smaller-or-large (asked on HN on the Taichi thread):
def conforms(candidate, minsize, forgive):
"""
Check if partition `candidate` is at most `forgive` additions from making
all its elements conform to having minimum size `minsize`
"""
deficit = 0
for p in candidate:
need = minsize - len(p)
if need > 0:
deficit += need
# Is the deficit small enough?
return (deficit <= forgive)
def partition_filtered(collection, minsize=1, forgive=0):
"""
Generate partitions that contain at least `minsize` elements per set;
allow `forgive` missing elements, which can get added in subsequent steps
"""
if len(collection) == 1:
yield [ collection ]
return
first = collection[0]
for smaller in partition_filtered(collection[1:], minsize, forgive=forgive+1):
# insert `first` in each of the subpartition's subsets
for n, subset in enumerate(smaller):
candidate = smaller[:n] + [[ first ] + subset] + smaller[n+1:]
if conforms(candidate, minsize, forgive):
yield candidate
# put `first` in its own subset
candidate = [ [ first ] ] + smaller
if conforms(candidate, minsize, forgive):
yield candidate
import time
t = time.time()
something = list(range(1,15)) # [1, ... 12]
for n, p in enumerate(partition_filtered(something, minsize=2), 1):
print(n, sorted(p))
print(time.time() - t)
Performance on x86 iMac:
Tool | Time |
---|---|
Python 3.7 | 157s |
PyPy 3.6 | 69s |
Codon (-release ) |
85s |
Is your license open source?
from C import sqrt(float) -> float as foo
print(sqrt(100.0)) # works, but should not have assigned name "sqrt"
Similar scenario when importing C variables.
tuple(0 for _ in [0])
Assert failed: type not set for (gen (int 0) (for '_.2 (stmt-expr ((assign '._cont_136 (stmt-expr ((suite (assign '._ctr_140 (call ('std.internal.types.ptr.List.__new__:0 #:type "std.internal.types.ptr.List.__new__:0") #:type "List[int]")) (expr (call ('std.internal.gc.register_finalizer #:type "std.internal.gc.register_finalizer:0[List[int]]") (('._ctr_140 #:type "List[int]") #:name 'p) #:type "TR")) (expr (call ('std.internal.types.ptr.List.__init__:4 #:type "std.internal.types.ptr.List.__init__:4[List[int],int]") (('._ctr_140 #:type "List[int]") #:name 'self)((int* 1 #:type "int") #:name 'capacity) #:type "TR")))) ('._ctr_140 #:type "List[int]") #:type "List[int]")) (expr (call* ('std.internal.types.ptr.List[int]:std.internal.types.ptr.List.append:0[std.internal.types.ptr.List[int],int]* #:type "std.internal.types.ptr.List.append:0[List[int],int]") (('._cont_136* #:type "List[int]") #:name 'self)((int* 0 #:type "int") #:name 'x) #:type "NoneType"))) ('._cont_136* #:type "List[int]") #:type "List[int]"))) [scratch.codon:1:1]
Expression: expr->type
Source: /Users/arshajii/Documents/workspace/codon/codon/parser/visitors/typecheck/typecheck.cpp:53
[1] 35617 abort build/codon run -release build/scratch.codon
class A:
a: int
def __init__(self):
self.a = 0
def __add__(self, other: A):
return A(0)
v = A()
@par(num_threads=2)
for a in [A(1)]:
v += a
Crashes Jupyter (but not with @tuple
)
I have some code which has two mutually recursive functions:
def A():
// under some condition
B()
def B():
// under some condition
A()
This is perfectly legitimate Python, but Codon can't compile it, as it wants B to be declared already at the point it is called in A.
Lists with None
s are not inferred to be lists of optionals unless the first element is None
:
['a', None] # type is List[str] ; fails at runtime with "optional is None"
[None, 'a'] # type is List[Optional[str]] ; works as expected
Also, comparing two None
s fails:
a, b = 0, 0
a = None
b = None
print(a == b) # error: optional is none ; should give True based on Python
I tried adding the following to Optional
class:
def __eq__(self, other: Optional[T]):
self_is_none = self is None
other_is_none = other is None
if self_is_none or other_is_none:
return self_is_none and other_is_none
return ~self == ~other
def __ne__(self, other: Optional[T]):
self_is_none = self is None
other_is_none = other is None
if self_is_none or other_is_none:
return not (self_is_none and other_is_none)
return ~self != ~other
def __eq__(self, other: T):
if self is None:
return False
return ~self == other
def __ne__(self, other: T):
if self is None:
return True
return ~self != other
but it seems to not have an effect (I guess this is handled beforehand in type checker).
After I successfully compiled a test python program using codon build <demo.py>
, on macOS Monterey (intel), I checked the dependencies as per the following:
$ otool -L demo
demo:
@rpath/libcodonrt.dylib (compatibility version 0.0.0, current version 0.0.0)
@rpath/libomp.dylib (compatibility version 5.0.0, current version 5.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0)
/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.11)
The two @rpath
dependencies, libcodonrt.dylib
and libomp.dylib
are included in the codon
distribution, they are 1.2M and 772K respectively so not so large.
Is there any thought to having static libraries of these two dependencies available so one may statically compile the executable instead to make it more relocatable?
for x in range(1):
(1 for _ in range(x))
gives
Assert failed: '"x.85.1495" value not found
Expression: varPtr
Source: /Users/arshajii/Documents/workspace/codon/codon/sir/llvm/llvisitor.cpp:1336
[1] 43399 abort build/codon build -llvm -release build/scratch.codon
I believe it's an issue on the parser side, but not 100% sure. Note that with lists ([1 for _ in range(x)]
) it works fine.
Example:
>>> Codon JIT v0.15.0 <<<
x
#%%
Assert failed: cannot find 'x' [<jit>:1:1]
Expression: val
Source: /Users/arshajii/Documents/workspace/codon/codon/parser/visitors/typecheck/access.cpp:37
[1] 4079 abort build/codon jit
Please add https://brew.sh/ installation method.
Directly unpacking @tuple classes with generics does not work:
# no generics
@tuple
class unpackable_plain:
a: int
b: str
u = unpackable_plain(1, 'str')
a, b = u # works
a, b = u.a, u.b # works
# generics
@tuple
class unpackable_gen:
a: int
b: T
T: type
u = unpackable_gen(1, 'str')
a, b = u # does not work
a, b = u.a, u.b # works
Hi, cool project.
I was just testing out the following example:
"""
demo module doctrings
"""
GLOBAL_VAR_A = 100
GLOBAL_VAR_B = "hello"
GLOBAL_VAR_C = 100.51
def foo(x,y):
"""foo function docstrings"""
return x+y
def bar(home):
"""bar function docstrings"""
return home
class Person:
"""Person class docstrings"""
def __init__(self, name, age):
self.name = name
self.age = age
class Vehicle:
"""Vehicle class docstrings"""
def __init__(self, year, owner):
self.year = year
self.owner = owner
person = Person('sam', 50)
fiesta = Vehicle(1997, person)
foo_results = foo(1,2)
bar_results = bar('hello')
and after trying to build it, I get the following error:
$ codon build demo.py
demo.py:27:9-13: error: 'Person' object has no attribute 'name'
╰─ demo.py:38:10-27: error: during the realization of __init__(self: Person, name: str, age: int)
Can you please explain what is going wrong here.
codon_test.py
a = input()
print(a)
codon run ./codon_test.py
dcodon_test.py:1:5-10: error: name 'input' is not defined
codon_test.py:2:7-8: error: name 'a' is not defined
Thank you for the new release. I was trying to install Codon package in Python, but it did not work on both MacOS 13 (with Intel CPU) and AlmaLinux 8.5 (AMD EPYC 7352). I got similar error:
on macOS:
ERROR: codon-0.15.1-cp39-cp39-macosx_10_16_x86_64.whl is not a supported wheel on this platform
on Linux:
ERROR: codon-0.15.1-cp36-cp36m-manylinux2014_x86_64.whl is not a supported wheel on this platform
The error seems to be because of a new version Python was used. Could you also make the package available for Python3.8-3.11?
Thank you!
def f(x: int):
pass
f(0)
@overload
def f(x: str):
pass
gives
Assert failed: type is null [scratch.codon:4:1]
Expression: type
Source: /Users/arshajii/Documents/workspace/codon/codon/parser/visitors/typecheck/ctx.cpp:84
[1] 71722 abort build/codon run -release build/scratch.codon
Moving the call f(0)
after the two function definitions makes it work as expected.
def foo(x):
if isinstance(x, str):
return x.upper()
return x + 1
foo('hello') # error: cannot find magic 'add' in str
foo(10)
f'"\t".join([a for a in b])'
takes ~2 minutes to parse. When no f-strings present it is instant.
def f(x):
return f([x])
f(1)
I didn't find any discussion group.
I am reading about Codon's C/C++ integration and it seems that there is a need to manually translate
@export
def foo(n: int):
into
int64_t foo(int64_t);
It would be nice, if
codon build -o libfoo.so foo.codon
automatically generated C/C++ header file, that could be included from C/C++ code:
#include "foo.h"
Go language has this functionality. Running
go build -o libfoo.so -buildmode=c-shared foo.go
will generate foo.h
as well as libfoo.so
.
def f(x: Static[int]):
print('int', x)
@overload
def f(x: Static[str]):
print('str', x)
f(10)
gives
test.codon:9:1: error: cannot unify 10 and x
Also having multiple versions of the same method in a class with static arguments does not work.
I have a Python script which runs fine when using Python 3.8.5:
from typing import Tuple
def combine(a: int, b: int) -> Tuple[int, int]:
return (a, b)
print('Tuple from 1 and 2:', combine(1, 2))
When running codon run test.py
(0.15.1), I get an error: test.py:1:6-12: error: no module named 'typing'
.
If I remove the first line it works just fine.
Is it not possible to import and use the typing
module? I want my script to be both executable by codon and Python itself.
class A:
def __init__(self):
pass
class B(A):
def __init__(self):
super.__init__()
B()
gives
Assert failed: cannot find IdExpr 'super' (scratch.codon:84:9)
Expression: val
Source: /Users/arshajii/Documents/workspace/codon/codon/parser/visitors/typecheck/typecheck_expr.cpp:122
zsh: abort build/codon run -release build/scratch.codon
This code:
from time import time
t0 = time()
n = 0
with open('/Users/arshajii/Documents/data/news/all-the-news-2-1.csv') as f:
for line in f:
if 'MSFT' in line:
n += 1
t1 = time()
print('took', t1 - t0, 'seconds')
print(n)
takes
probably due to algorithmic differences in the string search function. We should review these functions and update them to use whatever (fast) algorithm Python uses.
FYI this is what Python does: https://github.com/python/cpython/blob/main/Objects/stringlib/fastsearch.h
Expressions and lambdas that have no return type fail at compile time, even though they should work as they work in python:
def a():
pass
# lamda error
b = lambda: a()
b()
# expression error
a() if True else a()
Both raise error: expression with void type
install via bash, run
codon run test.py
get error
dyld[67860]: Library not loaded: '/opt/homebrew/opt/zstd/lib/libzstd.1.dylib'
Referenced from: '/Users/xxx/.codon/lib/codon/libcodonc.dylib'
Reason: tried: '/opt/homebrew/opt/zstd/lib/libzstd.1.dylib' (no such file), '/usr/local/lib/libzstd.1.dylib' (no such file), '/usr/lib/libzstd.1.dylib' (no such file)
zsh: abort test.py
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.