-
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
base: levi/profiles_dictionary
Are you sure you want to change the base?
Conversation
BenchmarksComparisonBenchmark execution time: 2025-11-20 04:42:30 Comparing candidate commit 38c8e3c in PR branch Found 0 performance improvements and 0 performance regressions! Performance is the same for 55 metrics, 2 unstable metrics. CandidateCandidate benchmark detailsGroup 1
Group 2
Group 3
Group 4
Group 5
Group 6
Group 7
Group 8
Group 9
Group 10
Group 11
Group 12
Group 13
Group 14
Group 15
Group 16
Group 17
Group 18
Group 19
BaselineOmitted due to size. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## levi/profiles_dictionary #1351 +/- ##
============================================================
+ Coverage 71.15% 71.31% +0.15%
============================================================
Files 396 399 +3
Lines 62798 63164 +366
============================================================
+ Hits 44685 45043 +358
- Misses 18113 18121 +8
🚀 New features to boost your workflow:
|
|
✅ Tests 🎉 All green!❄️ No new flaky tests detected 🔗 Commit SHA: 38c8e3c | Docs | Datadog PR Page | Was this helpful? Give us feedback! |
Artifact Size Benchmark Reportaarch64-alpine-linux-musl
aarch64-apple-darwin
aarch64-unknown-linux-gnu
libdatadog-x64-windows
libdatadog-x86-windows
x86_64-alpine-linux-musl
x86_64-apple-darwin
x86_64-unknown-linux-gnu
|
| pub fn try_new2( | ||
| profiles_dictionary: crate::profiles::collections::Arc<ProfilesDictionary>, | ||
| sample_types: &[ValueType2], | ||
| period: Option<Period2>, | ||
| ) -> io::Result<Self> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having tried this out in PHP, I've decided that it's too much churn in the code to do this at this time. I'm going to adjust the signature so that it takes ValueType and Period rather than ValueType2 and Period2 (and remove the *2 types in these cases). I will keep the profiles_dictionary argument but rename the method, maybe try_new_with_dictionary or something.
| // SAFETY: the profiles_dictionary keeps the storage for Ids alive. | ||
| unsafe impl Send for ProfilesDictionaryTranslator {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not clear how this is sufficient to establish Send
| }; | ||
| use indexmap::map::Entry; | ||
|
|
||
| pub struct ProfilesDictionaryTranslator { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the purpose and expected uses for this class
| #[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, | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to use Rust enums here to enforce this?
| labels: L, | ||
| timestamp: Option<Timestamp>, | ||
| ) -> anyhow::Result<()> { | ||
| let Some(translator) = &mut self.profiles_dictionary_translator else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we want to use anyhow, since that can allocate on error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, not really, but that kind of thinking is what slowed down the past efforts I made. Each little thing adds up.
| let mut lbls = Vec::new(); | ||
| lbls.try_reserve_exact(labels.len())?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this because with_capacity can panic?
| #[inline(never)] | ||
| #[cold] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How often do you make new Profile objects? I'd consider once per minute pretty cold, wouldn't you?
| let function2 = (unsafe { id2.read() }).unwrap_or_default(); | ||
| let set_id = unsafe { core::mem::transmute::<FunctionId2, SetId<dt::Function>>(id2) }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
safety comments
| return Ok(*internal); | ||
| } | ||
|
|
||
| let (name, system_name, filename) = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the advantage of the tuple here
| let internal_id = functions.dedup(function); | ||
| self.functions.try_reserve(1)?; | ||
| self.functions.insert(set_id, internal_id); | ||
| Ok(internal_id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so this both functions and self.functions? Why do we need both
| let Some(mapping2) = (unsafe { id2.read() }) else { | ||
| return Ok(None); | ||
| }; | ||
| let set_id = unsafe { core::mem::transmute::<MappingId2, SetId<dt::Mapping>>(id2) }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've noticed a bunch of these transmutes. Why are they needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, safety comment
What does this PR do?
This adds two new methods to
libdd_profiling::internal::Profilewith supporting types:Profile::try_new2Profile::try_add_sample2Motivation
These APIs together are faster than the versions that aren't suffixed with 2.
Additional Notes
This doesn't add the FFI API yet, that's coming next.
How to test the change?
Everything should be the same. There is a benchmark you can run to compare some basic API level tests. Here is what I got locally on my M1:
In this basic benchmarking test, it's roughly 3x faster.