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

Implementation of HistogramVec by providing LuaC bindings to Rust module prometheus #502

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

ochaton
Copy link
Member

@ochaton ochaton commented Mar 3, 2025

I suggest you to look at PoC (IMO good enough) which demonstrates power of Lua-bindings to Rust ecosystem, and how it can be used to greatly solve our performance issues in tarantool/metrics.

I've implemented only HistogramVec with slight change of API:

  1. [change] Users must specify label_names on creation of HistogramVec (HistogramVec:new()), and pass all of the values in Histogram:observe(value, <kv-label_pairs>)
  2. [change] HistogramVec:collect() is slower than Histogram:collect() because it has to copy Rust-strings into LuaStrings and allocate many LuaTables. :collect() mainly used for export metrics via http-server, and they basically are converted into String anyway. So HistogramVec:collect_str() can be used for that, it just builds final strings which prometheus can understand.
  3. [change] To allow dynamic reconfiguration of global_label_pairs, Rust Registry is implemented, which stores global_label_pairs. Everywhere we need to reset global_label_pairs we should pass a copy of them into Rust.
  • Tests
  • Changelog
  • Documentation (README and rst)
  • Rockspec and rpm spec

PoC for #461

Motivation - Performance increase (Results)

Scenario Histogram:observe() HistogramVec:observe() Histogram:collect() HistogramVec:collect_str()
No labels 57K/s 3686K/s (+6366%) 103K/s 151K/s (+46%)
1 label 48K/s 1235K/s (+2473%) 53K/s 75K/s (+41%)

No labels - means, Histogram is created without custom labels. Meaning, if User does not use labels, he must not pay for them.

1 label - means, Histogram support single label_key. Mostly used to add boolean ok = false|true label.

Table that follows shows performance increase of overall collect of all histogram collectors.

Lua:collect() Rust:collect()
32K/s 49K/s (+53%)
.rocks/bin/luabench -d 3s
Tarantool version: Tarantool 3.3.0-0-g5fc82b8
Tarantool build: Darwin-arm64-RelWithDebInfo (static)
Tarantool build flags:  -fexceptions -funwind-tables -fasynchronous-unwind-tables -fno-common  -fmacro-prefix-map=/var/folders/8x/1m5v3n6d4mn62g9w_65vvt_r0000gn/T/tarantool_install1980638789=. -std=c11 -Wall -Wextra -Wno-gnu-alignof-expression -Wno-cast-function-type -O2 -g -DNDEBUG -ggdb -O2 
CPU: Apple M1 @ 8
JIT: Disabled
Duration: 3s
Global timeout: 60

--- BENCH: histogram_bench::bench_001_no_labels_001_observe:histogram:observe
  206723             17634 ns/op             56709 op/s     4872 B/op   +960.50MB
--- BENCH: histogram_bench::bench_001_no_labels_001_observe:histogram_vec:observe
13372211               271.3 ns/op         3686003 op/s        0 B/op   +928B


--- BENCH: histogram_bench::bench_001_no_labels_002_collect:histogram:collect
  370499              9687 ns/op            103231 op/s     4239 B/op   +1498.14MB
--- BENCH: histogram_bench::bench_001_no_labels_002_collect:histogram_vec:collect
   91363             39864 ns/op             25085 op/s    21455 B/op   +1869.47MB
--- BENCH: histogram_bench::bench_001_no_labels_002_collect:histogram_vec:collect_str
  549039              6597 ns/op            151582 op/s       96 B/op   +50.27MB

--- BENCH: histogram_bench::bench_002_one_label_001_observe:histogram:observe
  176461             20869 ns/op             47918 op/s     4967 B/op   +836.05MB
--- BENCH: histogram_bench::bench_002_one_label_001_observe:histogram_vec:observe
 4459330               809.3 ns/op         1235665 op/s      192 B/op   +816.53MB



--- BENCH: histogram_bench::bench_002_one_label_002_collect:histogram:collect
  204015             18952 ns/op             52764 op/s     8151 B/op   +1586.09MB
--- BENCH: histogram_bench::bench_002_one_label_002_collect:histogram_vec:collect
   43171             83987 ns/op             11907 op/s    45431 B/op   +1870.49MB
