{"id":21962201,"url":"https://github.com/antoniotejada/epycc","last_synced_at":"2026-01-23T18:32:28.064Z","repository":{"id":130016284,"uuid":"396960306","full_name":"antoniotejada/epycc","owner":"antoniotejada","description":"C99 just in time Python embeddable compiler with no external compiler dependencies so you can seamlessly use fast/existing C code from Python","archived":false,"fork":false,"pushed_at":"2021-09-10T04:31:00.000Z","size":888,"stargazers_count":9,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-23T21:36:32.009Z","etag":null,"topics":["c","c99","compiler","jit","lark","lark-parser","llvm","llvmlite","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/antoniotejada.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-08-16T20:26:50.000Z","updated_at":"2024-09-03T13:37:30.000Z","dependencies_parsed_at":null,"dependency_job_id":"bae451f0-e178-4853-ac0b-835b3049abae","html_url":"https://github.com/antoniotejada/epycc","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/antoniotejada/epycc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antoniotejada%2Fepycc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antoniotejada%2Fepycc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antoniotejada%2Fepycc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antoniotejada%2Fepycc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/antoniotejada","download_url":"https://codeload.github.com/antoniotejada/epycc/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antoniotejada%2Fepycc/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28697428,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-23T17:25:48.045Z","status":"ssl_error","status_checked_at":"2026-01-23T17:25:47.153Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","c99","compiler","jit","lark","lark-parser","llvm","llvmlite","python"],"created_at":"2024-11-29T10:35:19.042Z","updated_at":"2026-01-23T18:32:28.046Z","avatar_url":"https://github.com/antoniotejada.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Epycc: Embedded Python C Compiler\r\n\r\nEmbedded Python C Compiler is a Python 2.7 module that allows JIT compiling and invoking C code seamlessly from Python, as if it was a Python function, but at native non-interpreted execution speeds (faster than numpy or numba, see [Performance](#performance) below).\r\n\r\n```python\r\nc_code = \"\"\"\r\nfloat f2pow2(int a) {\r\n    return 2.0f * (a * a);\r\n}\r\n\"\"\"\r\nlib = epycc_compile(c_code)\r\nprint lib.f2pow2(2)\r\n```\r\n```python\r\n8.0\r\n```\r\n\r\n## Current functionality\r\n- [x] Parse all C99 code (only missing C lexer hack)\r\n- [x] Generate IR for floating point and integer expressions, casts\r\n- [x] Generate IR for function definitions\r\n- [x] Generate IR for assigning / reading to / from function parameters and local scalar variables\r\n- [x] Generate IR for if then / else statements\r\n- [x] Generate IR for for/while continue/break statements\r\n- [x] Generate IR for internal function calls, forward function declarations, direct and indirect recursive functions\r\n- [x] Generate IR for arrays (open, runtime, and compile time sized)\r\n- [x] Generate IR for structs, arrays of structs, structs of arrays\r\n- [x] Execute generated IR seamlessly like a Python function\r\n- [x] \"ctypable\" transparent Python parameter passing support, including converting Python lists to C arrays under the hood\r\n\r\nCheck the [tests directory](tests/cfiles) for examples of the currently supported constructs.\r\n\r\n\r\n## Future functionality\r\n- [ ] Generate IR for switch statements\r\n- [ ] Generate IR for pointers, sizeof, addressof operator\r\n- [ ] Generate IR for unions, user defined types, bitfields\r\n- [ ] Generate IR for vararg functions\r\n- [ ] Generate IR for global variables/constants\r\n- [ ] Generate IR for global constructors (via llvm.global_ctors or manually)\r\n- [ ] Parse lexer hack\r\n- [ ] Assembler support\r\n- [ ] Widely used compiler-specific pragma/attributes/declspec (thread, packed, aligned...). See https://clang.llvm.org/docs/AttributeReference.html\r\n- [ ] Packaging into a proper Python package\r\n- [ ] Publishing to Pypi\r\n- [ ] External native function calling from inside C\r\n- [ ] Python function calling from inside C\r\n- [ ] Spilling generated IR or executable to disk for distribution\r\n- [ ] C runtime, invoking Python's in-process loaded runtime via external native function calls\r\n- [ ] C preprocessor, include file support\r\n- [ ] Compile arbitrary C sources (and call external DLL/so functions)\r\n- [ ] Python 3.x compatible\r\n\r\n\r\n# Implementation details\r\n- C99 grammar straight and unmodified from the 9899:1999 spec\r\n- Clang for precompiling C code into IR snippets that get called internally.\r\n- Generated code validation via comparison vs. clang-generated code\r\n- [Lark](https://github.com/lark-parser/lark) for parsing\r\n- [llvmlite](https://github.com/numba/llvmlite/) for JIT compiling LLVM IR into executable code.\r\n\r\n\r\n## Simple Example\r\n\r\n\r\n### Original C code\r\n\r\n```python\r\nc_code = \"\"\"\r\nfloat f2pow2(int a) {\r\n    return 2.0f * (a * a);\r\n}\r\n\"\"\"\r\nlib = epycc_compile(c_code)\r\nprint lib.f2pow2(2)\r\n```\r\n```python\r\n8.0\r\n```\r\n\r\nInternally it generates LLVM IR and uses [llvmlite](https://github.com/numba/llvmlite/) to JIT compile it into executable machine code in memory.\r\n\r\nThe generated LLVM IR code calls into LLVM IR snippets pregenerated from C code. This is in order to accelerate epycc development and perform some brittle tasks like C99-compliant type conversion, etc:\r\n\r\n```python\r\nprint lib.ir\r\n```\r\n```LLVM\r\n; ModuleID = '\u003cstring\u003e'\r\nsource_filename = \"\u003cstring\u003e\"\r\ntarget datalayout = \"e-m:e-i64:64-f80:128-n8:16:32:64-S128\"\r\n\r\ndefine float @f2pow2(i32 %.1) {\r\nentry:\r\n  %.3 = alloca i32\r\n  store i32 %.1, i32* %.3\r\n  %.5 = load i32, i32* %.3\r\n  %.6 = load i32, i32* %.3\r\n  %.7 = call i32 @mul__int__int__int(i32 %.5, i32 %.6)\r\n  %.8 = call float @cnv__float__int(i32 %.7)\r\n  %.9 = call float @mul__float__float__float(float 2.000000e+00, float %.8)\r\n  ret float %.9\r\n}\r\n\r\ndefine dso_local float @cnv__float__int(i32) {\r\n  %2 = alloca i32, align 4\r\n  store i32 %0, i32* %2, align 4\r\n  %3 = load i32, i32* %2, align 4\r\n  %4 = sitofp i32 %3 to float\r\n  ret float %4\r\n}\r\n\r\ndefine dso_local float @mul__float__float__float(float, float) {\r\n  %3 = alloca float, align 4\r\n  %4 = alloca float, align 4\r\n  store float %1, float* %3, align 4\r\n  store float %0, float* %4, align 4\r\n  %5 = load float, float* %4, align 4\r\n  %6 = load float, float* %3, align 4\r\n  %7 = fmul float %5, %6\r\n  ret float %7\r\n}\r\n\r\ndefine dso_local i32 @mul__int__int__int(i32, i32) {\r\n  %3 = alloca i32, align 4\r\n  %4 = alloca i32, align 4\r\n  store i32 %1, i32* %3, align 4\r\n  store i32 %0, i32* %4, align 4\r\n  %5 = load i32, i32* %4, align 4\r\n  %6 = load i32, i32* %3, align 4\r\n  %7 = mul nsw i32 %5, %6\r\n  ret i32 %7\r\n}\r\n\r\n; Function Attrs: nounwind\r\ndeclare void @llvm.stackprotector(i8*, i8**) #0\r\n\r\nattributes #0 = { nounwind }\r\n```\r\n\r\nNote using those snippets doesn't suppose any performance issues because LLVM optimizes them away inlining the calls and removing any unnecessary load/stores:\r\n\r\n```python\r\nprint lib.ir_optimized\r\n```\r\n```LLVM\r\n; ModuleID = '\u003cstring\u003e'\r\nsource_filename = \"\u003cstring\u003e\"\r\ntarget datalayout = \"e-m:e-i64:64-f80:128-n8:16:32:64-S128\"\r\n\r\n; Function Attrs: norecurse nounwind readnone\r\ndefine float @f2pow2(i32 %.1) local_unnamed_addr #0 {\r\nentry:\r\n  %0 = mul nsw i32 %.1, %.1\r\n  %1 = sitofp i32 %0 to float\r\n  %2 = fmul float %1, 2.000000e+00\r\n  ret float %2\r\n}\r\n\r\n; Function Attrs: norecurse nounwind readnone\r\ndefine dso_local float @cnv__float__int(i32) local_unnamed_addr #0 {\r\n  %2 = sitofp i32 %0 to float\r\n  ret float %2\r\n}\r\n\r\n; Function Attrs: norecurse nounwind readnone\r\ndefine dso_local float @mul__float__float__float(float, float) local_unnamed_addr #0 {\r\n  %3 = fmul float %0, %1\r\n  ret float %3\r\n}\r\n\r\n; Function Attrs: norecurse nounwind readnone\r\ndefine dso_local i32 @mul__int__int__int(i32, i32) local_unnamed_addr #0 {\r\n  %3 = mul nsw i32 %1, %0\r\n  ret i32 %3\r\n}\r\n\r\nattributes #0 = { norecurse nounwind readnone }\r\n```\r\n\r\n```python\r\nprint lib.asm_optimized\r\n```\r\n```assembly\r\n\t.text\r\n\t.intel_syntax noprefix\r\n\t.file\t\"\u003cstring\u003e\"\r\n\t.globl\tf2pow2\r\n\t.p2align\t4, 0x90\r\n\t.type\tf2pow2,@function\r\nf2pow2:\r\n\timul\tecx, ecx\r\n\tcvtsi2ss\txmm0, ecx\r\n\taddss\txmm0, xmm0\r\n\tret\r\n.Lfunc_end0:\r\n\t.size\tf2pow2, .Lfunc_end0-f2pow2\r\n\r\n\t.globl\tcnv__float__int\r\n\t.p2align\t4, 0x90\r\n\t.type\tcnv__float__int,@function\r\ncnv__float__int:\r\n\tcvtsi2ss\txmm0, ecx\r\n\tret\r\n.Lfunc_end1:\r\n\t.size\tcnv__float__int, .Lfunc_end1-cnv__float__int\r\n\r\n\t.globl\tmul__float__float__float\r\n\t.p2align\t4, 0x90\r\n\t.type\tmul__float__float__float,@function\r\nmul__float__float__float:\r\n\tmulss\txmm0, xmm1\r\n\tret\r\n.Lfunc_end2:\r\n\t.size\tmul__float__float__float, .Lfunc_end2-mul__float__float__float\r\n\r\n\t.globl\tmul__int__int__int\r\n\t.p2align\t4, 0x90\r\n\t.type\tmul__int__int__int,@function\r\nmul__int__int__int:\r\n\tmov\teax, ecx\r\n\timul\teax, edx\r\n\tret\r\n.Lfunc_end3:\r\n\t.size\tmul__int__int__int, .Lfunc_end3-mul__int__int__int\r\n\r\n\r\n\t.section\t\".note.GNU-stack\",\"\",@progbits\r\n```\r\n\r\nSince the module includes a full featured C parser, it can also be used to parse and inspect C code.\r\n\r\n## A more complex example\r\n### Original C code\r\n```c\r\nint fforif(int a, int b) {\r\n    int s = 0;\r\n    for (int i = 0; i \u003c a; i += 1) {\r\n        if (a \u003e b) {\r\n            s += b;\r\n        } else {\r\n            s += a;\r\n        }\r\n    }\r\n    \r\n    return s;\r\n}\r\n```\r\n### Generated LLVM IR (unoptimized)\r\n```LLVM\r\ndefine i32 @fifforf(i32 %.1, i32 %.2) {\r\nentry:\r\n  %.4 = alloca i32\r\n  %.5 = load i32, i32* %.4\r\n  store i32 0, i32* %.4\r\n  %.7 = alloca i32\r\n  store i32 %.1, i32* %.7\r\n  %.9 = load i32, i32* %.7\r\n  %.10 = alloca i32\r\n  store i32 %.2, i32* %.10\r\n  %.12 = load i32, i32* %.10\r\n  %.13 = call i32 @gt__int__int__int(i32 %.9, i32 %.12)\r\n  %.14 = call i1 @cnv___Bool__int(i32 %.13)\r\n  %.16 = alloca i32\r\n  %.37 = alloca i32\r\n  br i1 %.14, label %entry.if, label %entry.else\r\n\r\nentry.if:                                         ; preds = %entry\r\n  %.17 = load i32, i32* %.16\r\n  store i32 0, i32* %.16\r\n  br label %forcond\r\n\r\nentry.else:                                       ; preds = %entry\r\n  %.38 = load i32, i32* %.37\r\n  store i32 0, i32* %.37\r\n  br label %forcond.1\r\n\r\nentry.endif:                                      ; preds = %forcond.1, %forcond\r\n  %.58 = load i32, i32* %.4\r\n  ret i32 %.58\r\n\r\nforcond:                                          ; preds = %forbody, %entry.if\r\n  %.20 = load i32, i32* %.16\r\n  %.21 = load i32, i32* %.7\r\n  %.22 = call i32 @lt__int__int__int(i32 %.20, i32 %.21)\r\n  %.23 = call i1 @cnv___Bool__int(i32 %.22)\r\n  br i1 %.23, label %forbody, label %entry.endif\r\n\r\nforbody:                                          ; preds = %forcond\r\n  %.30 = load i32, i32* %.10\r\n  %.31 = load i32, i32* %.4\r\n  %.32 = call i32 @add__int__int__int(i32 %.31, i32 %.30)\r\n  %.33 = load i32, i32* %.4\r\n  store i32 %.32, i32* %.4\r\n  %.25 = load i32, i32* %.16\r\n  %.26 = call i32 @add__int__int__int(i32 %.25, i32 1)\r\n  %.27 = load i32, i32* %.16\r\n  store i32 %.26, i32* %.16\r\n  br label %forcond\r\n\r\nforcond.1:                                        ; preds = %forbody.1, %entry.else\r\n  %.41 = load i32, i32* %.37\r\n  %.42 = load i32, i32* %.10\r\n  %.43 = call i32 @lt__int__int__int(i32 %.41, i32 %.42)\r\n  %.44 = call i1 @cnv___Bool__int(i32 %.43)\r\n  br i1 %.44, label %forbody.1, label %entry.endif\r\n\r\nforbody.1:                                        ; preds = %forcond.1\r\n  %.51 = load i32, i32* %.7\r\n  %.52 = load i32, i32* %.4\r\n  %.53 = call i32 @add__int__int__int(i32 %.52, i32 %.51)\r\n  %.54 = load i32, i32* %.4\r\n  store i32 %.53, i32* %.4\r\n  %.46 = load i32, i32* %.37\r\n  %.47 = call i32 @add__int__int__int(i32 %.46, i32 1)\r\n  %.48 = load i32, i32* %.37\r\n  store i32 %.47, i32* %.37\r\n  br label %forcond.1\r\n}\r\n\r\ndefine dso_local i32 @gt__int__int__int(i32, i32) {\r\n  %3 = alloca i32, align 4\r\n  %4 = alloca i32, align 4\r\n  store i32 %1, i32* %3, align 4\r\n  store i32 %0, i32* %4, align 4\r\n  %5 = load i32, i32* %4, align 4\r\n  %6 = load i32, i32* %3, align 4\r\n  %7 = icmp sgt i32 %5, %6\r\n  %8 = zext i1 %7 to i32\r\n  ret i32 %8\r\n}\r\n\r\ndefine dso_local zeroext i1 @cnv___Bool__int(i32) {\r\n  %2 = alloca i32, align 4\r\n  store i32 %0, i32* %2, align 4\r\n  %3 = load i32, i32* %2, align 4\r\n  %4 = icmp ne i32 %3, 0\r\n  ret i1 %4\r\n}\r\n\r\ndefine dso_local i32 @add__int__int__int(i32, i32) {\r\n  %3 = alloca i32, align 4\r\n  %4 = alloca i32, align 4\r\n  store i32 %1, i32* %3, align 4\r\n  store i32 %0, i32* %4, align 4\r\n  %5 = load i32, i32* %4, align 4\r\n  %6 = load i32, i32* %3, align 4\r\n  %7 = add nsw i32 %5, %6\r\n  ret i32 %7\r\n}\r\n\r\ndefine dso_local i32 @lt__int__int__int(i32, i32) {\r\n  %3 = alloca i32, align 4\r\n  %4 = alloca i32, align 4\r\n  store i32 %1, i32* %3, align 4\r\n  store i32 %0, i32* %4, align 4\r\n  %5 = load i32, i32* %4, align 4\r\n  %6 = load i32, i32* %3, align 4\r\n  %7 = icmp slt i32 %5, %6\r\n  %8 = zext i1 %7 to i32\r\n  ret i32 %8\r\n}\r\n```\r\n#### Control Flow Graph\r\n![Unoptimized Control Flow Graph](fforif.dot.png)\r\n### LLVM IR After LLVM Optimization\r\n```LLVM\r\ndefine i32 @fifforf(i32 %.1, i32 %.2) local_unnamed_addr #0 {\r\nentry:\r\n  %0 = icmp sgt i32 %.1, %.2\r\n  br i1 %0, label %forcond.preheader, label %forcond.1.preheader\r\n\r\nforcond.1.preheader:                              ; preds = %entry\r\n  %1 = icmp sgt i32 %.2, 0\r\n  %2 = mul i32 %.2, %.1\r\n  %spec.select = select i1 %1, i32 %2, i32 0\r\n  ret i32 %spec.select\r\n\r\nforcond.preheader:                                ; preds = %entry\r\n  %3 = icmp sgt i32 %.1, 0\r\n  %4 = mul i32 %.2, %.1\r\n  %spec.select10 = select i1 %3, i32 %4, i32 0\r\n  ret i32 %spec.select10\r\n}\r\n```\r\n### Control Flow Graph\r\n![Optimized Control Flow Graph](fforif.optimized.dot.png)\r\n\r\n\r\n# Performance \r\n\r\n![image](https://user-images.githubusercontent.com/6446344/132799399-34a22b8b-c174-4fec-9b9a-d9debdd24bde.png)\r\n\r\nThe graph shows the performance of transforming an increasing number of 4D vectors by a 4x4 matrix, using python, pyrr, numba, numpy and epycc. See [test_perf.py](tests/test_perf.py)\r\n\r\nWhere\r\n- no params: Empty function called with no arguments.\r\n- params only: Empty function called using the same arguments as the full function call.\r\n- numpy: Calling the function using numpy arrays as arguments.\r\n- ctypes: Calling the function using ctypes as arguments.\r\n- tuple: Calling the function using Python tuples (but for the output which has to be list since tuples are immutable)\r\n- list: Calling the function using Python lists\r\n\r\n## Summary\r\n- Epycc is the fastest in performing the calculation, 1.5x the speed of numpy, 40x the speed of numba, and 700x the speed of Python\r\n- When the input parameters are already numpy arrays, numpy is 2x the speed of Epycc due to Epycc having to convert them to ctypes. This overhead is constant so there's a breaking point beyond the 1000 vertex count tested here where Epycc is faster than numpy.\r\n- Epycc seamlessly accepts Python tuples and lists, but converting them to ctypes is very slow (Epycc using ctypes directly is 1700x faster than using lists, and 1000x faster than using tuples)\r\n- Numba call overhead is very low, 1.5x the speed of calling a Python function and 2x the speed of calling an Epycc function\r\n- Numba parameter passing overhead using numpy arrays is very low, 15x the speed of Epycc using numpy, and 1.5x the speed of Epycc using ctypes.\r\n- Numba calculation time slowness is due to inefficient code generation (no inlining, no unrolling) and refcount overhead. When both Epycc and Numba use numpy arrays for parameters, Epycc is 15x the speed of Numba.\r\n- Pyrr is the same speed as Python despite using numpy, since apply_to_vector cannot do matrix by array of vector multiplication and has to be done manually in a Python loop (or use numpy, but at that point you might as well just use numpy, see https://github.com/adamlwgriffiths/Pyrr/issues/106)\r\n\r\n\r\n## Details\r\n\r\nThis study looks at:\r\n- The numba, python and Epycc function call overhead\r\n- The numba and Epycc parameter passing overhead for different parameter types.\r\n- The numba, python, Epycc and numpy execution speed of transforming N 4D vectors by a 4x4 matrix\r\n\r\n### Package Versions Used\r\n- python 2.7\r\n- numba 0.47.0\r\n- llvmlite 0.31.0\r\n- numpy 1.13.3\r\n- pyrr 0.10.3\r\n\r\n### Sources\r\nepycc code (c99)\r\n```c\r\nfloat dot_prod_vector(float v[4], float w[4])\r\n{\r\n    float res = 0.0f;\r\n    for (int i = 0; i \u003c 4; ++i)\r\n    {\r\n        res += v[i] * w[i];\r\n    }\r\n    return res;\r\n}\r\n\r\nvoid transform_vector(float mat[4][4], float in[4], float out[4])\r\n{\r\n    for (int i = 0; i \u003c 4; ++i)\r\n    {\r\n        out[i] = dot_prod_vector(mat[i], in);\r\n    }\r\n\r\n}\r\n\r\nvoid transform_vectors(float matrix[4][4], float in[][4], float out[][4], int count) {\r\n    for (int i = 0; i \u003c count; ++i) \r\n    {\r\n        transform_vector(matrix, in[i], out[i]);\r\n    }\r\n}\r\nvoid transform_vectors_empty(float matrix[4][4], float in[][4], float out[][4], int count) {\r\n    return;\r\n}\r\nvoid empty() {\r\n}\r\n```\r\n\r\nPython code\r\n```python\r\ndef dot_prod(v, w):\r\n    return sum([vi * wi for vi, wi in zip(v, w)])\r\n    \r\ndef transform_vector(mat, v):\r\n    return [dot_prod(row, v) for row in mat]\r\n\r\ndef transform_vectors(mat, vs, vsout):\r\n    vsout[:] = [transform_vector(mat, v) for v in vs]\r\n    \r\ndef python_empty():\r\n    pass\r\n```\r\n\r\nNumba code\r\n```python\r\n@nb.njit\r\ndef nb_dot_prod(v, w):\r\n    # Numba cannot determine the type of sum, switch to loop\r\n    # return np.sum([vi * wi for vi, wi in zip(v, w)])\r\n\r\n    # This needs to be specified as float32 so the result is accumulated in \r\n    # float32, even if the arguments are already float32\r\n    res = np.float32(0)\r\n    for vi, wi in zip(v, w):\r\n        res += vi * wi\r\n\r\n    return res\r\n\r\n@nb.njit\r\ndef nb_transform_vector(mat, v, vout):\r\n    # Numba errors out with\r\n    # \"Direct iteration is not supported for arrays with dimension \u003e 1\"\r\n    # Need to use array indexing instead of element traversing\r\n    for i in xrange(len(mat)):\r\n        vout[i] = nb_dot_prod(mat[i], v)\r\n\r\n\r\n@nb.njit\r\ndef nb_transform_vectors(mat, vs, vsout):\r\n    # Numba errors out with\r\n    # \"Direct iteration is not supported for arrays with dimension \u003e 1\"\r\n    # Need to use array indexing instead of element traversing\r\n    # To get really good performance, don't let numba generate python lists and\r\n    # pass arrays for source and destination\r\n    for i in xrange(len(vs)):\r\n        nb_transform_vector(mat, vs[i], vsout[i])\r\n\r\n@nb.njit\r\ndef nb_transform_vectors_empty(mat, vs, vsout):\r\n    pass\r\n\r\n@nb.njit\r\ndef nb_empty():\r\n    pass\r\n```\r\n\r\nNumpy code\r\n```python\r\nnpvectors.dot(npmatrix.T, npvectors_out)\r\n```\r\n### Call Overhead\r\n\r\n![image](https://user-images.githubusercontent.com/6446344/132799455-7538b52e-8edd-41b2-9e5d-0ff634cb6c5e.png)\r\n\r\n\r\n### Parameter Overhead\r\n\r\n![image](https://user-images.githubusercontent.com/6446344/132799485-91e247bb-a321-4e8a-83e9-d205611084ad.png)\r\n\r\n![image](https://user-images.githubusercontent.com/6446344/132799523-7c7d9b59-bb28-494f-aaa1-e119b502ab61.png)\r\n\r\n![image](https://user-images.githubusercontent.com/6446344/132799542-96499996-4d35-4412-9afa-9f51e05632c1.png)\r\n\r\n\r\n### Full Call\r\n\r\n![image](https://user-images.githubusercontent.com/6446344/132799559-63a7c8b5-6612-4c04-b2ec-c5939bfa9ea0.png)\r\n\r\n![image](https://user-images.githubusercontent.com/6446344/132799565-213efc98-6185-4558-b7d1-9707d9260731.png)\r\n\r\n![image](https://user-images.githubusercontent.com/6446344/132799580-81f7082a-7b16-455a-a026-a86d666d81fe.png)\r\n\r\n\r\n### Numba vs. Epycc codegen\r\n\r\n#### Epycc\r\n\r\n```assembly\r\n\t.text\r\n\t.intel_syntax noprefix\r\n\t.file\t\"\u003cstring\u003e\"\r\n\t.globl\ttransform_vectors\r\n\t.p2align\t4, 0x90\r\n\t.type\ttransform_vectors,@function\r\ntransform_vectors:\r\n\ttest\tr9d, r9d\r\n\tjle\t.LBB2_3\r\n\tmov\tr9d, r9d\r\n\tmov\teax, 12\r\n\txorps\txmm0, xmm0\r\n\t.p2align\t4, 0x90\r\n.LBB2_2:\r\n\tmovss\txmm1, dword ptr [rcx]\r\n\tmovss\txmm2, dword ptr [rcx + 4]\r\n\tmulss\txmm1, dword ptr [rdx + rax - 12]\r\n\taddss\txmm1, xmm0\r\n\tmulss\txmm2, dword ptr [rdx + rax - 8]\r\n\taddss\txmm2, xmm1\r\n\tmovss\txmm1, dword ptr [rcx + 8]\r\n\tmulss\txmm1, dword ptr [rdx + rax - 4]\r\n\taddss\txmm1, xmm2\r\n\tmovss\txmm2, dword ptr [rcx + 12]\r\n\tmulss\txmm2, dword ptr [rdx + rax]\r\n\taddss\txmm2, xmm1\r\n\tmovss\tdword ptr [r8 + rax - 12], xmm2\r\n\tmovss\txmm1, dword ptr [rcx + 16]\r\n\tmovss\txmm2, dword ptr [rcx + 20]\r\n\tmulss\txmm1, dword ptr [rdx + rax - 12]\r\n\taddss\txmm1, xmm0\r\n\tmulss\txmm2, dword ptr [rdx + rax - 8]\r\n\taddss\txmm2, xmm1\r\n\tmovss\txmm1, dword ptr [rcx + 24]\r\n\tmulss\txmm1, dword ptr [rdx + rax - 4]\r\n\taddss\txmm1, xmm2\r\n\tmovss\txmm2, dword ptr [rcx + 28]\r\n\tmulss\txmm2, dword ptr [rdx + rax]\r\n\taddss\txmm2, xmm1\r\n\tmovss\tdword ptr [r8 + rax - 8], xmm2\r\n\tmovss\txmm1, dword ptr [rcx + 32]\r\n\tmovss\txmm2, dword ptr [rcx + 36]\r\n\tmulss\txmm1, dword ptr [rdx + rax - 12]\r\n\taddss\txmm1, xmm0\r\n\tmulss\txmm2, dword ptr [rdx + rax - 8]\r\n\taddss\txmm2, xmm1\r\n\tmovss\txmm1, dword ptr [rcx + 40]\r\n\tmulss\txmm1, dword ptr [rdx + rax - 4]\r\n\taddss\txmm1, xmm2\r\n\tmovss\txmm2, dword ptr [rcx + 44]\r\n\tmulss\txmm2, dword ptr [rdx + rax]\r\n\taddss\txmm2, xmm1\r\n\tmovss\tdword ptr [r8 + rax - 4], xmm2\r\n\tmovss\txmm1, dword ptr [rcx + 48]\r\n\tmovss\txmm2, dword ptr [rcx + 52]\r\n\tmulss\txmm1, dword ptr [rdx + rax - 12]\r\n\taddss\txmm1, xmm0\r\n\tmulss\txmm2, dword ptr [rdx + rax - 8]\r\n\taddss\txmm2, xmm1\r\n\tmovss\txmm1, dword ptr [rcx + 56]\r\n\tmulss\txmm1, dword ptr [rdx + rax - 4]\r\n\taddss\txmm1, xmm2\r\n\tmovss\txmm2, dword ptr [rcx + 60]\r\n\tmulss\txmm2, dword ptr [rdx + rax]\r\n\taddss\txmm2, xmm1\r\n\tmovss\tdword ptr [r8 + rax], xmm2\r\n\tadd\trax, 16\r\n\tdec\tr9\r\n\tjne\t.LBB2_2\r\n.LBB2_3:\r\n\tret\r\n.Lfunc_end2:\r\n\t.size\ttransform_vectors, .Lfunc_end2-transform_vectors\r\n\r\n```\r\n#### Numba\r\n```assembly\r\n\t.text\r\n\t.intel_syntax noprefix\r\n\t.file\t\"\u003cstring\u003e\"\r\n\t.globl\t_ZN8__main__24nb_transform_vectors$243E5ArrayIfLi2E1C7mutable7alignedE5ArrayIfLi2E1C7mutable7alignedE5ArrayIfLi2E1C7mutable7alignedE\r\n\t.p2align\t4, 0x90\r\n\t.type\t_ZN8__main__24nb_transform_vectors$243E5ArrayIfLi2E1C7mutable7alignedE5ArrayIfLi2E1C7mutable7alignedE5ArrayIfLi2E1C7mutable7alignedE,@function\r\n_ZN8__main__24nb_transform_vectors$243E5ArrayIfLi2E1C7mutable7alignedE5ArrayIfLi2E1C7mutable7alignedE5ArrayIfLi2E1C7mutable7alignedE:\r\n\t.cfi_startproc\r\n\tpush\tr15\r\n\t.cfi_def_cfa_offset 16\r\n\tpush\tr14\r\n\t.cfi_def_cfa_offset 24\r\n\tpush\tr13\r\n\t.cfi_def_cfa_offset 32\r\n\tpush\tr12\r\n\t.cfi_def_cfa_offset 40\r\n\tpush\trsi\r\n\t.cfi_def_cfa_offset 48\r\n\tpush\trdi\r\n\t.cfi_def_cfa_offset 56\r\n\tpush\trbp\r\n\t.cfi_def_cfa_offset 64\r\n\tpush\trbx\r\n\t.cfi_def_cfa_offset 72\r\n\tsub\trsp, 264\r\n\t.cfi_def_cfa_offset 336\r\n\t.cfi_offset rbx, -72\r\n\t.cfi_offset rbp, -64\r\n\t.cfi_offset rdi, -56\r\n\t.cfi_offset rsi, -48\r\n\t.cfi_offset r12, -40\r\n\t.cfi_offset r13, -32\r\n\t.cfi_offset r14, -24\r\n\t.cfi_offset r15, -16\r\n\tmov\tqword ptr [rsp + 256], r9\r\n\tmov\tqword ptr [rsp + 232], rdx\r\n\tmov\tqword ptr [rsp + 240], rcx\r\n\tmov\trsi, qword ptr [rsp + 496]\r\n\tmov\tr14, qword ptr [rsp + 464]\r\n\tmov\trbx, qword ptr [rsp + 424]\r\n\tmov\tqword ptr [rsp + 224], 0\r\n\tmov\tqword ptr [rsp + 216], 0\r\n\tmovabs\trdi, offset NRT_incref\r\n\tmov\tqword ptr [rsp + 208], r8\r\n\tmov\trcx, r8\r\n\tcall\trdi\r\n\tmov\trcx, rbx\r\n\tcall\trdi\r\n\tmov\trcx, rsi\r\n\tcall\trdi\r\n\ttest\tr14, r14\r\n\tjle\t.LBB0_7\r\n\tmov\trax, qword ptr [rsp + 544]\r\n\tmov\trbp, qword ptr [rsp + 528]\r\n\tmov\trcx, qword ptr [rsp + 472]\r\n\tmov\trdi, qword ptr [rsp + 456]\r\n\tlea\trax, [4*rax]\r\n\tmov\tqword ptr [rsp + 248], rax\r\n\tlea\tr13, [4*rcx]\r\n\tmovabs\tr12, offset NRT_decref\r\n\tmovabs\tr15, offset NRT_incref\r\n\t.p2align\t4, 0x90\r\n.LBB0_2:\r\n\tmov\trcx, rbx\r\n\tcall\tr15\r\n\tmov\trcx, rsi\r\n\tcall\tr15\r\n\tmov\tqword ptr [rsp + 224], 0\r\n\tmov\trax, qword ptr [rsp + 560]\r\n\tmov\tqword ptr [rsp + 192], rax\r\n\tmov\trax, qword ptr [rsp + 544]\r\n\tmov\tqword ptr [rsp + 184], rax\r\n\tmov\tqword ptr [rsp + 176], rbp\r\n\tmov\trcx, qword ptr [rsp + 520]\r\n\tmov\tqword ptr [rsp + 168], rcx\r\n\tmov\tqword ptr [rsp + 160], rax\r\n\tmov\trax, qword ptr [rsp + 504]\r\n\tmov\tqword ptr [rsp + 152], rax\r\n\tmov\tqword ptr [rsp + 144], rsi\r\n\tmov\trax, qword ptr [rsp + 488]\r\n\tmov\tqword ptr [rsp + 136], rax\r\n\tmov\trax, qword ptr [rsp + 472]\r\n\tmov\tqword ptr [rsp + 128], rax\r\n\tmov\tqword ptr [rsp + 120], rdi\r\n\tmov\trcx, qword ptr [rsp + 448]\r\n\tmov\tqword ptr [rsp + 112], rcx\r\n\tmov\tqword ptr [rsp + 104], rax\r\n\tmov\trax, qword ptr [rsp + 432]\r\n\tmov\tqword ptr [rsp + 96], rax\r\n\tmov\tqword ptr [rsp + 88], rbx\r\n\tmov\trax, qword ptr [rsp + 416]\r\n\tmov\tqword ptr [rsp + 80], rax\r\n\tmov\trax, qword ptr [rsp + 408]\r\n\tmov\tqword ptr [rsp + 72], rax\r\n\tmov\trax, qword ptr [rsp + 400]\r\n\tmov\tqword ptr [rsp + 64], rax\r\n\tmov\trax, qword ptr [rsp + 392]\r\n\tmov\tqword ptr [rsp + 56], rax\r\n\tmov\trax, qword ptr [rsp + 384]\r\n\tmov\tqword ptr [rsp + 48], rax\r\n\tmov\trax, qword ptr [rsp + 376]\r\n\tmov\tqword ptr [rsp + 40], rax\r\n\tmov\trax, qword ptr [rsp + 368]\r\n\tmov\tqword ptr [rsp + 32], rax\r\n\tlea\trcx, [rsp + 224]\r\n\tlea\trdx, [rsp + 216]\r\n\tmov\tr8, qword ptr [rsp + 208]\r\n\tmov\tr9, qword ptr [rsp + 256]\r\n\tmovabs\trax, offset _ZN8__main__23nb_transform_vector$244E5ArrayIfLi2E1C7mutable7alignedE5ArrayIfLi1E1C7mutable7alignedE5ArrayIfLi1E1C7mutable7alignedE\r\n\tcall\trax\r\n\tcmp\teax, -2\r\n\tje\t.LBB0_6\r\n\ttest\teax, eax\r\n\tjne\t.LBB0_4\r\n.LBB0_6:\r\n\tmov\trcx, rsi\r\n\tcall\tr12\r\n\tmov\trcx, rbx\r\n\tcall\tr12\r\n\tadd\trbp, qword ptr [rsp + 248]\r\n\tadd\trdi, r13\r\n\tadd\tr14, -1\r\n\tjg\t.LBB0_2\r\n.LBB0_7:\r\n\tmovabs\trdi, offset NRT_decref\r\n\tmov\trcx, rsi\r\n\tcall\trdi\r\n\tmov\trcx, rbx\r\n\tcall\trdi\r\n\tmov\trcx, qword ptr [rsp + 208]\r\n\tcall\trdi\r\n\tmov\trax, qword ptr [rsp + 240]\r\n\tmov\tqword ptr [rax], 0\r\n\txor\teax, eax\r\n\tjmp\t.LBB0_5\r\n.LBB0_4:\r\n\tmov\trcx, qword ptr [rsp + 216]\r\n\tmov\trdx, qword ptr [rsp + 232]\r\n\tmov\tqword ptr [rdx], rcx\r\n.LBB0_5:\r\n\tadd\trsp, 264\r\n\tpop\trbx\r\n\tpop\trbp\r\n\tpop\trdi\r\n\tpop\trsi\r\n\tpop\tr12\r\n\tpop\tr13\r\n\tpop\tr14\r\n\tpop\tr15\r\n\tret\r\n.Lfunc_end0:\r\n\t.size\t_ZN8__main__24nb_transform_vectors$243E5ArrayIfLi2E1C7mutable7alignedE5ArrayIfLi2E1C7mutable7alignedE5ArrayIfLi2E1C7mutable7alignedE, .Lfunc_end0-_ZN8__main__24nb_transform_vectors$243E5ArrayIfLi2E1C7mutable7alignedE5ArrayIfLi2E1C7mutable7alignedE5ArrayIfLi2E1C7mutable7alignedE\r\n\t.cfi_endproc\r\n\r\n```\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantoniotejada%2Fepycc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fantoniotejada%2Fepycc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantoniotejada%2Fepycc/lists"}