Skip to content

Add a ForthMachine to the codebase, copying from 'studies'.#648

Merged
jpivarski merged 57 commits into
mainfrom
jpivarski/add-forth-machine-to-codebase
Jan 16, 2021
Merged

Add a ForthMachine to the codebase, copying from 'studies'.#648
jpivarski merged 57 commits into
mainfrom
jpivarski/add-forth-machine-to-codebase

Conversation

@jpivarski

@jpivarski jpivarski commented Jan 13, 2021

Copy link
Copy Markdown
Member
  • Make stubs of all the necessary files.
  • Make stubs of all the necessary functions.
  • Pass through the public functions to Python, so that developments can be tested from Python.
  • Connect those inputs and outputs to Python.
  • Rebuild input/output framework, so that declaration-only programs can be run.
  • Develop parser and test output with bytecodes and assembly_instructions.
  • Set up stepped-running with a generator interface in Python.
  • Fill in runtime machine.
  • Ensure that we can obtain 5 ns per instruction again.
  • Set up example to parse TTree data from Uproot. (Test is gated by pytest.importorskip("uproot").)

Full-scale ROOT parsing measurements are not part of this PR.

Nor is the Avro → Awkward example.

@jpivarski jpivarski marked this pull request as draft January 13, 2021 00:08
… OLD, VTABLES-based version is up to 1.8x faster.
…, and calling dictionary words from outside the ForthMachine.
…pause and simplified raise/ignore boolean handling.
@jpivarski jpivarski marked this pull request as ready for review January 16, 2021 02:19
@jpivarski

Copy link
Copy Markdown
Member Author

I verified that we still have the 5 ns per instruction.

By contrast, Python's virtual machine on compiled bytecodes (similar in kind to what we do) takes ~900 ns per instruction.

>>> import dis
>>> def f():
...   x = 0
...   for i in range(100000):
...     x = 10 + x + 10 + x + 10 + x + 10 + x + 10 + x + 10
... 
>>> # There are 25 Python bytecode instructions in this loop, which gets repeated 100000 times.
>>> dis.dis(f)
  2           0 LOAD_CONST               1 (0)
              2 STORE_FAST               0 (x)

  3           4 LOAD_GLOBAL              0 (range)
              6 LOAD_CONST               2 (100000)
              8 CALL_FUNCTION            1
             10 GET_ITER
        >>   12 FOR_ITER                48 (to 62)
             14 STORE_FAST               1 (i)

  4          16 LOAD_CONST               3 (10)
             18 LOAD_FAST                0 (x)
             20 BINARY_ADD
             22 LOAD_CONST               3 (10)
             24 BINARY_ADD
             26 LOAD_FAST                0 (x)
             28 BINARY_ADD
             30 LOAD_CONST               3 (10)
             32 BINARY_ADD
             34 LOAD_FAST                0 (x)
             36 BINARY_ADD
             38 LOAD_CONST               3 (10)
             40 BINARY_ADD
             42 LOAD_FAST                0 (x)
             44 BINARY_ADD
             46 LOAD_CONST               3 (10)
             48 BINARY_ADD
             50 LOAD_FAST                0 (x)
             52 BINARY_ADD
             54 LOAD_CONST               3 (10)
             56 BINARY_ADD
             58 STORE_FAST               0 (x)
             60 JUMP_ABSOLUTE           12
        >>   62 LOAD_CONST               0 (None)
             64 RETURN_VALUE

>>> begintime = time.time(); f(); print((time.time() - begintime) * 1e9)
2324416875.8392334
>>> 2324416875.8392334 / (25*100000)
929.7667503356934

>>> begintime = time.time(); f(); print((time.time() - begintime) * 1e9)
2277071237.564087
>>> 2277071237.564087 / (25*100000)
910.8284950256348

>>> begintime = time.time(); f(); print((time.time() - begintime) * 1e9)
2262091398.2391357
>>> 2262091398.2391357 / (25*100000)
904.8365592956543

@jpivarski jpivarski merged commit 86d3853 into main Jan 16, 2021
@jpivarski jpivarski deleted the jpivarski/add-forth-machine-to-codebase branch January 16, 2021 02:23
jpivarski added a commit that referenced this pull request Jan 22, 2021
jpivarski added a commit that referenced this pull request Jan 22, 2021
@henryiii henryiii mentioned this pull request Jan 26, 2021
@jpivarski

Copy link
Copy Markdown
Member Author

For the future: the calculation in #648 (comment) is wrong! Since x increases throughout the loop, it gets bigger than any int32 or int64, and most of the time measured is in handling bigint (arbitrary precision) arithmetic, rather than stepping through Python bytecodes.

A function like this:

def f():
    x = 0
    for i in range(100000):
        x = 10 + x - 10 + x + 10 + x - 10 + x + 10 + x - 10

would walk through the same number of bytecodes (see the dis.dis(f)) but the final result is about 3 ns per instruction!

See the bottom of https://github.com/jpivarski-talks/2024-08-19-python-school-setting-stage/blob/main/snake-eats-its-tail.ipynb

This is still about 40× slower than the throughput of compiled instructions (at -O0 for apples-to-apples), which is about the right order of magnitude. Compiled code can get even faster by taking advantage of vectorization and such, but this is the closest comparison that I think makes sense.

Anyway, no more saying, "Python takes 900 ns per instruction"! It takes about 3 ns per instruction. Incidentally, that's on par with AwkwardForth. The difference between Python and AwkwardForth must be due to something else. (Type checks?)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant