-
Notifications
You must be signed in to change notification settings - Fork 15
feat(profiling): Profile::{try_new2,try_add_sample2}
#1351
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
Changes from 12 commits
5c9f168
2f6bdcf
7e5ae4b
316f1ee
2f8c573
a43f7bc
939907b
cfc8a17
89b7a8e
aaf1b01
91d6ac3
38c8e3c
45580ed
524c417
1fb7d57
e8a1ad8
4f0168e
f1bec40
8e35d78
f2b3f8a
7d451b3
f1a03bf
19bb0ef
eae2db2
0de818a
ef5c761
b315745
d21dd81
b38db9c
e677b96
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,157 @@ | ||
| // Copyright 2025-Present Datadog, Inc. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| use criterion::*; | ||
| use libdd_profiling::api2::Location2; | ||
| use libdd_profiling::profiles::collections::SetId; | ||
| use libdd_profiling::profiles::datatypes::{Function, FunctionId2, MappingId2}; | ||
| use libdd_profiling::{self as profiling, api, api2}; | ||
|
|
||
| fn make_sample_types() -> Vec<api::ValueType<'static>> { | ||
| vec![api::ValueType::new("samples", "count")] | ||
| } | ||
|
|
||
| fn make_stack_api(frames: &[Frame]) -> (Vec<api::Location<'static>>, Vec<i64>) { | ||
| // No mappings in Ruby, but the v1 API requires it. | ||
| let mapping = api::Mapping::default(); | ||
| let mut locations = Vec::with_capacity(frames.len()); | ||
| for frame in frames { | ||
| locations.push(api::Location { | ||
| mapping, | ||
| function: api::Function { | ||
| name: frame.function_name, | ||
| filename: frame.function_name, | ||
| ..Default::default() | ||
| }, | ||
| line: frame.line_number as i64, | ||
| ..Default::default() | ||
| }); | ||
| } | ||
| let values = vec![1i64]; | ||
| (locations, values) | ||
| } | ||
|
|
||
| fn make_stack_api2(frames: &[Frame2]) -> (Vec<Location2>, Vec<i64>) { | ||
| let mut locations = Vec::with_capacity(frames.len()); | ||
|
|
||
| for frame in frames { | ||
| locations.push(Location2 { | ||
| mapping: MappingId2::default(), | ||
| function: frame.function, | ||
| address: 0, | ||
| line: frame.line_number as i64, | ||
| }); | ||
|
Comment on lines
+37
to
+42
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would this make sense as a |
||
| } | ||
|
|
||
| let values = vec![1i64]; | ||
| (locations, values) | ||
| } | ||
|
|
||
| #[derive(Clone, Copy)] | ||
| struct Frame { | ||
| file_name: &'static str, | ||
| line_number: u32, | ||
| function_name: &'static str, | ||
| } | ||
| impl Frame { | ||
| pub const fn new( | ||
| file_name: &'static str, | ||
| line_number: u32, | ||
| function_name: &'static str, | ||
| ) -> Self { | ||
| Self { | ||
| file_name, | ||
| line_number, | ||
| function_name, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[derive(Clone, Copy)] | ||
| struct Frame2 { | ||
| function: FunctionId2, | ||
| line_number: u32, | ||
| } | ||
|
|
||
| pub fn bench_add_sample_vs_add2(c: &mut Criterion) { | ||
| let sample_types = make_sample_types(); | ||
| let dict = profiling::profiles::datatypes::ProfilesDictionary::try_new().unwrap(); | ||
|
|
||
| // This is root-to-leaf, instead of leaf-to-root. We'll reverse it below. | ||
| // Taken from a Ruby app, everything here is source-available. | ||
| let mut frames = [ | ||
| Frame::new("/usr/local/bundle/gems/logging-2.4.0/lib/logging/diagnostic_context.rb", 474, "create_with_logging_context"), | ||
| Frame::new("/usr/local/bundle/gems/puma-6.4.3/lib/puma/thread_pool.rb", 155, "spawn_thread"), | ||
| Frame::new("/usr/local/bundle/gems/puma-6.4.3/lib/puma/server.rb", 245, "run"), | ||
| Frame::new("/usr/local/bundle/gems/puma-6.4.3/lib/puma/server.rb", 464, "process_client"), | ||
| Frame::new("/usr/local/bundle/gems/puma-6.4.3/lib/puma/request.rb", 99, "handle_request"), | ||
| Frame::new("/usr/local/bundle/gems/puma-6.4.3/lib/puma/thread_pool.rb", 378, "with_force_shutdown"), | ||
| Frame::new("/usr/local/bundle/gems/puma-6.4.3/lib/puma/request.rb", 100, "handle_request"), | ||
| Frame::new("/usr/local/bundle/gems/puma-6.4.3/lib/puma/configuration.rb", 272, "call"), | ||
| Frame::new("/usr/local/bundle/gems/railties-7.0.8.7/lib/rails/engine.rb", 530, "call"), | ||
| Frame::new("/usr/local/bundle/gems/datadog-2.18.0/lib/datadog/tracing/contrib/rack/middlewares.rb", 474, "call"), | ||
| Frame::new("/usr/local/bundle/gems/datadog-2.18.0/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb", 17, "call"), | ||
| Frame::new("/usr/local/bundle/gems/datadog-2.18.0/lib/datadog/tracing/contrib/rack/middlewares.rb", 70, "call"), | ||
| Frame::new("/usr/local/bundle/gems/datadog-2.18.0/lib/datadog/appsec/contrib/rack/request_middleware.rb", 82, "call"), | ||
| Frame::new("/usr/local/lib/libruby.so.3.3", 0, "catch"), | ||
| Frame::new("/usr/local/bundle/gems/datadog-2.18.0/lib/datadog/appsec/contrib/rack/request_middleware.rb", 85, "catch"), | ||
| Frame::new("/usr/local/bundle/gems/datadog-2.18.0/lib/datadog/appsec/instrumentation/gateway.rb", 41, "push"), | ||
| Frame::new("/usr/local/bundle/gems/datadog-2.18.0/lib/datadog/appsec/instrumentation/gateway.rb", 37, "push"), | ||
| Frame::new("/usr/local/bundle/gems/datadog-2.18.0/lib/datadog/appsec/instrumentation/gateway/middleware.rb", 18, "call"), | ||
| ]; | ||
| frames.reverse(); | ||
|
|
||
| let strings = dict.strings(); | ||
| let functions = dict.functions(); | ||
|
|
||
| let frames2 = frames.map(|f| { | ||
| let set_id = functions | ||
| .try_insert(Function { | ||
| name: strings.try_insert(f.file_name).unwrap(), | ||
| system_name: Default::default(), | ||
| file_name: strings.try_insert(f.file_name).unwrap(), | ||
| }) | ||
| .unwrap(); | ||
| Frame2 { | ||
| function: unsafe { core::mem::transmute::<SetId<Function>, FunctionId2>(set_id) }, | ||
| line_number: f.line_number, | ||
| } | ||
| }); | ||
| let dict = profiling::profiles::collections::Arc::try_new(dict).unwrap(); | ||
|
|
||
| c.bench_function("profile_add_sample_frames_x1000", |b| { | ||
| b.iter(|| { | ||
| let mut profile = profiling::internal::Profile::try_new(&sample_types, None).unwrap(); | ||
| let (locations, values) = make_stack_api(frames.as_slice()); | ||
| for _ in 0..1000 { | ||
| let sample = api::Sample { | ||
| locations: locations.clone(), | ||
| values: &values, | ||
| labels: vec![], | ||
| }; | ||
| black_box(profile.try_add_sample(sample, None)).unwrap(); | ||
| } | ||
| black_box(profile.only_for_testing_num_aggregated_samples()) | ||
| }) | ||
| }); | ||
|
|
||
| c.bench_function("profile_add_sample2_frames_x1000", |b| { | ||
| b.iter(|| { | ||
| let mut profile = profiling::internal::Profile::try_new_with_dictionary( | ||
| &sample_types, | ||
| None, | ||
| dict.try_clone().unwrap(), | ||
| ) | ||
| .unwrap(); | ||
| let (locations, values) = make_stack_api2(frames2.as_slice()); | ||
| for _ in 0..1000 { | ||
| // Provide an empty iterator for labels conversion path | ||
| let labels_iter = std::iter::empty::<anyhow::Result<api2::Label>>(); | ||
| black_box(profile.try_add_sample2(&locations, &values, labels_iter, None)).unwrap(); | ||
| } | ||
| black_box(profile.only_for_testing_num_aggregated_samples()) | ||
| }) | ||
| }); | ||
| } | ||
|
|
||
| criterion_group!(benches, bench_add_sample_vs_add2); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| // Copyright 2025-Present Datadog, Inc. https://www.datadoghq.com/ | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| use crate::profiles::datatypes::{FunctionId2, MappingId2, StringId2}; | ||
|
|
||
| #[derive(Copy, Clone, Debug, Default)] | ||
| #[repr(C)] | ||
| pub struct Location2 { | ||
| pub mapping: MappingId2, | ||
| pub function: FunctionId2, | ||
|
|
||
| /// The instruction address for this location, if available. It | ||
| /// should be within [Mapping.memory_start...Mapping.memory_limit] | ||
| /// for the corresponding mapping. A non-leaf address may be in the | ||
| /// middle of a call instruction. It is up to display tools to find | ||
| /// the beginning of the instruction if necessary. | ||
| pub address: u64, | ||
| pub line: i64, | ||
| } | ||
|
|
||
| #[derive(Copy, Clone, Debug, Default)] | ||
| pub struct Label<'a> { | ||
| pub key: StringId2, | ||
|
|
||
| /// At most one of `.str` and `.num` should not be empty. | ||
| pub str: &'a str, | ||
| pub num: i64, | ||
|
|
||
| /// Should only be present when num is present. | ||
| /// Specifies the units of num. | ||
| /// Use arbitrary string (for example, "requests") as a custom count unit. | ||
| /// If no unit is specified, consumer may apply heuristic to deduce the unit. | ||
| /// Consumers may also interpret units like "bytes" and "kilobytes" as memory | ||
| /// units and units like "seconds" and "nanoseconds" as time units, | ||
| /// and apply appropriate unit conversions to these. | ||
| pub num_unit: &'a str, | ||
| } | ||
|
Comment on lines
+21
to
+37
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it make sense to use Rust enums here to enforce this?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some of the code does model it this way, such as the |
||
|
|
||
| impl<'a> Label<'a> { | ||
| pub const fn str(key: StringId2, str: &'a str) -> Label<'a> { | ||
| Label { | ||
| key, | ||
| str, | ||
| num: 0, | ||
| num_unit: "", | ||
| } | ||
| } | ||
|
|
||
| pub const fn num(key: StringId2, num: i64, num_unit: &'a str) -> Label<'a> { | ||
| Label { | ||
| key, | ||
| str: "", | ||
| num, | ||
| num_unit, | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.