Skip to content

Commit 98bcce4

Browse files
committed
Optimise histograms
1 parent 78d9d1b commit 98bcce4

File tree

2 files changed

+58
-44
lines changed

2 files changed

+58
-44
lines changed

src/metrics/prometheus_histogram.erl

Lines changed: 32 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -240,27 +240,24 @@ Raises:
240240
Count :: integer().
241241
observe_n(Registry, Name, LabelValues, Value, Count) when is_integer(Value), is_integer(Count) ->
242242
Key = key(Registry, Name, LabelValues),
243-
case ets:lookup(?TABLE, Key) of
244-
[Metric] ->
245-
BucketPosition = calculate_histogram_bucket_position(Metric, Value),
246-
ets:update_counter(
247-
?TABLE,
248-
Key,
249-
[
250-
{?ISUM_POS, Value * Count},
251-
{?BUCKETS_START + BucketPosition, Count}
252-
]
253-
);
254-
[] ->
243+
try ets:lookup_element(?TABLE, Key, ?BOUNDS_POS) of
244+
Buckets ->
245+
BucketPosition = prometheus_buckets:position(Buckets, Value),
246+
Spec = [{?ISUM_POS, Value * Count}, {?BUCKETS_START + BucketPosition, Count}],
247+
ets:update_counter(?TABLE, Key, Spec)
248+
catch
249+
error:badarg ->
255250
insert_metric(Registry, Name, LabelValues, Value, Count, fun observe_n/5)
256251
end,
257252
ok;
258253
observe_n(Registry, Name, LabelValues, Value, Count) when is_float(Value), is_integer(Count) ->
259254
Key = key(Registry, Name, LabelValues),
260-
case ets:lookup(?TABLE, Key) of
261-
[Metric] ->
262-
fobserve_impl(Key, Metric, Value, Count);
263-
[] ->
255+
try ets:lookup_element(?TABLE, Key, ?BOUNDS_POS) of
256+
Buckets ->
257+
BucketPosition = prometheus_buckets:position(Buckets, Value),
258+
fobserve_impl(Key, Buckets, BucketPosition, Value, Count)
259+
catch
260+
error:badarg ->
264261
insert_metric(Registry, Name, LabelValues, Value, Count, fun observe_n/5)
265262
end;
266263
observe_n(_Registry, _Name, _LabelValues, Value, Count) when is_number(Value) ->
@@ -438,7 +435,6 @@ Raises:
438435
{number(), infinity | number()} | undefined.
439436
value(Registry, Name, LabelValues) ->
440437
MF = prometheus_metric:check_mf_exists(?TABLE, Registry, Name, LabelValues),
441-
442438
RawValues = [
443439
ets:lookup(?TABLE, {Registry, Name, LabelValues, Scheduler})
444440
|| Scheduler <- schedulers_seq()
@@ -535,11 +531,6 @@ insert_metric(Registry, Name, LabelValues, Value, Count, CB) ->
535531
insert_placeholders(Registry, Name, LabelValues),
536532
CB(Registry, Name, LabelValues, Value, Count).
537533

538-
fobserve_impl(Key, Metric, Value, Count) ->
539-
Buckets = metric_buckets(Metric),
540-
BucketPos = calculate_histogram_bucket_position(Metric, Value),
541-
fobserve_impl(Key, Buckets, BucketPos, Value, Count).
542-
543534
fobserve_impl(Key, Buckets, BucketPos, Value, Count) ->
544535
ets:select_replace(?TABLE, generate_select_replace(Key, Buckets, BucketPos, Value, Count)).
545536

@@ -551,33 +542,33 @@ insert_placeholders(Registry, Name, LabelValues) ->
551542
prometheus_time:maybe_convert_to_native(DU, Bucket)
552543
end,
553544
BoundCounters = lists:duplicate(length(MFBuckets), 0),
545+
Buckets = list_to_tuple(lists:map(Fun, MFBuckets)),
554546
MetricSpec =
555-
[key(Registry, Name, LabelValues), lists:map(Fun, MFBuckets), 0, 0] ++
556-
BoundCounters,
547+
[key(Registry, Name, LabelValues), Buckets, 0, 0 | BoundCounters],
557548
ets:insert_new(?TABLE, list_to_tuple(MetricSpec)).
558549

559-
calculate_histogram_bucket_position(Metric, Value) ->
560-
Buckets = metric_buckets(Metric),
561-
prometheus_buckets:position(Buckets, Value).
562-
563550
generate_select_replace(Key, Bounds, BucketPos, Value, Count) ->
564551
BoundPlaceholders = gen_query_bound_placeholders(Bounds),
565-
HistMatch = list_to_tuple([Key, '$2', '$3', '$4'] ++ BoundPlaceholders),
552+
HistMatch = list_to_tuple([Key, '$2', '$3', '$4' | BoundPlaceholders]),
566553
BucketUpdate =
567554
lists:sublist(BoundPlaceholders, BucketPos) ++
568-
[{'+', gen_query_placeholder(?BUCKETS_START + BucketPos), Count}] ++
569-
lists:nthtail(BucketPos + 1, BoundPlaceholders),
570-
HistUpdate = list_to_tuple([{Key}, '$2', '$3', {'+', '$4', Value * Count}] ++ BucketUpdate),
555+
[
556+
{'+', gen_query_placeholder(?BUCKETS_START + BucketPos), Count}
557+
| lists:nthtail(BucketPos + 1, BoundPlaceholders)
558+
],
559+
HistUpdate = list_to_tuple([{Key}, '$2', '$3', {'+', '$4', Value * Count} | BucketUpdate]),
571560
[{HistMatch, [], [{HistUpdate}]}].
572561

573-
buckets_seq(Buckets) ->
574-
lists:seq(?BUCKETS_START, ?BUCKETS_START + length(Buckets) - 1).
562+
buckets_seq(Buckets) when is_list(Buckets) ->
563+
lists:seq(?BUCKETS_START, ?BUCKETS_START + length(Buckets) - 1);
564+
buckets_seq(Buckets) when is_tuple(Buckets) ->
565+
lists:seq(?BUCKETS_START, ?BUCKETS_START + tuple_size(Buckets) - 1).
575566

576567
generate_update_spec(Buckets) ->
577568
[{Index, 0} || Index <- buckets_seq(Buckets)].
578569

579570
gen_query_placeholder(Index) ->
580-
list_to_atom("$" ++ integer_to_list(Index)).
571+
list_to_atom([$$ | integer_to_list(Index)]).
581572

582573
gen_query_bound_placeholders(Buckets) ->
583574
[gen_query_placeholder(Index) || Index <- buckets_seq(Buckets)].
@@ -586,9 +577,9 @@ augment_counters([Start | Counters]) ->
586577
augment_counters(Counters, [Start], Start).
587578

588579
augment_counters([], LAcc, _CAcc) ->
589-
LAcc;
580+
lists:reverse(LAcc);
590581
augment_counters([Counter | Counters], LAcc, CAcc) ->
591-
augment_counters(Counters, LAcc ++ [CAcc + Counter], CAcc + Counter).
582+
augment_counters(Counters, [CAcc + Counter | LAcc], CAcc + Counter).
592583

593584
metric_buckets(Metric) ->
594585
element(?BOUNDS_POS, Metric).
@@ -599,7 +590,7 @@ reduce_buckets_counters(Metrics) ->
599590
sub_tuple_to_list(
600591
Metric,
601592
?BUCKETS_START,
602-
?BUCKETS_START + length(metric_buckets(Metric))
593+
?BUCKETS_START + tuple_size(metric_buckets(Metric))
603594
)
604595
|| Metric <- Metrics
605596
],
@@ -629,17 +620,17 @@ create_histogram_metric(CLabels, Labels, DU, Bounds, LabelValues, [ISum, FSum |
629620

630621
load_all_values(Registry, Name, Bounds) ->
631622
BoundPlaceholders = gen_query_bound_placeholders(Bounds),
632-
QuerySpec = [{Registry, Name, '$1', '_'}, '_', '$3', '$4'] ++ BoundPlaceholders,
623+
QuerySpec = [{Registry, Name, '$1', '_'}, '_', '$3', '$4' | BoundPlaceholders],
633624
ets:match(?TABLE, list_to_tuple(QuerySpec)).
634625

635626
deregister_select(Registry, Name, Buckets) ->
636627
BoundCounters = lists:duplicate(length(Buckets), '_'),
637-
MetricSpec = [{Registry, Name, '_', '_'}, '_', '_', '_'] ++ BoundCounters,
628+
MetricSpec = [{Registry, Name, '_', '_'}, '_', '_', '_' | BoundCounters],
638629
[{list_to_tuple(MetricSpec), [], [true]}].
639630

640631
delete_metrics(Registry, Buckets) ->
641632
BoundCounters = lists:duplicate(length(Buckets), '_'),
642-
MetricSpec = [{Registry, '_', '_', '_'}, '_', '_', '_'] ++ BoundCounters,
633+
MetricSpec = [{Registry, '_', '_', '_'}, '_', '_', '_' | BoundCounters],
643634
ets:match_delete(?TABLE, list_to_tuple(MetricSpec)).
644635

645636
sub_tuple_to_list(Tuple, Pos, Size) when Pos < Size ->

src/prometheus_buckets.erl

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,14 @@ linear(_Start, _Step, Count) when Count < 1 ->
156156
linear(Start, Step, Count) ->
157157
linear(Start, Step, Count, []).
158158

159-
-spec position(buckets(), number()) -> pos_integer().
159+
?DOC("""
160+
Find the first index that is greater than or equal to the given value.
161+
""").
162+
-spec position(buckets() | tuple(), number()) -> pos_integer().
160163
position(Buckets, Value) when is_list(Buckets), is_number(Value) ->
161-
find_position(Buckets, Value, 0).
164+
find_position(Buckets, Value, 0);
165+
position(Buckets, Value) when is_tuple(Buckets), 1 < tuple_size(Buckets), is_number(Value) ->
166+
find_position_in_tuple(Buckets, Value, 1, tuple_size(Buckets)).
162167

163168
ddsketch([], _, Acc) ->
164169
lists:reverse(Acc);
@@ -187,7 +192,8 @@ try_to_maintain_integer_bounds(Bound) when is_float(Bound) ->
187192
false -> Bound
188193
end.
189194

190-
-spec find_position(buckets(), number(), non_neg_integer()) -> pos_integer().
195+
%% Find the first index that is greater than or equal to the given value.
196+
-spec find_position(buckets(), number(), non_neg_integer()) -> non_neg_integer().
191197
find_position([], _Value, _Pos) ->
192198
0;
193199
find_position([Bound | L], Value, Pos) ->
@@ -197,3 +203,20 @@ find_position([Bound | L], Value, Pos) ->
197203
false ->
198204
find_position(L, Value, Pos + 1)
199205
end.
206+
207+
%% Find the first index that is greater than or equal to the given value.
208+
-spec find_position_in_tuple(tuple(), number(), non_neg_integer(), pos_integer()) ->
209+
non_neg_integer().
210+
find_position_in_tuple(Tuple, Value, Low, High) when Low =< High ->
211+
Mid = Low + (High - Low) div 2,
212+
case element(Mid, Tuple) of
213+
Element when Element < Value ->
214+
find_position_in_tuple(Tuple, Value, Mid + 1, High);
215+
Element when Value =< Element ->
216+
find_position_in_tuple(Tuple, Value, Low, Mid - 1)
217+
end;
218+
find_position_in_tuple(Tuple, _Value, Low, _High) ->
219+
case tuple_size(Tuple) < Low of
220+
true -> 0;
221+
false -> Low - 1
222+
end.

0 commit comments

Comments
 (0)