--- BENCH: histogram_bench::bench_002_one_label_002_collect:histogram_vec:collect_str
  273251             13182 ns/op             75861 op/s       96 B/op   +25.02MB


--- BENCH: histogram_bench::bench_003_lua_gather
  113821             31461 ns/op             31785 op/s    13512 B/op   +1466.71MB
--- BENCH: histogram_bench::bench_003_rust_gather
  175913             20369 ns/op             49094 op/s       96 B/op   +16.11MB

Build of Rust part is already orchestrated using CMake, so make test, make .rocks and others should work fine. You can repeat my benchmarks using make bench.

What's next to decide:

  1. Decide should metrics-rs live in tarantool/metrics or it is better to move it into separate repo? (can we change tarantool/metrics API for that)
  2. Bundling into Tarantool binary

@oleggator
Copy link
Contributor

It would be cool to run cargo clippy --fix && cargo fmt and fix according to the lint recommendations.

@ochaton
Copy link
Member Author

ochaton commented Mar 3, 2025

@vasiliy-t as requested, added benchmarks for 2 and 3 labels

Scenario (histograms) Histogram:observe HistogramVec:observe Histogram:collect HistogramVec:collect_str
No labels (1) 56K/s 3667K/s (+6448%) 103K/s 155K/s (+50%)
1 label (2) 49K/s 1225K/s (+2400%) 56K/s 74K/s (+34%)
2 labels (8) 38K/s 990K/s (+2500%) 14K/s 19K/s (+35%)
3 labels (136) 30K/s 796K/s (+2553%) 0.88K/s 1.00K/s (+13%)

For 1 label was used true|false => 2 histograms were created
For 2 labels was used true|false multiplied on GET|PUT|POST|DELETE so 2x4 => 8 histograms were created
For 3 labels was added multiplier by func_name (17 variants) => 8x17 => 136 histograms were created.

Histograms Lua collect Rust gather
3 32K/s 49K/s
141 0.70K/s 0.95K/s (+35%)

In fact, Rust.gather is faster, because it returns final LuaString, which is used as a response body to http client, while Lua collect will build LuaString from LuaTable.

.rocks/bin/luabench -d 3s Tarantool version: Tarantool 3.3.0-0-g5fc82b8 Tarantool build: Darwin-arm64-RelWithDebInfo (static) Tarantool build flags: -fexceptions -funwind-tables -fasynchronous-unwind-tables -fno-common -fmacro-prefix-map=/var/folders/8x/1m5v3n6d4mn62g9w_65vvt_r0000gn/T/tarantool_install1980638789=. -std=c11 -Wall -Wextra -Wno-gnu-alignof-expression -Wno-cast-function-type -O2 -g -DNDEBUG -ggdb -O2 CPU: Apple M1 @ 8 JIT: Disabled Duration: 3s Global timeout: 60

--- BENCH: histogram_bench::bench_001_no_labels_001_observe:histogram:observe
211435 17726 ns/op 56416 op/s 4872 B/op +982.39MB

--- BENCH: histogram_bench::bench_001_no_labels_001_observe:histogram_vec:observe
13200013 272.7 ns/op 3666846 op/s 0 B/op +928B

--- BENCH: histogram_bench::bench_001_no_labels_002_collect:histogram:collect
378676 9632 ns/op 103822 op/s 4239 B/op +1531.21MB

--- BENCH: histogram_bench::bench_001_no_labels_002_collect:histogram_vec:collect
91068 39599 ns/op 25253 op/s 21455 B/op +1863.44MB

--- BENCH: histogram_bench::bench_001_no_labels_002_collect:histogram_vec:collect_str
549500 6461 ns/op 154771 op/s 96 B/op +50.31MB

--- BENCH: histogram_bench::bench_002_one_label_001_observe:histogram:observe
177654 20455 ns/op 48887 op/s 4968 B/op +841.70MB

--- BENCH: histogram_bench::bench_002_one_label_001_observe:histogram_vec:observe
4433361 816.5 ns/op 1224792 op/s 192 B/op +811.77MB

--- BENCH: histogram_bench::bench_002_one_label_002_collect:histogram:collect
203404 17861 ns/op 55989 op/s 8151 B/op +1581.34MB

