Fmt: Performance Question: How to format to a raw buffer?

Created on 21 Sep 2020  路  3Comments  路  Source: fmtlib/fmt

Hello,

I am fmt::format("{:g}", value) that outputs a string which I copy to a raw char * buffer of size 32, through this extremely inefficient method string_to_buf:

inline static void string_to_buf(const std::string &src, char* buffer, const size_t buf_len) {
    const size_t output_size = src.size();
    src.copy(buffer, buf_len);
    if (output_size >= buf_len) {
      Log::Fatal("Numeric conversion failed. Buffer overflow.");
    }
    buffer[output_size] = '\0';
}

inline static void DoubleToStr(double value, char* buffer, const size_t buffer_len) {
  const std::string s = fmt::format("{:.17g}", value);
  string_to_buf(s, buffer, buffer_len);  
}

I saw in the docs that one can use fmt::format_to(out, "{:g}", value) to a memory_buffer to avoid generating a new string. But the memory_buffer API is not extensively documented so I have some questions:

  1. Can out be a "normal" C++ buffer created through new char[X] or an std::vector<char>?
  2. Can I reserve a size for memory_buffer? Looking at the source-code it looks so but just double-checking.
  3. Can I reuse memory_buffer ? If I do several calls of format_to to the same memory_buffer it keeps appending at the end of it, or overwrites data in it?
  4. Just curious: Is there any facility to check the output string size? Alike snprintf?

Thank you so much!

Most helpful comment

Based on code you provided, I think it's better to use format_to_n in this case and write directly to the char buffer, so you will have code like this:

inline static void DoubleToStr(double value, char* buffer, const size_t buffer_len) {
  auto result = fmt::format_to_n(buffer, buffer_len, "{:.17g}", value);
  auto output_string_size = result.size;
}

as you can see, also you will be able to get output size.

Actually, this is an answer for your first question. But if it doesn't work for you:

  1. Yes, you can. This is documented feature, and you can always check it by calling corresponding methods:
    https://godbolt.org/z/zn3K1K

  2. Yes, you can reuse memory_buffer, but if you want to have only last formatted output in buffer you should call clear method. I'm not sure that this is documented anywhere.

  3. hmm... I don't think you can get an output string size while using format_to with memory_buffer.

All 3 comments

Based on code you provided, I think it's better to use format_to_n in this case and write directly to the char buffer, so you will have code like this:

inline static void DoubleToStr(double value, char* buffer, const size_t buffer_len) {
  auto result = fmt::format_to_n(buffer, buffer_len, "{:.17g}", value);
  auto output_string_size = result.size;
}

as you can see, also you will be able to get output size.

Actually, this is an answer for your first question. But if it doesn't work for you:

  1. Yes, you can. This is documented feature, and you can always check it by calling corresponding methods:
    https://godbolt.org/z/zn3K1K

  2. Yes, you can reuse memory_buffer, but if you want to have only last formatted output in buffer you should call clear method. I'm not sure that this is documented anywhere.

  3. hmm... I don't think you can get an output string size while using format_to with memory_buffer.

In addition to what @alexezeder wrote, you can get the output size with formatted_size.

Thanks for the amazing answer @alexezeder I didn't even know format_to_n existed. Looks just what I need and the other things will be handy too. Damn, this library is insanely good and easy to use!
Thanks @vitaut as well.

Was this page helpful?
0 / 5 - 0 ratings