GithubHelp home page GithubHelp logo

facebookincubator / cinder Goto Github PK

View Code? Open in Web Editor NEW
3.4K 61.0 119.0 500.69 MB

Cinder is Meta's internal performance-oriented production version of CPython.

Home Page: https://trycinder.com

License: Other

Shell 0.11% C 32.72% C++ 6.34% DTrace 0.01% Python 59.64% Batchfile 0.13% HTML 0.32% CSS 0.01% Roff 0.07% VBScript 0.01% PLSQL 0.04% XSLT 0.01% PowerShell 0.03% Rich Text Format 0.01% Objective-C 0.05% Makefile 0.07% Assembly 0.08% Common Lisp 0.04% JavaScript 0.03% M4 0.32%
interpreter jit python compiler runtime

cinder's Introduction

Cinder Logo

Cinder Logo

Support Ukraine - Help Provide Humanitarian Aid to Ukraine.

Cinder build status on GitHub Actions

Welcome to Cinder!

Cinder is Meta's internal performance-oriented production version of CPython 3.10. It contains a number of performance optimizations, including bytecode inline caching, eager evaluation of coroutines, a method-at-a-time JIT, and an experimental bytecode compiler that uses type annotations to emit type-specialized bytecode that performs better in the JIT.

Cinder is powering Instagram, where it started, and is increasingly used across more and more Python applications in Meta.

For more information on CPython, see README.cpython.rst.

Is this supported?

Short answer: no.

We've made Cinder publicly available in order to facilitate conversation about potentially upstreaming some of this work to CPython and to reduce duplication of effort among people working on CPython performance.

Cinder is not polished or documented for anyone else's use. We don't have the desire for it to become an alternative to CPython. Our goal in making this code available is a unified faster CPython. So while we do run Cinder in production, if you choose to do so you are on your own. We can't commit to fixing external bug reports or reviewing pull requests. We make sure Cinder is sufficiently stable and fast for our production workloads, but we make no assurances about its stability or correctness or performance for any external workloads or use-cases.

That said, if you have experience in dynamic language runtimes and have ideas to make Cinder faster; or if you work on CPython and want to use Cinder as inspiration for improvements in CPython (or help upstream parts of Cinder to CPython), please reach out; we'd love to chat!

How do I build it?

Cinder should build just like CPython; configure and make -j. However as most development and usage of Cinder occurs in the highly specific context of Meta we do not exercise it much in other environments. As such, the most reliable way to build and run Cinder is to re-use the Docker-based setup from our GitHub CI workflow.

If you just want to get a working Cinder without building it yourself, our Runtime Docker Image is going to be the easiest (no repo clone needed!):

  1. Install and setup Docker.
  2. Fetch and run our cinder-runtime image:

    docker run -it --rm ghcr.io/facebookincubator/cinder-runtime:cinder-3.10

If you want to build it yourself:

  1. Install and setup Docker.
  2. Clone the Cinder repo:

    git clone https://github.com/facebookincubator/cinder

  3. Run a shell in the Docker environment used by the CI:

    docker run -v "$PWD/cinder:/vol" -w /vol -it --rm ghcr.io/facebookincubator/cinder/python-build-env:latest bash

    The above command does the following:
    • Downloads (if not already cached) a pre-built Docker image used by the CI from https://ghcr.io/facebookincubator/cinder/python-build-env.
    • Makes the Cinder checkout above ($PWD/cinder) available to the Docker environment at the mount point /vol.
    • Interactively (-it) runs bash in the /vol directory.
    • Cleanup the local image after it's finished (--rm) to avoid disk bloat.
  4. Build Cinder from the shell started the Docker environment:

    ./configure && make

Please be aware that Cinder is only built or tested on Linux x64; anything else (including macOS) probably won't work. The Docker image above is Fedora Linux-based and built from a Docker spec file in the Cinder repo: .github/workflows/python-build-env/Dockerfile.

There are some new test targets that might be interesting. make testcinder is pretty much the same as make test except that it skips a few tests that are problematic in our dev environment. make testcinder_jit runs the test suite with the JIT fully enabled, so all functions are JIT'ed. make testruntime runs a suite of C++ gtest unit tests for the JIT. And make test_strict_module runs a test suite for strict modules (see below).

Note that these steps produce a Cinder Python binary without PGO/LTO optimizations enabled, so don't expect to use these instructions to get any speedup on any Python workload.

How do I explore it?

Cinder Explorer is a live playground, where you can see how Cinder compiles Python code from source to assembly -- you're welcome to try it out! Feel free to file feature requests and bug reports. Keep in mind that the Cinder Explorer, like the rest of this, "supported" on a best-effort basis.

What's here?

Immortal Instances

Instagram uses a multi-process webserver architecture; the parent process starts, performs initialization work (e.g. loading code), and forks tens of worker processes to handle client requests. Worker processes are restarted periodically for a number of reasons (e.g. memory leaks, code deployments) and have a relatively short lifetime. In this model, the OS must copy the entire page containing an object that was allocated in the parent process when the object's reference count is modified. In practice, the objects allocated in the parent process outlive workers; all the work related to reference counting them is unnecessary.

Instagram has a very large Python codebase and the overhead due to copy-on-write from reference counting long-lived objects turned out to be significant. We developed a solution called "immortal instances" to provide a way to opt-out objects from reference counting. See Include/object.h for details. This feature is controlled by defining Py_IMMORTAL_INSTANCES and is enabled by default in Cinder. This was a large win for us in production (~5%), but it makes straight-line code slower. Reference counting operations occur frequently and must check whether or not an object participates in reference counting when this feature is enabled.

Shadowcode

"Shadowcode" or "shadow bytecode" is our implementation of a specializing interpreter. It observes particular optimizable cases in the execution of generic Python opcodes and (for hot functions) dynamically replaces those opcodes with specialized versions. The core of shadowcode lives in Shadowcode/shadowcode.c, though the implementations for the specialized bytecodes are in Python/ceval.c with the rest of the eval loop. Shadowcode-specific tests are in Lib/test/test_shadowcode.py.