--- BENCH: histogram_bench::bench_002_one_label_002_collect:histogram_vec:collect
43635 83404 ns/op 11990 op/s 45431 B/op +1890.59MB

--- BENCH: histogram_bench::bench_002_one_label_002_collect:histogram_vec:collect_str
264472 13453 ns/op 74335 op/s 96 B/op +24.22MB

--- BENCH: histogram_bench::bench_002_two_labels_001_observe:histogram:observe
138146 26128 ns/op 38274 op/s 7008 B/op +923.28MB

--- BENCH: histogram_bench::bench_002_two_labels_001_observe:histogram_vec:observe
3365964 1010 ns/op 989804 op/s 200 B/op +642.01MB

--- BENCH: histogram_bench::bench_002_two_labels_002_collect:histogram:collect
53930 68802 ns/op 14534 op/s 31783 B/op +1634.70MB

--- BENCH: histogram_bench::bench_002_two_labels_002_collect:histogram_vec:collect
10000 363595 ns/op 2750 op/s 201064 B/op +1917.50MB

--- BENCH: histogram_bench::bench_002_two_labels_002_collect:histogram_vec:collect_str
67051 53058 ns/op 18847 op/s 96 B/op +6.15MB

--- BENCH: histogram_bench::bench_003_three_labels_001_observe:histogram:observe
111010 32774 ns/op 30512 op/s 7136 B/op +755.56MB

--- BENCH: histogram_bench::bench_003_three_labels_001_observe:histogram_vec:observe
2870087 1256 ns/op 796019 op/s 256 B/op +700.71MB

--- BENCH: histogram_bench::bench_003_three_labels_002_collect:histogram:collect
3130 1147022 ns/op 871.8 op/s 532376 B/op +1589.24MB

--- BENCH: histogram_bench::bench_003_three_labels_002_collect:histogram_vec:collect
541 6734926 ns/op 148.5 op/s 3615480 B/op +1866.23MB

--- BENCH: histogram_bench::bench_003_three_labels_002_collect:histogram_vec:collect_str
3618 992868 ns/op 1007 op/s 143 B/op +507.35KB

--- BENCH: histogram_bench::bench_004_lua_gather
2541 1416612 ns/op 705.9 op/s 642085 B/op +1556.39MB

--- BENCH: histogram_bench::bench_004_rust_gather
3406 1048686 ns/op 953.6 op/s 149 B/op +496.69KB

Copy link
Member

@DifferentialOrange DifferentialOrange left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't know a thing about rust, tests seem convincing.

The main issue will be embedding this one to the tarantool core since, after 2.11.1, tarantool has metrics as a built-in package, and adding rust to a core build system will likely be a bother (both technically and ideologically).

@ochaton
Copy link
Member Author

ochaton commented Mar 4, 2025

As @ligurio mentioned, benchmark was made on Apple @ M1 with disabled jit.

I've rerun benchmark on VK Cloud VM (4 vCPU, 4Gb RAM, no drive used, Load-Average before benchmark ≈ 0.21). Results below. Unfortunately, I was able to install Tarantool 2.10.7

Scenario (histograms) Histogram:observe HistogramVec:observe Histogram:collect HistogramVec:collect_str
No labels (1) 45K/s 4562K/s (+10037%) 66K/s 150K/s
1 label (2) 38K/s 1252K/s (+3200%) 34K/s 72K/s
2 labels (8) 26K/s 1044K/s (+3950%) 9K/s 17K/s
3 labels (136) 21K/s 810K/s (+3757%) 0.50K/s 0.85K/s
.rocks/bin/luabench -d 3s

Tarantool version: Tarantool 2.10.7-0-g60f7e18

Tarantool build: Linux-x86_64-RelWithDebInfo (static)

Tarantool build flags: -static-libstdc++ -fexceptions -funwind-tables -fno-common -fopenmp -msse2 -Wformat -Wformat-security -Werror=format-security -fstack-protector-strong -fPIC -fmacro-prefix-map=/tmp/tarantool_install35630681=. -std=c11 -Wall -Wextra -Wno-strict-aliasing -Wno-char-subscripts -Wno-format-truncation -Wno-gnu-alignof-expression -fno-gnu89-inline -Wno-cast-function-type

CPU: Intel Xeon Processor (Skylake, IBRS) @ 2095.076MHz

JIT: Enabled
JIT: SSE2 SSE3 SSE4.1 BMI2 fold cse dce fwd dse narrow loop abc sink fuse

Duration: 3s
Global timeout: 60

--- BENCH: histogram_bench::bench_001_no_labels_001_observe:histogram:observe
158064 22219 ns/op 45007 op/s 3336 B/op +502.88MB

--- BENCH: histogram_bench::bench_001_no_labels_001_observe:histogram_vec:observe
15454017 219.2 ns/op 4561605 op/s 0 B/op +2.09KB

--- BENCH: histogram_bench::bench_001_no_labels_002_collect:histogram:collect
243817 15046 ns/op 66463 op/s 3431 B/op +798.02MB

--- BENCH: histogram_bench::bench_001_no_labels_002_collect:histogram_vec:collect
57085 66962 ns/op 14934 op/s 14735 B/op +802.24MB

--- BENCH: histogram_bench::bench_001_no_labels_002_collect:histogram_vec:collect_str
567925 6647 ns/op 150441 op/s 64 B/op +34.67MB

--- BENCH: histogram_bench::bench_002_one_label_001_observe:histogram:observe
141094 26487 ns/op 37754 op/s 3432 B/op +461.80MB

--- BENCH: histogram_bench::bench_002_one_label_001_observe:histogram_vec:observe
4669480 799.0 ns/op 1251566 op/s 128 B/op +570.01MB

--- BENCH: histogram_bench::bench_002_one_label_002_collect:histogram:collect
129976 29411 ns/op 34001 op/s 6663 B/op +826.04MB

--- BENCH: histogram_bench::bench_002_one_label_002_collect:histogram_vec:collect
25214 144683 ns/op 6912 op/s 31191 B/op +750.04MB

--- BENCH: histogram_bench::bench_002_one_label_002_collect:histogram_vec:collect_str
284007 13806 ns/op 72434 op/s 64 B/op +17.34MB

--- BENCH: histogram_bench::bench_002_two_labels_001_observe:histogram:observe
94990 37986 ns/op 26326 op/s 5471 B/op +495.71MB

--- BENCH: histogram_bench::bench_002_two_labels_001_observe:histogram_vec:observe
3550093 957.6 ns/op 1044245 op/s 136 B/op +460.45MB

--- BENCH: histogram_bench::bench_002_two_labels_002_collect:histogram:collect
32208 108813 ns/op 9190 op/s 26215 B/op +805.25MB

--- BENCH: histogram_bench::bench_002_two_labels_002_collect:histogram_vec:collect
6054 691280 ns/op 1447 op/s 140740 B/op +812.59MB

--- BENCH: histogram_bench::bench_002_two_labels_002_collect:histogram_vec:collect_str
60323 58229 ns/op 17173 op/s 64 B/op +3.69MB

--- BENCH: histogram_bench::bench_003_three_labels_001_observe:histogram:observe
80040 46784 ns/op 21375 op/s 5600 B/op +427.53MB

--- BENCH: histogram_bench::bench_003_three_labels_001_observe:histogram_vec:observe
2976822 1234 ns/op 810304 op/s 192 B/op +545.07MB

--- BENCH: histogram_bench::bench_003_three_labels_002_collect:histogram:collect
1584 1996099 ns/op 501.0 op/s 439584 B/op +664.37MB

--- BENCH: histogram_bench::bench_003_three_labels_002_collect:histogram_vec:collect
262 13684450 ns/op 73.08 op/s 2528277 B/op +631.90MB

--- BENCH: histogram_bench::bench_003_three_labels_002_collect:histogram_vec:collect_str
3187 1177284 ns/op 849.4 op/s 118 B/op +368.13KB

--- BENCH: histogram_bench::bench_004_lua_gather
1455 2311810 ns/op 432.6 op/s 541553 B/op +751.75MB

--- BENCH: histogram_bench::bench_004_rust_gather
2978 1199541 ns/op 833.7 op/s 124 B/op +363.41KB

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.

3 participants