spdlog vs boost.log for prmon logging
quantum-shift opened this issue · comments
It was decided to test spdlog and boost.log for integration with prmon. spdlog turned out to be a clear winner in all tests.
The speeds of spdlog and boost.log were compared. Three kinds of tests were conducted varying the number and types of sinks being logged into:
a) Only a console sink
b) Only a file sink
c) Both console and file sinks.
1e6 messages were logged to the sinks and the execution times were recorded. Each test was repeated 5 times.
Here are the results:
ONLY CONSOLE SINK | ONLY FILE SINK | BOTH CONSOLE AND FILE SINKS | |||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
Time (in s) | Average | Median | Time (in s) | Average | Median | Time (in s) | Average | Median | |||
4.431 | 0.125 | 4.403 | |||||||||
4.364 | 0.111 | 4.459 | |||||||||
SPDLOG | 4.35 | 4.3892 | 4.35 | 0.136 | 0.121 | 0.119 | 4.531 | 4.4984 | 4.522 | ||
4.459 | 0.114 | 4.522 | |||||||||
4.342 | 0.119 | 4.577 | |||||||||
7.429 | 2.812 | 9.921 | |||||||||
7.271 | 2.878 | 10.235 | |||||||||
BOOSTLOG | 7.28 | 7.2894 | 7.28 | 2.949 | 2.8996 | 2.914 | 9.83 | 9.8566 | 9.83 | ||
7.298 | 2.914 | 9.579 | |||||||||
7.169 | 2.945 | 9.718 |
Some other differences:
a) It was much harder to install and configure boost.log than spdlog.
b) The number of header files to include for spdlog was much less than that for boost.log to accomplish the same behaviour.
c) The amount of code required to initialise sinks and logger to produce similar output for
- boost.log
BOOST_LOG_ATTRIBUTE_KEYWORD(scope_attr, "Scope", boost::log::attributes::named_scope::value_type)
BOOST_LOG_ATTRIBUTE_KEYWORD(severity_attr, "Severity", boost::log::trivial::severity_level)
void custom_formatter(logging::record_view const& rec, logging::formatting_ostream& strm)
{
strm << logging::extract< boost::posix_time::ptime >("TimeStamp", rec);
strm << " [" << rec[scope_attr] << "] ";
strm << "[" << rec[severity_attr] << "] ";
strm << rec[expr::smessage];
}
void init_fsink()
{
//Initialise the file sink
typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();
sink->locked_backend()->add_stream(boost::make_shared< std::ofstream >("sample.log"));
sink->set_formatter(&custom_formatter);
logging::core::get()->add_sink(sink);
}
void init_sink()
{
//Initialise the console sink
typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();
boost::shared_ptr< std::ostream > stream(&std::clog, boost::null_deleter());
sink->locked_backend()->add_stream(stream);
sink->set_formatter(&custom_formatter);
logging::core::get()->add_sink(sink);
}
BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(my_logger, src::severity_logger<boost::log::trivial::severity_level>)
int main() {
boost::log::core::get()->add_global_attribute("Scope", boost::log::attributes::named_scope());
boost::log::add_common_attributes();
BOOST_LOG_NAMED_SCOPE("benchmark");
init_fsink();
init_sink();
src::severity_logger<boost::log::trivial::severity_level>& lg = my_logger::get();
...
}
- spdlog
int main(){
auto sink = std::make_shared<spdlog::sinks::stdout_color_sink_st>();
auto fsink = std::make_shared<spdlog::sinks::basic_file_sink_mt> ("sample.log", true);
spdlog::sinks_init_list sink_list = {sink, fsink};
auto logger = std::make_shared<spdlog::logger>("benchmark", sink_list.begin(), sink_list.end());
logger->set_level(spdlog::level::warn);
...
}
Taking all these into account, we decided to proceed with spdlog for the integration.
Thanks very much for this study @quantum-shift - it's a nice result to get such an unambiguous answer.
I have a few questions, mostly for completeness
- What versions of
spdlog
andboost::log
were tried? - Which compiler and build flags were used?
- What was the hardware and OS that you tested on?
- It looks like you did 5 tests and took stats from that, so a good idea just to state that explicitly
- What versions of
spdlog
andboost::log
were tried?
spdlog version - 1.8.5
boost.log version - 1.76.0
- Which compiler and build flags were used?
There were no flags specific to spdlog.
For boost.log, the Boost_USE_STATIC_LIBS flag was set as we had decided to use static libraries.
- What was the hardware and OS that you tested on?
CPU - Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz
RAM - 16 GB
OS - Ubuntu 20.04.2
I have updated the first comment to clarify that each test was performed 5 times.
Thanks @quantum-shift ! One thing I would say is that we should test with at least the -O2
flag set. Some libraries are coded in such a way that unoptimised they might run quite slowly, but with optimisations on they would run a lot faster. That should be quite easy to do.
After that I think we are done with this issue.
Yes, @graeme-a-stewart. Both spdlog and boost.log were built with O3
flag set (the default for prmon).
I would like to bring your attention to another logging library candidate that has some potential (and claims to be faster than many competitors): https://github.com/odygrd/quill
Hi @pikacic - cool, thanks for bringing that up. prmon
doesn't log much so probably we will start with spdlog
for now (as it's in fact ready to go). However, the implementation abstracts from the underlying library so we could swap in the future (or later in the GSoC project!).
Would you test again with latest version?
a) It was much harder to install and configure boost.log than spdlog.
Now with vcpkg, it's easy to install and configure boost.log.