It is similar in spirit to the specializing adaptive interpreter (PEP-659) that will be built into CPython 3.11.

Await-aware function calls

The Instagram Server is an async-heavy workload, where each web request may trigger hundreds of thousands of async tasks, many of which can be completed without suspension (e.g. thanks to memoized values).

We extended the vectorcall protocol to pass a new flag, Ci_Py_AWAITED_CALL_MARKER, indicating the caller is immediately awaiting this call.

When used with async function calls that are immediately awaited, we can immediately (eagerly) evaluate the called function, up to completion, or up to its first suspension. If the function completes without suspending, we are able to return the value immediately, with no extra heap allocations.

When used with async gather, we can immediately (eagerly) evaluate the set of passed awaitables, potentially avoiding the cost of creation and scheduling of multiple tasks for coroutines that could be completed synchronously, completed futures, memoized values, etc.

These optimizations resulted in a significant (~5%) CPU efficiency improvement.

This is mostly implemented in Python/ceval.c, via a new vectorcall flag Ci_Py_AWAITED_CALL_MARKER, indicating the caller is immediately awaiting this call. Look for uses of the IS_AWAITED() macro and this vectorcall flag.

The Cinder JIT

The Cinder JIT is a method-at-a-time custom JIT implemented in C++. It is enabled via the -X jit flag or the PYTHONJIT=1 environment variable. It supports almost all Python opcodes, and can achieve 1.5-4x speed improvements on many Python performance benchmarks.

By default when enabled it will JIT-compile every function that is ever called, which may well make your program slower, not faster, due to overhead of JIT-compiling rarely-called functions. The option -X jit-list-file=/path/to/jitlist.txt or PYTHONJITLISTFILE=/path/to/jitlist.txt can point it to a text file containing fully qualified function names (in the form path.to.module:funcname or path.to.module:ClassName.method_name), one per line, which should be JIT-compiled. We use this option to compile only a set of hot functions derived from production profiling data. (A more typical approach for a JIT would be to dynamically compile functions as they are observed to be called frequently. It hasn't yet been worth it for us to implement this, since our production architecture is a pre-fork webserver, and for memory sharing reasons we wish to do all of our JIT compiling up front in the initial process before workers are forked, which means we can't observe the workload in-process before deciding which functions to JIT-compile.)

The JIT lives in the Jit/ directory, and its C++ tests live in RuntimeTests/ (run these with make testruntime). There are also some Python tests for it in Lib/test/test_cinderjit.py; these aren't meant to be exhaustive, since we run the entire CPython test suite under the JIT via make testcinder_jit; they cover JIT edge cases not otherwise found in the CPython test suite.

See Jit/pyjit.cpp for some other -X options and environment variables that influence the behavior of the JIT. There is also a cinderjit module defined in that file which exposes some JIT utilities to Python code (e.g. forcing a specific function to compile, checking if a function is compiled, disabling the JIT). Note that cinderjit.disable() only disables future compilation; it immediately compiles all known functions and keeps existing JIT-compiled functions.

The JIT first lowers Python bytecode to a high-level intermediate representation (HIR); this is implemented in Jit/hir/. HIR maps reasonably closely to Python bytecode, though it is a register machine instead of a stack machine, it is a bit lower level, it is typed, and some details that are obscured by Python bytecode but important for performance (notably reference counting) are exposed explicitly in HIR. HIR is transformed into SSA form, some optimization passes are performed on it, and then reference counting operations are automatically inserted into it according to metadata about the refcount and memory effects of HIR opcodes.

HIR is then lowered to a low-level intermediate representation (LIR), which is an abstraction over assembly, implemented in Jit/lir/. In LIR we do register allocation, some additional optimization passes, and then finally LIR is lowered to assembly (in Jit/codegen/) using the excellent asmjit library.

The JIT is in its early stages. While it can already eliminate interpreter loop overhead and offers significant performance improvements for many functions, we've only begun to scratch the surface of possible optimizations. Many common compiler optimizations are not yet implemented. Our prioritization of optimizations is largely driven by the characteristics of the Instagram production workload.

Strict Modules

Strict modules is a few things rolled into one:

1. A static analyzer capable of validating that executing a module's top-level code will not have side effects visible outside that module.

2. An immutable StrictModule type usable in place of Python's default module type.

3. A Python module loader capable of recognizing modules opted in to strict mode (via an import __strict__ at the top of the module), analyzing them to validate no import side effects, and populating them in sys.modules as a StrictModule object.

Static Python

Static Python is a bytecode compiler that makes use of type annotations to emit type-specialized and type-checked Python bytecode. Used along with the Cinder JIT, it can deliver performance similar to MyPyC or Cython in many cases, while offering a pure-Python developer experience (normal Python syntax, no extra compilation step). Static Python plus Cinder JIT achieves 18x the performance of stock CPython on a typed version of the Richards benchmark. At Instagram we have successfully used Static Python in production to replace all Cython modules in our primary webserver codebase, with no performance regression.

The Static Python compiler is built on top of the Python compiler module that was removed from the standard library in Python 3 and has since been maintained and updated externally; this compiler is incorporated into Cinder in Lib/compiler. The Static Python compiler is implemented in Lib/compiler/static/, and its tests are in Lib/test/test_compiler/test_static.py.

Classes defined in Static Python modules are automatically given typed slots (based on inspection of their typed class attributes and annotated assignments in __init__), and attribute loads and stores against instances of these types use new STORE_FIELD and LOAD_FIELD opcodes, which in the JIT become direct loads/stores from/to a fixed memory offset in the object, with none of the indirection of a LOAD_ATTR or STORE_ATTR. Classes also gain vtables of their methods, for use by the INVOKE_* opcodes mentioned below. The runtime support for these features is located in StaticPython/classloader.h and StaticPython/classloader.c.

A static Python function begins with a hidden prologue which checks that the supplied arguments' types match the type annotations, and raises TypeError if not. Calls from a static Python function to another static Python function will skip this opcode (since the types are already validated by the compiler). Static to static calls can also avoid much of the overhead of a typical Python function call. We emit an INVOKE_FUNCTION or INVOKE_METHOD opcode which carries with it metadata about the called function or method; this plus optionally immutable modules (via StrictModule) and types (via cinder.freeze_type(), which we currently apply to all types in strict and static modules in our import loader, but in future may become an inherent part of Static Python) and compile-time knowledge of the callee signature allow us to (in the JIT) turn many Python function calls into direct calls to a fixed memory address using the x64 calling convention, with little more overhead than a C function call.

Static Python is still gradually typed, and supports code that is only partially annotated or uses unknown types by falling back to normal Python dynamic behavior. In some cases (e.g. when a value of statically-unknown type is returned from a function with a return annotation), a runtime CAST opcode is inserted which will raise TypeError if the runtime type does not match the expected type.

Static Python also supports new types for machine integers, bools, doubles, and vectors/arrays. In the JIT these are handled as unboxed values, and e.g. primitive integer arithmetic avoids all Python overhead. Some operations on builtin types (e.g. list or dictionary subscript or len()) are also optimized.

Cinder supports gradual adoption of static modules via a strict/static module loader that can automatically detect static modules and load them as static with cross-module compilation. The loader will look for import __static__ and import __strict__ annotations at the top of a file, and compile modules appropriately. To enable the loader, you have one of three options:

1. Explicitly install the loader at the top level of your application via from cinderx.compiler.strict.loader import install; install().

  1. Set PYTHONINSTALLSTRICTLOADER=1 in your env.
  2. Run ./python -X install-strict-loader application.py.

Alternatively, you can compile all code statically by using ./python -m compiler --static some_module.py, which will compile the module as static Python and execute it.

See CinderDoc/static_python.rst for more detailed documentation.

cinder's People

Contributors

1st1 avatar akuchling avatar benjaminp avatar berkerpeksag avatar birkenfeld avatar bitdancer avatar brettcannon avatar ezio-melotti avatar freddrake avatar gpshead avatar gvanrossum avatar gward avatar jackjansen avatar jeremyhylton avatar loewis avatar mdickinson avatar merwok avatar miss-islington avatar ned-deily avatar nnorwitz avatar orsenthil avatar pitrou avatar rhettinger avatar serhiy-storchaka avatar terryjreedy avatar tim-one avatar tiran avatar vsajip avatar vstinner avatar warsaw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cinder's Issues

-X jit-enable-jit-list-wildcards has high overhead

The -X jit-enable-jit-list-wildcards option causes overhead of PyEntry_init to increase by 4x.

The following are measured with prof top -g -p ... on my app process.

empty jit list, wildcards disabled:
Screen Shot 2021-06-17 at 11 53 08 AM

empty jit list, -X jit-enable-jit-list-wildcards:
Screen Shot 2021-06-17 at 11 54 03 AM

(from discussion in #18)

`Union[str, int]` is treated as `dynamic`

What version of Static Python are you using?

6862bbd
2021-10-03

What program did you run?

from typing import Union

x: Union[int, str] = 1
reveal_type(x)

What happened?

The type of x is dynamic

compiler.static.errors.TypedSyntaxError:
reveal_type(x): 'dynamic',
'x' has declared type 'dynamic' and
local type 'Literal[1]'

What should have happened?

We expected the type of x to be Union[int, str] because union types are supported:

x: Any = 1
if True:
    x = "hello"
else:
    x = 42
reveal_type(x)

# compiler.static.errors.TypedSyntaxError:
# reveal_type(x): 'dynamic',
# 'x' has declared type 'dynamic' and
# local type 'Union[str, int]'

Class values as CheckedDict type-arguments are not handled properly

9186730
2021-12-01

This issue might be related to #33

What program did you run?

from __static__ import CheckedDict
def make_C():
    class C: pass
    return C
C = make_C()
d = CheckedDict[str, C]({})

What happened?

The program raises a runtime error.

TypeError: chkdict[str, object].__new__(chkdict[str, C]): chkdict[str, C] is not a subtype of chkdict[str, object]

What should have happened?

We expected two possible outcomes:

  • a compile-time error complaining that CheckedDict[str, C] is invalid because C is not a valid type
  • a clearer run-time error
  • the program terminates without any error.

Jit/slot_gen.cpp:111:6: error: call to member function 'bt' is ambiguous

>> Jit/slot_gen.o
warning: unknown warning option '-Wno-cast-function-type'; did you mean '-Wno-bad-function-cast'? [-Wunknown-warning-option]
Jit/slot_gen.cpp:111:6: error: call to member function 'bt' is ambiguous
  as.bt(tmp, kImmortalBitPos);
  ~~~^~
./ThirdParty/asmjit/src/asmjit/./x86/x86emitter.h:482:18: note: candidate function
  ASMJIT_INST_2i(bt, Bt, Gp, Imm)                                      // ANY
                 ^
./ThirdParty/asmjit/src/asmjit/./x86/x86emitter.h:482:18: note: candidate function
./ThirdParty/asmjit/src/asmjit/./x86/x86emitter.h:482:18: note: candidate function
./ThirdParty/asmjit/src/asmjit/./x86/x86emitter.h:482:18: note: candidate function
Jit/slot_gen.cpp:120:6: error: call to member function 'call' is ambiguous

PyDict is not equivalent to dict

What version of Static Python are you using?

9965302
2021-07-15

The Problem

We believe that PyDict is supposed to be the same as the dict type. However, the following pair of programs reveals that these two type constructors are not equivalent. Is this a bug? Or are there some subtle differences between PyDict and dict?

$ diff ./CheckedDict_is_dict.py ./CheckedDict_is_not_PyDict.py 
...
7c7
< def consumer(d: dict[int, int]) -> int:
---
> def consumer(d: PyDict[int, int]) -> int:
...
# CheckedDict_is_dict.py
from __static__ import CheckedDict

def producer() -> CheckedDict[int, int]:
    return CheckedDict[int, int]({2: 3})

def consumer(d: dict[int, int]) -> int:
    return d[2]

d = producer()
print(consumer(d))  # 3
# CheckedDict_is_not_PyDict.py
from __static__ import CheckedDict, PyDict

def producer() -> CheckedDict[int, int]:
    return CheckedDict[int, int]({2: 3})

def consumer(d: PyDict[int, int]) -> int:
    return d[2]

d = producer()
print(consumer(d))

# We expected the value `3`, but saw
#
#    compiler.static.TypedSyntaxError: type mismatch: chkdict[int, int]
#    received for positional arg 'd', expected dict

undefined variables do not raise a type error

What version of Static Python are you using?

6862bbd
2021-10-07

What program did you run?

def f() -> int:
    return a_definitly_undefined_variable

What happened?

The program passes type checks.

What should have happened?

We expected a compile-time error complaining that a_definitly_undefined_variable is undefined. (This is what mypy does.)

calling CheckedDict

What version of Static Python are you using?

9965302
2021-07-21

What program did you run?

# calling_CheckedDict.py
from __static__ import CheckedDict


def testfunc():
    x = CheckedDict[int, str]({int(i): int(i) for i in
                               range(1, 5)})
    return x

testfunc()

What happened?

A dynamic error was raised.

TypeError: bad value 'int' for dict[int, str]

What should have happened?

We expected a static error because the type checker has enough information to push int and str into the comprehension. This behavior has been illustrated by a similar program, which raises a static error.

# calling_CheckedDict_variant.py
from __static__ import CheckedDict


def testfunc():
    x: CheckedDict[int, str] = {int(i): int(i) for i in
                               range(1, 5)}
    return x

testfunc()

# compiler.static.TypedSyntaxError: type mismatch: 
# Exact[chkdict[Exact[int], Exact[int]]] cannot be assigned to chkdict[int, str] 

-X jit-enable-jit-list-wildcards adds significant overhead

As per discussion in #18, enabling this option adds a ~2% overhead when enabled. We should at least add a comment to let people know this is not suitable for production.

The root-cause is likely O(all pattern lengths) complexity being introduced on every PyEval_LazyInit() which can be a very significant given how nested-function support currently works.

Cannot declare class with attributes

What version of Static Python are you using?

9965302
2021-07-13

What program did you run?

class C:
    x: int = 3

What happened?

ValueError: 'x' in __slots__ conflicts with class variable

What should have happened?

We expected the program to terminate without errors.

The error confuses us because it mentions __slots__. As far as we can tell from the python doc and a stackoverflow, __slots__ is used to optimize access to instance attributes. And this optimization has to be enabled in a manual and low-level manner. From this perspective, class attributes should have little to do with slots, unless a class attribute is shadowed by an instance attribute.

We found a similar test in cinder/test_static.py

    def test_class_level_final_reinitialized_directly(self):
        codestr = """
        from typing import Final

        class C:
            x: Final[int] = 3

        C.x = 4
        """
        # Note - this will raise even without the Final, we don't allow assignments to slots
        with self.assertRaisesRegex(
            TypedSyntaxError,
            type_mismatch("Literal[4]", "Exact[types.MemberDescriptorType]"),
        ):
            self.compile(codestr, StaticCodeGenerator, modname="foo")

We wonder

  • How to read the line x: Final[int] = 3? Is this an initialization, an assignment, or both?
  • Does Static Python have class attributes? Or are you only supposed to declare the fields that instances will have?

Redeclaring variables may cause segmentation fault

bae060e
2021-10-22

What program did you run?

from __static__ import CheckedDict

class f():
    pass

# reveal_type(f) # 'Exact[chkdict[str, int]]'

print(f["foo"])

f: CheckedDict[str, int] = CheckedDict[str, int]({'foo': 2})

What happened?

The program is type-checked and compiled, but triggers a segmentation fault at runtime.

What should have happened?

We expected two possible outcomes. But either way, the type of f shouldn't be Exact[chkdict[str, int]].

One outcome is that the program fails type checks. It is reasonable to disallow redeclaring a variable at the same scope level when the new type is not a subtype of the old type.

Another possible outcome is that Static Python catches the runtime error. In this case, the type of f should be dynamic. The local type can be type[f] and, after the redeclaration, chkdict[str, int].

chkdict prints as "dict"

What version of Static Python are you using?

9965302
2021-06-24

What program did you run?

from __static__ import CheckedDict

def chk_ref(d: CheckedDict[int, int]) -> int:
  return d[0]

pydict: dict[int, int] = {1: 1}

chk_ref(pydict)

What happened?

TypeError: expected 'dict[int, int]', got 'dict'

What should have happened?

Error: expected Checked Dict got Python dict

error: catching polymorphic type ‘class std::exception’ by value

/cinder/Jit/pyjit.cpp: In function ‘long int flag_long(const char*, const char*, long int)’:
/cinder/Jit/pyjit.cpp:535:19: error: catching polymorphic type ‘class std::exception’ by value [-Werror=catch-value=]
  535 |     } catch (std::exception) {
      |                   ^~~~~~~~~
cc1plus: all warnings being treated as errors
make: *** [Makefile:2110: Jit/pyjit.o] Error 1
make: *** Waiting for unfinished jobs....

when running either configure & make or ./oss-build-and-test.sh on Ubuntu 20.04 on x64

What would it take to make unhashable dict key a static error?

What version of Static Python are you using?

9965302
2021-07-15

What program did you run?

# compile_nested_dict_dict_key.py

from __static__ import CheckedDict

class B: pass

class D(B): pass

def testfunc():
    x = CheckedDict[B, int]({B():42, D():42})
    y = CheckedDict[CheckedDict[B, int], int]({x: 42})
    return y

print(testfunc())

What happened?

The program raises a runtime error.

TypeError: unhashable type: 'dict[B, int]'

What should have happened?

We expected a compile-time error complaining that CheckedDict[B, int] is unhashable. Maybe the type system can look for a __hash__ method.

docker build

   docker run -t -i fedora:32 bash
   git clone https://github.com/facebookincubator/cinder.git
   yum install zlib-devel openssl-devel
   ./configure
   make
   make altinstall

can't open file 'Lib/test/test_multithreaded_compile.py': [Errno 2] No such file or directory

./oss-build-and-test.sh from the root directory fails on Ubuntu 20.10 w/ the next error:

Total duration: 3 min 25 sec
Tests result: SUCCESS
./python  -X jit -X jit-test-multithreaded-compile -X jit-batch-compile-workers=10 Lib/test/test_multithreaded_compile.py
./python: can't open file 'Lib/test/test_multithreaded_compile.py': [Errno 2] No such file or directory
make: *** [Makefile:1567: testcinder_jit] Segmentation fault (core dumped)

jit-list wildcards: support "name.*" for <qualname>

Currently there doesn't seem to be a way to include all methods of a certain class.

possible now: include everything in a module (<module>:*), include methods of any class with a certain name (<module>:*.__init__).

not possible now: <module>:MyClass.*

2021 H2 static Python roadmap

Usability

  • [P0] Make static python consumable easily for any Cinder user
    • Module loader in cinder
    • Consolidate rewriter logic into static compiler (out of strict modules)
    • minimum runtime version for static pycs
  • [P0] Make static python type checker run as a linter
  • [P0] Fix module dependency tracking for pyc invalidation
  • [P1] Support Enums defined in static modules
  • [P1] Expand Static Python reference guide
    • How to improve perf: primitives, CheckedDict...
    • Things that aren’t supported: multiple inheritance, metaclasses...
    • Gotchas: cbool comparison results
  • [P1] Don’t JIT all static functions
    • As we adopt more code into Static Python, not all static functions will be hot enough to deserve JIT, this should be left up to the JIT list to determine.
  • [P1] support prod_assert
  • [P1] support primitive floats
  • [P1] cross-module import always sees consistent (DeclarationVisit only) view of imported module types
  • [P1] fix patching of static functions with compatible but keyword-requiring signatures
  • [P2] improve type inference of primitive int literals

Performance

  • [P0] CheckedList
  • [P0] Optimize calls to types
  • [P0] Improved descriptor support:
    • Properties
    • Cached properties
    • class methods
  • [P1] Primitives in method calls
  • [P1] Primitives in type calls
  • [P1] More typed built-in methods
  • [P1] Dataclass intrinsic
  • [P1] Run declaration visitor on nonstatic dependency code and stubs so we can generate invokes against them
    • make sure the failure mode is appropriate Python error, not crash
    • also emit CAST on return value (helps with narrowing and Pyre compat for usability)
  • [P1] Support for runtime-checking small unions
  • [P1] avoid checking types of default argument values
  • [P1] return type of async functions
    • type-specialized await?
    • eager evaluation?
  • [P1] type-preserving user-defined decorators (util.etc, statsd.timer)
  • [P1] Declarable Exact types
    • Unlocks more non-GC containers, can declare they contain only Exact[str], which is non-GC
    • Unlocks other optimizations and statically known types also, subclasses of Python builtins can do crazy things
  • [P2] inlining decorators at compile time
  • [P2] Fixed size arrays
  • [P2] Perhaps some exploratory work around object lifetimes? Would be great to get some objects out of the GC.
    • Multiple return values that box to tuples but don’t allocate might be something slightly related

Static Python allows passing too many arguments to `__init__`

What version of Static Python are you using?

4f1b313
2021-07-26

What program did you run?

# constructor_args_static.py

class Person:
    def __init__(self, name, age):
        pass

def main():
    p1 = Person('Alice', 21, False)
    return p1

What happened?

The program terminates without errors.

What should have happened?

We expected a compile-time error because the Person constructor (__init__) accepts only 2 arguments, but was given 3.

Is bool a subtype of int?

What version of Static Python are you using?

4f1b313
2021-07-27

What program did you run?

x: int = True

What happened?

The program errors.

compiler.static.errors.TypedSyntaxError: type mismatch: Exact[bool] cannot be assigned to int

What should have happened?

We expected no error because bool is a subclass of int (isinstance(True, int) is True).

error: ‘siginterrupt’ is deprecated: Use sigaction with SA_RESTART instead

./oss-build-and-test.sh output (Ubuntu 20.04):

/home/dev/experiments/cinder/Modules/signalmodule.c: In function ‘signal_siginterrupt_impl’:
/home/dev/experiments/cinder/Modules/signalmodule.c:661:5: error: ‘siginterrupt’ is deprecated: Use sigaction with SA_RESTART instead [-Werror=deprecated-declarations]
  661 |     if (siginterrupt(signalnum, flag)<0) {
      |     ^~
In file included from /home/dev/experiments/cinder/Modules/signalmodule.c:26:
/usr/include/signal.h:311:12: note: declared here
  311 | extern int siginterrupt (int __sig, int __interrupt) __THROW
      |            ^~~~~~~~~~~~
gcc -pthread -I"/home/dev/experiments/cinder" -fno-omit-frame-pointer -momit-leaf-frame-pointer  -Wno-unused-result -Wsign-compare -Wno-cast-function-type -Wno-type-limits -DNDEBUG -g -fwrapv -O3 -Wall    -std=c99 -Wextra -Werror -Wno-unused-result -Wno-unused-parameter -Wno-missing-field-initializers -Werror=implicit-function-declaration  -I/home/dev/experiments/cinder/Include/internal -I"/home/dev/experiments/cinder" -IObjects -IInclude -IPython -I. -I/home/dev/experiments/cinder/Include -I/home/dev/experiments/cinder/ThirdParty/fmt-6.1.1/include -I/home/dev/experiments/cinder/ThirdParty/i386-dis   -DFMT_HEADER_ONLY=1  -DPy_BUILD_CORE_BUILTIN  -c /home/dev/experiments/cinder/Modules/_stat.c -o Modules/_stat.o
cc1: all warnings being treated as errors
make: *** [Makefile:2431: Modules/signalmodule.o] Error 1
make: *** Waiting for unfinished jobs....

undefined reference to shm_open

I get a couple of these errors when linking the final executable:

$ g++ -pthread   -lrt   -Xlinker -export-dynamic -o python Programs/python.o libpython3.8_static.a -lcrypt -lpthread -ldl  -lutil -lm   -lm 
/usr/bin/ld: libpython3.8_static.a(virtmem.o): in function `asmjit::VirtMem_openAnonymousMemory(int*, bool) [clone .part.0]':
/home/kmod/cinder/ThirdParty/asmjit/src/asmjit/core/virtmem.cpp:330: undefined reference to `shm_open'
/usr/bin/ld: /home/kmod/cinder/ThirdParty/asmjit/src/asmjit/core/virtmem.cpp:332: undefined reference to `shm_unlink'
collect2: error: ld returned 1 exit status

Adding -lrt to the makefile LIBS variable solved this for me. This is on Ubuntu 20.04.

code accessing numpy array elements slower on cinder (jit on or off)

The example below is fairly straightforward, and gets about 20x speedup under numba. If I understand correctly, numba achieves that by accessing the numpy C API directly from jitted code, so the Python VM isn't involved. (I don't expect cinder jit to do that.)

Still, cinder (no jit) is 50% slower on this code than stock CPython (not using numba). And with cinder jit enabled on this function exclusively, there is no improvement over the no-jit case. (I confirmed that the function is compiled.)

What makes cinder slower than stock Python when touching numpy arrays?

def masked_mean(array: numpy.ndarray):
    n_rows, n_cols = array.shape
    mean = [0.0] * n_cols

    for j in range(n_cols):
        sum_ = 0.0
        n = 0
        for i in range(n_rows):
            val = array[i, j]
            if val > 0.0:
                sum_ += val
                n += 1
        mean[j] = sum_ / n if n > 0 else -1.0

    return mean

opt-in to JIT compile

From my understanding, starting cinder with -X jit will compile everything it encounters until cinderjit.disable(), but by then a bunch of things may already be compiled.

An option to start with jit disabled (i.e. init the JIT but set jit_config.is_enabled = 0) would be useful. For example, then I could use a decorator to tag functions for compile, without needing to maintain a jit-list.

Unable to build the _socket module

On Ubuntu 20.04, getting this error during the build:

$ gcc -pthread -fPIC -I. -fno-omit-frame-pointer -momit-leaf-frame-pointer -Wno-unused-result -Wsign-compare -Wno-cast-function-type -Wno-type-limits -DNDEBUG -g -fwrapv -O3 -Wall -std=c99 -Wextra -Werror -Wno-unused-result -Wno-unused-parameter -Wno-missing-field-initializers -Werror=implicit-function-declaration -I./Include/internal -I./ThirdParty/i386-dis -I. -I./Include -I./ThirdParty/fmt-6.1.1/include -I/usr/include/x86_64-linux-gnu -I/usr/local/include -I/home/kmod/cinder/Include -I/home/kmod/cinder -c /home/kmod/cinder/Modules/socketmodule.c -o build/temp.linux-x86_64-3.8/home/kmod/cinder/Modules/socketmodule.o
In file included from /usr/include/string.h:495,
                 from ./Include/Python.h:30,
                 from /home/kmod/cinder/Modules/socketmodule.c:103:
In function ‘memset’,
    inlined from ‘getsockaddrarg’ at /home/kmod/cinder/Modules/socketmodule.c:2301:9,
    inlined from ‘sock_bind’ at /home/kmod/cinder/Modules/socketmodule.c:3079:10:
/usr/include/x86_64-linux-gnu/bits/string_fortified.h:71:10: error: ‘__builtin_memset’ offset [17, 88] from the object at ‘addrbuf’ is out of the bounds of referenced subobject ‘sa’ with type ‘struct sockaddr’ at offset 0 [-Werror=array-bounds]
   71 |   return __builtin___memset_chk (__dest, __ch, __len, __bos0 (__dest));
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors

Unsound list to Array conversion

What version of SP are you using

43d1ce7 2021-10-21

What program did you run?

  # typed.py
  from __static__ import Array, int32
  def h(x:Array[int32])->int32:
    print(x)
    return x[0]
  from typed import h
  x = ["B"]
  print(h(x))
  # ['B'] # from typed code!
  # B

What happened?

No error

What should have happened?

A runtime error --- either because ["B"] is not an array or because "B" is not an int32.

On the bright side, I haven't been able to use the wrong types to get a serious error. Adding (+) and comparing (<) x[0] with another int32 gives an error about mixing strings and numbers.

In general, we were wondering what's the SP strategy for Arrays and Vectors at the boundaries to untyped code. Is there a conversion story like for int32, should these boundaries always error, or something else?

Right now, typed code can send an Array to Python, so I'd expect the function h to look for an Array object and reject everything else.

unsound static-to-static field overrides

What version of Static Python are you using?

9965302
2021-07-15

What program did you run?

# override_field_incompatible_type.py
from typing import ClassVar

class C:
    x: ClassVar[int] = 0

class D(C):
    x: ClassVar[str] = "0"

# Interestingly, `D.x = "hello"` raises a compile-time error
# 
#     compiler.static.TypedSyntaxError: type mismatch: Exact[str] 
#     cannot be assigned to int

What happened?

The program terminates without errors.

What should have happened?

We expected a compile-time error because D override the class variable x with an incompatible type.

have cinderjit.force_compile() override jit list

I noticed that force_compile() fails if the function isn't in the jit list. Would it be reasonable to change that behavior?

Example use case: I'd like to enable jit on some 3rd party modules using jit list wildcards, but within my own code use decorators and force_compile() to mark functions for compile.

numba import error with "-X jit-no-frame"

(noted that cinder jit is experimental, and jit-no-frame is more experimental)
(also, it's silly to have multiple jitters going on, and we want to exorcise numba from our app dependencies)

things are fine with plain jit:

$ python3.8 -X jit
Python 3.8.5+cinder (heads/cinder/3.8:afb6375, May 28 2021, 05:28:52)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from numba.core.types.common import Buffer
>>> Buffer(int, 2, 'C')
buffer(<class 'int'>, 2d, C)

import problem with jit-no-frame:

$ python3.8 -X jit -X jit-no-frame
Python 3.8.5+cinder (heads/cinder/3.8:afb6375, May 28 2021, 05:28:52)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from numba.core.types.common import Buffer
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/dev/.local/lib/python3.8/site-packages/numba/__init__.py", line 55, in <module>
    import numba.cpython.charseq
  File "/home/dev/.local/lib/python3.8/site-packages/numba/cpython/charseq.py", line 10, in <module>
    from numba.cpython import unicode
  File "/home/dev/.local/lib/python3.8/site-packages/numba/cpython/unicode.py", line 30, in <module>
    from numba.cpython.hashing import _Py_hash_t
  File "/home/dev/.local/lib/python3.8/site-packages/numba/cpython/hashing.py", line 562, in <module>
    'tmp': types.Array(types.uint64, 1, 'C'),
  File "/home/dev/.local/lib/python3.8/site-packages/numba/core/types/abstract.py", line 66, in __call__
    inst = type.__call__(cls, *args, **kwargs)
  File "/home/dev/.local/lib/python3.8/site-packages/numba/core/types/npytypes.py", line 411, in __init__
    super(Array, self).__init__(dtype, ndim, layout, name=name)
  File "/home/dev/.local/lib/python3.8/site-packages/numba/core/types/common.py", line 49, in __init__
    from .misc import unliteral
ModuleNotFoundError: No module named 'numba.cpython.misc'

numba version: 0.51.2

Can't get Docker Image - Unauthorized

When I try to run

docker run -v "$PWD/cinder:/vol" -w /vol -ti ghcr.io/facebookincubator/cinder/python-build-env:latest bash

I get

Unable to find image 'ghcr.io/facebookincubator/cinder/python-build-env:latest' locally
docker: Error response from daemon: Get https://ghcr.io/v2/facebookincubator/cinder/python-build-env/manifests/latest: unauthorized.
See 'docker run --help'.

Accessing undeclared field does not raise a type error

6862bbd
2021-10-13

What program did you run?

# lookup_self_field_neg.py
# This should fail.

class C:
    pass

def f(c: C):
    # reveal_type(c.x) # dynamic
    return c.x

What happened?

The program passes type checks.

What should have happened?

We expected a compile-time error complaining that "C has no attribute x". (This is what mypy does.)

Is this another case where Static Python falls back to dynamic (similar to #50)?

Why does `1 + "foo"` type check?

6862bbd
2021-10-14

What program did you run?

def f():
    return 1 + "foo"

f()

What happened?

The program passes type checks.

What should have happened?

We expected a compile-time error complaining that we can't add up int and str.

BTW, the type of int's __add__ method is printed in an interesting way, what does that type mean?

reveal_type((42).__add__)
# compiler.static.errors.TypedSyntaxError:
# reveal_type(int(42).__add__):
# 'int.__add__'

Improve handling of Type[...] so distinguish between types and instances properly

Currently we don't really fully handle instances of types correctly. One example of this is:

def produce() -> type:
    @final
    class C:
        pass
    return C
C = produce()
reveal_type(C)

The inferred type of "C" becomes "Type[type]", when it really should be "Type[object]", "Type[typing.Any]" or "Type[dynamic]".

There's other places that tie into this like can_assign_from might be better taking a Value vs. a class.

error: initializer element is not constant

Running #49~18.04.1-Ubuntu SMP Wed Apr 28 23:08:58 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
Building with either configure; make -j or ./oss-build-and-test
....

Python/codecs.o
Python/marshal.o
Python/context.o
Python/hamt.o
Python/errors.o
In file included from ./Include/pytime.h:6:0,
from ./Include/Python.h:85,
from Parser/acceler.c:13:
./Include/object.h:520:40: error: initializer element is not constant
static const Py_ssize_t kImmortalBit = 1L << kImmortalBitPos;
^~
./Include/object.h:521:49: error: initializer element is not constant
static const Py_ssize_t kImmortalInitialCount = kImmortalBit;
^~~~~~~~~~~~
In file included from ./Include/pytime.h:6:0,
from ./Include/Python.h:85,
from Parser/node.c:3:
./Include/object.h:520:40: error: initializer element is not constant
static const Py_ssize_t kImmortalBit = 1L << kImmortalBitPos;
^~
./Include/object.h:521:49: error: initializer element is not constant
static const Py_ssize_t kImmortalInitialCount = kImmortalBit;
^~~~~~~~~~~~
....

static compile: support chained comparison

if 0 < 5 < 10:
    pass
$ python -m compiler --static foo.py
Traceback (most recent call last):
  File "/usr/lib/python3.8/runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/usr/lib/python3.8/compiler/__main__.py", line 81, in <module>
    codeobj = pycodegen.compile(
  File "/usr/lib/python3.8/compiler/pycodegen.py", line 111, in compile
    result = make_compiler(source, filename, mode, flags, optimize, compiler, modname)
  File "/usr/lib/python3.8/compiler/pycodegen.py", line 154, in make_compiler
    return generator.make_code_gen(
  File "/usr/lib/python3.8/compiler/static.py", line 6848, in make_code_gen
    code_gen.visit(tree)
  File "/usr/lib/python3.8/compiler/pycodegen.py", line 2815, in visit
    ret = super().visit(node, *args)
  File "/usr/lib/python3.8/compiler/visitor.py", line 70, in visit
    return meth(node, *args)
  File "/usr/lib/python3.8/compiler/static.py", line 6939, in visitModule
    super().visitModule(node)
  File "/usr/lib/python3.8/compiler/pycodegen.py", line 379, in visitModule
    self.visit(self.skip_docstring(node.body))
  File "/usr/lib/python3.8/compiler/pycodegen.py", line 2815, in visit
    ret = super().visit(node, *args)
  File "/usr/lib/python3.8/compiler/visitor.py", line 62, in visit
    return self.walk_list(node, *args)
  File "/usr/lib/python3.8/compiler/visitor.py", line 53, in walk_list
    self.visit(item, *args)
  File "/usr/lib/python3.8/compiler/pycodegen.py", line 2815, in visit
    ret = super().visit(node, *args)
  File "/usr/lib/python3.8/compiler/visitor.py", line 70, in visit
    return meth(node, *args)
  File "/usr/lib/python3.8/compiler/pycodegen.py", line 586, in visitIf
    self.compileJumpIf(test, orelse or end, False)
  File "/usr/lib/python3.8/compiler/static.py", line 7282, in compileJumpIf
    self.get_type(test).emit_jumpif(test, next, is_if_true, self)
  File "/usr/lib/python3.8/compiler/static.py", line 911, in emit_jumpif
    CinderCodeGenerator.compileJumpIf(code_gen, test, next, is_if_true)
  File "/usr/lib/python3.8/compiler/pycodegen.py", line 2277, in compileJumpIf
    self.emitChainedCompareStep(
  File "/usr/lib/python3.8/compiler/static.py", line 7112, in emitChainedCompareStep
    self.visit(value)
  File "/usr/lib/python3.8/compiler/pycodegen.py", line 2815, in visit
    ret = super().visit(node, *args)
  File "/usr/lib/python3.8/compiler/visitor.py", line 70, in visit
    return meth(node, *args)
  File "/usr/lib/python3.8/compiler/visitor.py", line 42, in generic_visit
    for _field, value in ast.iter_fields(node):
  File "/usr/lib/python3.8/ast.py", line 229, in iter_fields
    for field in node._fields:
AttributeError: 'Block' object has no attribute '_fields'

"TypeError: an integer is required" when calling function returning double

seen as of cinder 5bd8c57 (and it occurs prior to that commit)

from __static__ import double

def test() -> double:
    return double(.5)

test()
$ python -m compiler --static foo.py
TypeError: an integer is required

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/lib/python3.8/runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/usr/lib/python3.8/compiler/__main__.py", line 111, in <module>
    exec(codeobj)
  File "/.../foo.py", line 6, in __main__
    test()
SystemError: PyEval_EvalFrameEx returned a result with an error set

(Same error even when test() is not called from module scope.)

Overriding a method with a field raises an unexpected error

What version of Static Python are you using?

4f1b313
2021-07-27

What program did you run?

# override_instance_method_with_field.py
class C:
    def x(self):
        pass
class D(C):
    x: int

What happened?

The program raised a seemly irrelevant error.
AttributeError: 'ContainerTypeRef' object has no attribute 'module'

What should have happened?

We expected a compile-time type error complaining that one cannot override a method with a field. Actually, the trace back, especially the second line of the following quote, shows that StaticPython was trying to report such an error.

    f"class cannot hide inherited member: {value!r}"
  File "/vol/Lib/compiler/static/types.py", line 2245, in __repr__
    return f"<{self.name} '{self.name}' instance, args={self.args}>"
  File "/vol/Lib/compiler/static/types.py", line 1415, in __repr__
    f"<Parameter name={self.name}, ref={self.type_ref}, "
  File "/vol/Lib/compiler/static/types.py", line 260, in __repr__
    return f"TypeRef({self.module.name}, {ast.dump(self.ref)})"
AttributeError: 'ContainerTypeRef' object has no attribute 'module'

Missing builtin modules such as math

Traceback (most recent call last):
File "", line 1, in
File "/usr/lib/python3.8/random.py", line 41, in
from math import log as _log, exp as _exp, pi as _pi, e as _e, ceil as _ceil
ModuleNotFoundError: No module named 'math'

the float type is unbound in a simple program

What version of Static Python are you using?

9965302
2021-07-15

What program did you run?

# float_unbound.py

def f(x: float):
    reveal_type(x)

What happened?

The variable x has type dynamic.

compiler.static.TypedSyntaxError: reveal_type(x): 'dynamic', 'x' has declared type 'dynamic' and local type 'dynamic'

What should have happened?

We expected that the variable x has type float because of the annotation x: float. The function float has a
type and float(1) has the type Exact[float]

Subclassing a final class should give a type error

What program did you run?

from __static__ import int64
class D(int64): pass

What happened?

No error

What should have happened?

Type error because int64 is final.

Currently D(1) gives a compiler assertion error. That's good, but it'd be more helpful to reject the class definition in the first place.

Add compilation statistics for static python

It'd be nice if we had some metrics around when/where we were emitting code in the static compiler which used static features. This could either be full statistics on opcodes used, or just individual stats on when we hit specific features. The compiler should expose some way to enable collecting this data and then we could log it out / dump it on compile.

unsound static-to-static method overrides

What version of Static Python are you using?

9965302
2021-06-24

What program did you run?

class A:
  def m(self) -> str:
    return "hello"

class B(A):
  def m(self) -> int:
    return 0

def main(a:A) -> str:
  return a.m()

print(main(B()))

What happened?

0

What should have happened?

Static error in the child class B because type int is not a subtype of the parent's return type.

Other notes

If B inherited from an untyped child of A, then we might need a runtime check instead.

Static Python: primitive types don't support pow operator

(and there is an exception while assembling the error message)

def test(x: double, y: double) -> double:
    return x ** y
  File "/usr/lib/python3.8/compiler/visitor.py", line 70, in visit
    return meth(node, *args)
  File "/usr/lib/python3.8/compiler/static.py", line 5995, in visitBinOp
    if ltype.bind_binop(node, self, type_ctx):
  File "/usr/lib/python3.8/compiler/static.py", line 4866, in bind_binop
    raise visitor.syntax_error(self.binop_error(self, rtype, node.op), node)
  File "/usr/lib/python3.8/compiler/static.py", line 4398, in binop_error
    return f"cannot {self._op_name[type(op)]} {left.name} and {right.name}"
KeyError: <class '_ast.Pow'>

track python3.8 minor releases

Cinder is on 3.8.5, and there have been countless security and bug fixes up until 3.8.11

is it reasonable for the Cinder project to track minor upstream releases?

I suppose trying to rebase locally before a build may have some chance of success, but I haven't tried it.

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.