如何为自定义输出类型实现高性能的std::format风格函数
为自定义分配器字符串实现高性能std::format风格函数
我有一个带自定义分配器的字符串类型,定义如下:
struct myalloc : std::allocator<char> {}; using mystring = std::basic_string<char, std::char_traits<char>, myalloc>;
我希望实现一个std::format风格的函数来创建该类型的实例。最初我通过迭代器实现:
template<class ...Args> constexpr mystring myformat(std::format_string<Args...> fmt, Args &&...args) { mystring ret; std::format_to(std::back_inserter(ret), fmt, std::forward<Args>(args)...); return ret; }
但这个实现性能极差:在AMD EPYC CPU上,用std::format格式化256字节字符串256次耗时约60ns,而myformat耗时790ns,慢了13倍!gcc 14.1/libstdc和clang 18.1/libc++下结果类似。
我的问题是:如何利用格式化库实现自定义输出类型的格式化函数,同时避免如此巨大的性能损耗?
更新:
根据Miles Budnek的建议,使用std::formatted_size预分配空间后,性能有所提升:
template<class ...Args> constexpr mystring myformat(std::format_string<Args...> fmt, Args &&...args) { mystring ret(std::formatted_size(fmt, std::forward<Args>(args)...), '\0'); std::format_to(ret.data(), fmt, std::forward<Args>(args)...); return ret; }
但该实现仍耗时134ns,比std::format慢两倍以上。另外,我希望将方案推广到vformat,但std::formatted_size没有对应的v版本,也无法对format_args执行类似std::apply的操作。
更新2:
我实现了一个仅统计字符数的伪迭代器,以此实现了vformat的等效版本myvformat,性能甚至优于调用std::formatted_size的版本,重新实现后的代码如下:
struct counter { std::size_t n{}; using difference_type = long; auto operator*() const { return std::ignore; } counter &operator++() { ++n; return *this; } counter operator++(int) { return counter{n++}; } }; static_assert(std::output_iterator<counter, char>); constexpr mystring myvformat(std::string_view fmt, std::format_args args) { counter c; c = std::vformat_to(c, fmt, args); mystring ret(c.n, '\0'); std::vformat_to(ret.data(), fmt, args); return ret; } template<class ...Args> constexpr mystring myformat(std::format_string<Args...> fmt, Args &&...args) { return myvformat(fmt.get(), std::make_format_args(std::forward<Args>(args)...)); }
此时myformat耗时85ns,而std::vformat输出到std::string耗时60ns,性能有所提升但仍有明显差距。相比之下,调用std::format后将结果拷贝到mystring耗时72ns,但出于安全考虑,实际应用中不能采用这种方式——内容不能存放在普通堆中。
内容的提问来源于stack exchange,提问作者user3188445
相关产品推荐
相关产品推荐

