1
1
mirror of https://github.com/anoma/juvix.git synced 2024-12-02 10:47:32 +03:00
Commit Graph

3 Commits

Author SHA1 Message Date
Paul Cadman
87c5f0af44
Improve performance of anomaEncode / anomaDecode in the Core evaluator (#2975)
This PR:

* Adds a new implementation of {decode, encode}ByteString functions,
used by anomaEncode and anomaDecode in the Core evaluator
* Adds property tests for roundtripping and benchmarks for the new
functions.

The old implementation used
[bitvec](https://hackage.haskell.org/package/bitvec) to manipulate the
ByteString. This was far too slow. The new implementation uses bit
operations directly on the input integer and ByteArray.

It's now possible to run
[anoma-app-patterns:`Tests/Swap.juvix`](https://github.com/anoma/anoma-app-patterns/blob/feature/tests/Tests/Swap.juvix)
to completion.

For encoding, if the size of the output integer exceeds 64 bits (and
therefore a BigInt must be used) then the new implementation has
quadratic time complexity in the number of input bytes if an
implementation of `ByteString -> Integer` is used as follows:

```
byteStringToIntegerLE :: ByteString -> Integer
byteStringToIntegerLE = BS.foldr (\b acc -> acc `shiftL` 8 .|. fromIntegral b) 0
```

```
byteStringToInteger' :: ByteString -> Integer
byteStringToInteger' = BS.foldl' (\acc b -> acc `shiftL` 8 .|. fromIntegral b) 0

```

I think this is because `shiftL` is expensive for large Integers. To
mitigate this I'm splitting the input ByteString into 1024 byte chunks
and processing each separately. Using this we get 100x speed up at
~0.25Mb input over the non-chunked approach and linear time-complexity
thereafter.

## Benchmarks

The benchmarks for encoding and decoding 250000 bytes:

```
 ByteString Encoding to/from integer
      encode bytes to integer:   OK
        59.1 ms ± 5.3 ms
      decode bytes from integer: OK
        338  ms ±  16 ms
```

The previous implementation would never complete for this input.

Benchmarks for encoding and decoding 2 * 250000 bytes:

```
    ByteString Encoding to/from integer
      encode bytes to integer:   OK
        121  ms ± 8.3 ms
      decode bytes from integer: OK
        651  ms ±  27 ms
```

Benchmarks for encoding and decoding 4 * 250000 bytes:

```
    ByteString Encoding to/from integer
      encode bytes to integer:   OK
        249  ms ±  17 ms
      decode bytes from integer: OK
        1.317 s ±  16 ms
```

---------

Co-authored-by: Lukasz Czajka <lukasz@heliax.dev>
2024-08-30 18:20:18 +01:00
Jan Mas Rovira
73364f4887
Nockma compile (#2570)
This PR is a snapshot of the current work on the JuvixAsm -> Nockma
translation. The compilation of Juvix programs to Nockma now works so we
decided to raise this PR now to avoid it getting too large.

## Juvix -> Nockma compilation

You can compile a frontend Juvix file to Nockma as follows:

example.juvix
```
module example;

import Stdlib.Prelude open;

fib : Nat → Nat → Nat → Nat
  | zero x1 _ := x1
  | (suc n) x1 x2 := fib n x2 (x1 + x2);

fibonacci (n : Nat) : Nat := fib n 0 1;

sumList (xs : List Nat) : Nat :=
  for (acc := 0) (x in xs)
    acc + x;

main : Nat := fibonacci 9 + sumList [1; 2; 3; 4];
```

```
$ juvix compile -t nockma example.juvix
```

This will generate a file `example.nockma` which can be run using the
nockma evaluator:

```
$ juvix dev nockma eval example.nockma
```

Alternatively you can compile JuvixAsm to Nockma:

```
$ juvix dev asm compile -t nockma example.jva
```

## Tests

We compile an evaluate the JuvixAsm tests in
cb3659e08e/test/Nockma/Compile/Asm/Positive.hs

We currently skip some because either:
1. They are too slow to run in the current evaluator (due to arithmetic
operations using the unjetted nock code from the anoma nock stdlib).
2. They trace data types like lists and booleans which are represented
differently by the asm interpreter and the nock interpreter
3. They operate on raw negative numbers, nock only supports raw natural
numbers

## Next steps

On top of this PR we will work on improving the evaluator so that we can
enable the slow compilation tests.

---------

Co-authored-by: Paul Cadman <git@paulcadman.dev>
Co-authored-by: Lukasz Czajka <lukasz@heliax.dev>
2024-01-17 11:15:38 +01:00
Paul Cadman
a9995b8e1c
Add nockma evaluator (#2564)
This PR adds an parser, pretty printer, evaluator, repl and quasi-quoter
for Nock terms.

## Parser / Pretty Printer

The parser and pretty printer handle both standard Nock terms and
'pretty' Nock terms (where op codes and paths can be named). Standard
and pretty Nock forms can be mixed in the same term.

For example instead of `[0 2]` you can write `[@ L]`.

See
a6028b0d92/src/Juvix/Compiler/Nockma/Language.hs (L79)
for the correspondence between pretty Nock and Nock operators.

In pretty Nock, paths are represented as strings of `L` (for head) and
`R` (for tail) instead of the number encoding in standard nock. The
character `S` is used to refer to the whole subject, i.e it is sugar for
`1` in standard Nock.

See
a6028b0d92/src/Juvix/Compiler/Nockma/Language.hs (L177)
for the correspondence between pretty Nock path and standard Nock
position.

## Quasi-quoter

A quasi-quoter is added so Nock terms can be included in the source, e.g
`[nock| [@ LL] |]`.

## REPL

Launch the repl with `juvix dev nockma repl`.

A Nock `[subject formula]` cell is input as `subject / formula` , e.g:

```
nockma>  [1 0] / [@ L]
1
```

The subject can be set using `:set-stack`.

```
nockma> :set-stack [1 0]
nockma> [@ L]
1
```

The subject can be viewed using `:get-stack`.

```
nockma> :set-stack [1 0]
nockma> :get-stack
[1 0]
```

You can assign a Nock term to a variable and use it in another
expression:

```
nockma> r := [@ L]
nockma> [1 0] / r
1
```

A list of assignments can be read from a file:

```
$ cat stack.nock
r := [@ L]
$ juvix dev nockma repl
nockma> :load stack.nock
nockma> [1 0] / r
1
```

* Closes https://github.com/anoma/juvix/issues/2557

---------

Co-authored-by: Jan Mas Rovira <janmasrovira@gmail.com>
Co-authored-by: Lukasz Czajka <lukasz@heliax.dev>
2024-01-11 12:04:38 +00:00