-
Notifications
You must be signed in to change notification settings - Fork 632
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
tracing::{info, error, warn, trace, debug}
is slower by factor of 30
#6072
Comments
tracing::{info, error, warn, trace, debug}
take long timetracing::{info, error, warn, trace, debug}
is slower by factor of 30
Just to make sure, have you verified all of these are doing the same kind of work?
Just making sure because this is a common gotcha people hit when benchmarking similar stuff. The performance of emitting tracing events and spans is heavily dependent on the implementation of the subscribers that have been registered. Right now we set up the formatting subscriber to use Of particular interest may be the |
> > For example tracing::info! takes around 1600ns to execute, when it's output is logged to file, while println! takes "only" 800 ns.
I did the benchmark, and I wrote my own formatting subscriber. They are not doing exactly the same amount of work. For example, At the same time, if While I agree that the amount of work is not the same. There is overhead caused by doing this extra formatting. The reason comes to this piece of code:
Formatting Though, the other 800ns is takes by I tried creating a tracing subscriber, that doesn't nothing except
I probably should publish my benchmark, and then maybe we can find a way to optimize the subscriber, or maybe we can file a bug report to |
Actually, that's a good point. However, when using In summary:
Also, keep in mind, that google cloud machines are around 2-3x slower, than my local desktop, so you need to multiply those values by a factor of 2-3x. |
tracing::{info, error, warn, trace, debug}
is slower by factor of 30 tracing::{info, error, warn, trace, debug}
is slower by factor of 50
tracing::{info, error, warn, trace, debug}
is slower by factor of 50 tracing::{info, error, warn, trace, debug}
is slower by factor of 30
@nagisa volunteered to make an improvement on this |
This will utilize a separate thread to write out the spans and events without while letting the main computation to proceed with its business. Additionally, we are buffering the output by lines, thus reducing the frequency of syscalls that can occur when the subscriber is writing out parts of the message. This should mitigate concerns of enabling debug logging as its impact on performance should now be minimal (putting an event structure onto a MPSC queue.) There are still costs associated with logging everything however. Most notably formatting and construction of the `tracing_core::ValueSet`s still occur on the caller side, so if constructing those is expensive, the logging might remain expensive. An example of code sketchy like that (although silly) could be: ``` debug!(message = { std::time::sleep(Duration::from_secs(1)); "hello" }) ``` or for a less silly example: ``` debug!("{}", my_vector.iter().map(|...| { do_expensive_stuff() }).collect::<String>()) ``` These should be considered a bug (alas one that `tracing` does not have any tooling to detect, sadly.) I opted adding a new crate dedicated to observability utilities. From my experience using things like prometheus will eventually result in a variety of utilities being written, so this crate eventually would likely expand in scope... Fixes #6072 (though I haven't made any actual measurements as to what the improvement really is)
We are currently using tracing for logging. This is done through
info, error, warn, trace, debug
macros.By default, tracing logs data to
stderr
, or the data gets redirected through pipe to a file.I did a few measurements, that shows that it's taking a considerable amount of time and that can be improved.
For example
tracing::info!
takes around 1600ns to execute, when it's output is logged to file, whileprintln!
takes "only" 800 ns.However, when the data is redirected to
/dev/null
, then it takes less time to execute those commands.This shows that, the
tracing:info!
is blocking, and does asyscall
which blocks the thread from executing, and it practice thread can be frozen for a longer period of time while doing disk write.On the other hand, doing only
format!
takes only around40ns
. Due to, both thetracing
being inefficient at both formatting the data and writing them to stdout, the time to write a log increases from40ns
to1600ns
I did some experiments, when instead of using
tracing
, and instead of writing data tostderr
, we write data to a concurrent, queue, which then gets consumed by another process. The result is that it takes only around60ns
to write a log, instead of1600ns
.My suggestion is to do the following.
tracing::{info, error, warn, trace, debug}
, with our own code, which formats the data, and sends an event through MPSCconqueue
to another threada) write data using
println!
, this is whattracing
does internally. It will take around700ns
, but at the very least we won't block the target threadb) send data using UDP to
syslog
(https://en.wikipedia.org/wiki/Syslog)A syslog service will be responsible for writing data to disk, and our process will not be blocked by disk writes anymore.
Drawbacks:
conqueue
can potentially get larger, than we can handle.However, that's still better than, the current solution, which is to block the log writer thread.
We can implement a custom strategy of dropping logs, to mitigate this. The advantage of this solution, is that we would still keep the current process alive, while logs.
The text was updated successfully, but these errors were encountered: