Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alternative implementation based on an OwnedEnv. #1

Merged
merged 2 commits into from
Oct 2, 2020

Conversation

Qqwy
Copy link
Contributor

@Qqwy Qqwy commented Oct 1, 2020

I have no clue as to the performance of this implementation, but it at least is able to store Erlang references correctly and it does not leak memory.

c.f. chrrasmussen/Idris2-Erlang#4

@chrrasmussen
Copy link
Owner

Wow, this is incredible! 😃

I will try it out it in the Idris 2 compiler later today. Thank you so much! ❤️

The outer ResourceArc is enough so we do not need the extra Arc.
Naming of the new resource datastructure has been altered for clarity.

It's now a 'MutableTermBox'.
@chrrasmussen chrrasmussen merged commit 075bdcd into chrrasmussen:ioref-test Oct 2, 2020
@chrrasmussen
Copy link
Owner

chrrasmussen commented Oct 2, 2020

Thanks for this change! ❤️ I noticed that the pull request went into the ioref-test branch. I intended for the commits to go into master branch. To fix it, I cherry-picked the commits to the master branch. Hope that's okay.

I have done a little bit of testing on this branch, and it might appear that there is a performance bottleneck. I have not done any profiling yet. I will look into it tomorrow.

To make testing easier I have added an option to idris2erl called --directive mutablestorage. When this option is present, idris2erl will generate code for Data.IORef that uses an Erlang module called mutable_storage. Otherwise, it will use the process dictionary (as before).

In the erlang branch of this repo I have modified the NIF to work with an Erlang module called mutable_storage.

How to run mutable_storage from code generated by Idris 2

If you want to try it out, you can follow these steps:

Create an Idris module called Main.idr with the following content:

import Data.IORef

main : IO ()
main = do
  x <- newIORef 42
  let y = x
  writeIORef y 94
  val <- readIORef x
  printLn val
  val <- readIORef y
  printLn val
  modifyIORef x (* 2)
  val <- readIORef x
  printLn val
  val <- readIORef y
  printLn val

Build main.beam by running: idris2erl -o main --output-dir . --directive mutablestorage Main.idr

From the mutable_storage repo, branch erlang:

  • Copy lib/mutable_storage.erl to ./mutable_storage.erl in the same directory as Main.idr. Compile it by running erlc mutable_storage.erl
  • Copy the built NIF library from priv/native/libmutable_storage.so to priv/native/libmutable_storage.so in the same directory as Main.idr

Run the program using: escript main.beam

The setup is a bit inconvenient, but it should be possible to package this up in a better way later.

@chrrasmussen
Copy link
Owner

I have now done some benchmarking. See bench.exs in the ioref-benchmark branch.

To run the benchmark:

  1. mix deps.get
  2. mix run bench.exs

Here are the results that I got:

Operating System: macOS
CPU Information: Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz
Number of Available Cores: 16
Available memory: 32 GB
Elixir 1.10.3
Erlang 22.2.1

Benchmark suite executing with the following configuration:
warmup: 2 s
time: 10 s
memory time: 2 s
parallel: 1
inputs: Big value, Small value
Estimated total run time: 1.87 min

Benchmarking MutableStorage (Only read) with input Big value...
Benchmarking MutableStorage (Only read) with input Small value...
Benchmarking MutableStorage (Only write) with input Big value...
Benchmarking MutableStorage (Only write) with input Small value...
Benchmarking MutableStorage (Read/write) with input Big value...
Benchmarking MutableStorage (Read/write) with input Small value...
Benchmarking Process dict (Read/write) with input Big value...
Benchmarking Process dict (Read/write) with input Small value...

##### With input Big value #####
Name                                  ips        average  deviation         median         99th %
Process dict (Read/write)        14994.48      0.0667 ms   ±155.41%      0.0470 ms        0.67 ms
MutableStorage (Only write)         87.62       11.41 ms     ±5.04%       11.27 ms       13.67 ms
MutableStorage (Read/write)         37.41       26.73 ms     ±6.76%       26.35 ms       34.09 ms
MutableStorage (Only read)          26.18       38.20 ms    ±12.02%       37.22 ms       67.05 ms

Comparison: 
Process dict (Read/write)        14994.48
MutableStorage (Only write)         87.62 - 171.14x slower +11.35 ms
MutableStorage (Read/write)         37.41 - 400.81x slower +26.66 ms
MutableStorage (Only read)          26.18 - 572.76x slower +38.13 ms

Memory usage statistics:

Name                           Memory usage
Process dict (Read/write)         0.0490 MB
MutableStorage (Only write)       0.0197 MB - 0.40x memory usage -0.02934 MB
MutableStorage (Read/write)        34.27 MB - 699.26x memory usage +34.22 MB
MutableStorage (Only read)         44.66 MB - 911.27x memory usage +44.61 MB

**All measurements for memory usage were the same**

##### With input Small value #####
Name                                  ips        average  deviation         median         99th %
Process dict (Read/write)         15.00 K      0.0667 ms   ±155.40%      0.0470 ms        0.67 ms
MutableStorage (Only read)         1.21 K        0.83 ms     ±4.71%        0.82 ms        0.98 ms
MutableStorage (Only write)        0.75 K        1.33 ms     ±4.46%        1.32 ms        1.57 ms
MutableStorage (Read/write)        0.44 K        2.28 ms     ±3.66%        2.27 ms        2.67 ms

Comparison: 
Process dict (Read/write)         15.00 K
MutableStorage (Only read)         1.21 K - 12.40x slower +0.76 ms
MutableStorage (Only write)        0.75 K - 19.91x slower +1.26 ms
MutableStorage (Read/write)        0.44 K - 34.22x slower +2.21 ms

Memory usage statistics:

Name                           Memory usage
Process dict (Read/write)          44.98 KB
MutableStorage (Only read)         52.63 KB - 1.17x memory usage +7.65 KB
MutableStorage (Only write)        21.53 KB - 0.48x memory usage -23.45313 KB
MutableStorage (Read/write)        51.84 KB - 1.15x memory usage +6.86 KB

**All measurements for memory usage were the same**

Overall, the NIF is quite a bit slower than using the process dictionary 😕

Even if the performance is not the greatest, this NIF can still serve a purpose: Avoid leaking memory.

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.

2 participants