deadtrickster / prometheus.erl

Prometheus.io client in Erlang
MIT License
341 stars 117 forks source link

Expirement with cas for doubles #62

Open deadtrickster opened 7 years ago

deadtrickster commented 7 years ago

Now for handling doubles prometheus.erl uses gen_server (because there is no ets:update_counter for doubles). This PR intended to provide nif based alternative for gen_server.

Benchmark for ets:update_counter-based counter:

Running single counter benchmark:
  Pids: 1
  Iterations: 1000000
  Expected Value: 1000000
  Duration (sec): 0.22 (100.0%)
  Counter value: 1000000
Running single counter benchmark:
  Pids: 5
  Iterations: 1000000
  Expected Value: 6000000
  Duration (sec): 0.398 (100.0%)
  Counter value: 6000000
Running single counter benchmark:
  Pids: 10
  Iterations: 1000000
  Expected Value: 16000000
  Duration (sec): 0.74 (100.0%)
  Counter value: 16000000
Running single counter benchmark:
  Pids: 100
  Iterations: 1000000
  Expected Value: 116000000
  Duration (sec): 7.231 (100.0%)
  Counter value: 116000000
Finished

Results for dinc (without call_timeout adjusted):

Running single counter benchmark:
  Pids: 1
  Iterations: 1000000
  Expected Value: 1000000
  Duration (sec): 0.885 (100.0%)
  Counter value: 1000000
Running single counter benchmark:
  Pids: 5
  Iterations: 1000000
  Expected Value: 6000000
  Duration (sec): 7.3 (100.0%))
  Counter value: 2182338
Running single counter benchmark:
  Pids: 10
  Iterations: 1000000
  Expected Value: 16000000
  Duration (sec): 15.367 (100.0%)
  Counter value: 7739012

Results for double + atomic_compare_exchange_strong

Running single counter benchmark:
  Pids: 1
  Iterations: 1000000
  Expected Value: 1000000
  Duration (sec): 0.043 (100.0%)
  Counter value: 1000000
Running single counter benchmark:
  Pids: 5
  Iterations: 1000000
  Expected Value: 6000000
  Duration (sec): 0.382 (100.0%)
  Counter value: 6000000
Running single counter benchmark:
  Pids: 10
  Iterations: 1000000
  Expected Value: 16000000
  Duration (sec): 0.79 (100.0%)
  Counter value: 16000000
Running single counter benchmark:
  Pids: 100
  Iterations: 1000000
  Expected Value: 116000000
  Duration (sec): 8.237 (100.0%)
  Counter value: 116000000
Finished

cc @benoitc

deadtrickster commented 7 years ago

So there is no big speed difference, and in theory dincs can be removed entirely. OTOH doubles are finite on both ERTS and C sides while int counters currently not. And we going NIF-way inc can be implemented with fetch_and_add.

However: CAS loop can be very long - we can block scheduler. Current NIF benchmark doesn't perform counter resolving - this has to be done as ETS lookup with possible optimization (like Benoit does) - provide with wrapper and store resource in process-dict. Also on lookups - first metric use looks now like this: inc -> badarg -> insert_metric, Next call: just inc (ets:update_counter) first call when using nif: inc -> no_resource -> create_resource -> insert to ets -> increment Next call -> ets:lookup -> increment. And with replaces ets:lookup with get and adds put to the first call.

deadtrickster commented 7 years ago

Since dirty nifs are experimental I need to limit number of cas iterations and add loop inside incr_counter on erlang side to allow context switching