Skip to content
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

Optimize tm formatting (Non C-locales and %Z) #2617

Merged
merged 3 commits into from
Dec 4, 2021

Conversation

phprus
Copy link
Contributor

@phprus phprus commented Nov 26, 2021

Replaced std::basic_ostringstream to std::basic_ostream with fmt::detail::formatbuf.

Benchmark: https://github.com/phprus/fmt-bench/tree/optimize-tm-formatting-6

@phprus phprus changed the title Optimize tm formatting (Non C-locales) Optimize tm formatting (Non C-locales and %Z) Nov 26, 2021
@vitaut
Copy link
Contributor

vitaut commented Nov 26, 2021

Thanks for the PR. I wonder why strftime is so much faster in the benchmark?

@phprus
Copy link
Contributor Author

phprus commented Nov 27, 2021

glibc and BSD libc has a tm.tm_zone field. I will add using of the tm.tm_zone to this PR.
But why ostringstream is so slower I don't know. I will do this research on Monday.

@phprus phprus marked this pull request as draft November 27, 2021 08:36
@phprus
Copy link
Contributor Author

phprus commented Nov 27, 2021

@vitaut,
glibc strftime does not use locales.
libstdc++ uses strftime_l and strftime_l uses locales.

New algorithm for Z format is implemented. Benchmark is updated.

@phprus phprus marked this pull request as ready for review November 27, 2021 13:02
Copy link
Contributor

@vitaut vitaut left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR!

include/fmt/format.h Outdated Show resolved Hide resolved
@@ -38,6 +38,7 @@
#include <limits> // std::numeric_limits
#include <memory> // std::uninitialized_copy
#include <stdexcept> // std::runtime_error
#include <streambuf> // std::basic_streambuf
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a dependency on streambuf is undesirable esp. since formatbuf is not even used in format.h. Let's drop it by making formatbuf parameterized on streambuf.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

include/fmt/chrono.h Outdated Show resolved Hide resolved
inline auto do_write(const std::tm& time, const std::locale& loc, char format,
char modifier) -> std::basic_string<Char> {
auto&& os = std::basic_ostringstream<Char>();
inline void do_write(buffer<Char>& buf, const std::tm& time,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think we can drop do_ here and below because this prefix is only used in cases with identical signatures. Here signature is specific enough.

Copy link
Contributor Author

@phprus phprus Dec 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.
[do_]write(buffer<Char>& buf, conflicts with write(OutputIt out

Comment on lines 349 to 350
constexpr size_t unit_buf_size = 32;
code_unit unit_buf[unit_buf_size] = {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move buffer management to [do_]write_codecvt by making it return a struct, something like:

struct codecvt_result {
  code_unit buf[32];
  code_unit* end;
};

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? write_codecvt does not depend on the the output buffer size. So I moved the buffer management out of write_codecvt.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because otherwise it has to be done by the callers effectively duplicating the logic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

end value is dependent of buf.
Returning codecvt_result from a function corrupts end.

@phprus phprus force-pushed the optimize-tm-formatting-6 branch 2 times, most recently from 387d007 to 2cf2bd1 Compare December 3, 2021 19:10
@phprus
Copy link
Contributor Author

phprus commented Dec 3, 2021

@vitaut, PR updated.

@phprus
Copy link
Contributor Author

phprus commented Dec 3, 2021

Unexpected runtime error: https://github.com/phprus/fmt/runs/4412073708?check_suite_focus=true
I convert PR to draft...

@phprus phprus marked this pull request as draft December 3, 2021 19:22
include/fmt/format.h Outdated Show resolved Hide resolved
@phprus
Copy link
Contributor Author

phprus commented Dec 3, 2021

@vitaut, PR updated. Error on Windows is fixed.

@phprus phprus marked this pull request as ready for review December 3, 2021 20:40
Copy link
Contributor

@vitaut vitaut left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general looks good, just a few more comments.

include/fmt/chrono.h Outdated Show resolved Hide resolved
};

template <typename CodeUnit>
inline void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not return codecvt_result instead of passing it as an output parameter?

Also inline is probably not needed here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

codecvt_result.end is depends on codecvt_result.buf
Returning codecvt_result from a function may result in codecvt_result being copied and end being corrupted.
It is possible to replace the pointer end with the length len, but this does not guarantee that there is no copying.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, let's leave as out parameter then.

Comment on lines 388 to 390
auto&& buf = basic_memory_buffer<Char>();
buf.append(sv.begin(), sv.end());
return write_char_buffer(out, buf, loc);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to replace write_char_buffer with a function that takes string_view instead of a buffer to avoid copy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

for (code_unit* p = buf; p != to_next; ++p) {
codecvt_result<code_unit> unit;
write_codecvt(unit, string_view(buf.data(), buf.size()), loc);
buf.clear();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this function should use a new buffer instead of reusing the one passed externally. This will also allow to pass input as a string_view suggested below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@vitaut vitaut merged commit c240d98 into fmtlib:master Dec 4, 2021
@vitaut
Copy link
Contributor

vitaut commented Dec 4, 2021

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants