为什么snprintf打印单个数字的速度始终比ostringstream快2倍? 我在C++中对格式>代码>双 s进行了各种测试,这里有一些代码: #include <chrono> #include <cstdio> #include <random> #include <vector> #include <sstream> #include <iostream> inline long double currentTime() { const auto now = std::chrono::steady_clock::now().time_since_epoch(); return std::chrono::duration<long double>(now).count(); } int main() { std::mt19937 mt(std::random_device{}()); std::normal_distribution<long double> dist(0, 1e280); static const auto rng=[&](){return dist(mt);}; std::vector<double> numbers; for(int i=0;i<10000;++i) numbers.emplace_back(rng()); const int precMax=200; const int precStep=10; char buf[10000]; std::cout << "snprintf\n"; for(int precision=10;precision<=precMax;precision+=precStep) { const auto t0=currentTime(); for(const auto num : numbers) std::snprintf(buf, sizeof buf, "%.*e", precision, num); const auto t1=currentTime(); std::cout << "Precision " << precision << ": " << t1-t0 << " s\n"; } std::cout << "ostringstream\n"; for(int precision=10;precision<=precMax;precision+=precStep) { std::ostringstream ss; ss.precision(precision); ss << std::scientific; const auto t0=currentTime(); for(const auto num : numbers) { ss.str(""); ss << num; } const auto t1=currentTime(); std::cout << "Precision " << precision << ": " << t1-t0 << " s\n"; } }

为什么snprintf打印单个数字的速度始终比ostringstream快2倍? 我在C++中对格式>代码>双 s进行了各种测试,这里有一些代码: #include <chrono> #include <cstdio> #include <random> #include <vector> #include <sstream> #include <iostream> inline long double currentTime() { const auto now = std::chrono::steady_clock::now().time_since_epoch(); return std::chrono::duration<long double>(now).count(); } int main() { std::mt19937 mt(std::random_device{}()); std::normal_distribution<long double> dist(0, 1e280); static const auto rng=[&](){return dist(mt);}; std::vector<double> numbers; for(int i=0;i<10000;++i) numbers.emplace_back(rng()); const int precMax=200; const int precStep=10; char buf[10000]; std::cout << "snprintf\n"; for(int precision=10;precision<=precMax;precision+=precStep) { const auto t0=currentTime(); for(const auto num : numbers) std::snprintf(buf, sizeof buf, "%.*e", precision, num); const auto t1=currentTime(); std::cout << "Precision " << precision << ": " << t1-t0 << " s\n"; } std::cout << "ostringstream\n"; for(int precision=10;precision<=precMax;precision+=precStep) { std::ostringstream ss; ss.precision(precision); ss << std::scientific; const auto t0=currentTime(); for(const auto num : numbers) { ss.str(""); ss << num; } const auto t1=currentTime(); std::cout << "Precision " << precision << ": " << t1-t0 << " s\n"; } },c++,performance,output,C++,Performance,Output,所以我的主要问题是:造成这种双重差异的原因是什么?此外,我如何使ostringstream的性能更接近snprintf 注意:另一个问题与我的不同。首先,这里没有具体的答案,为什么不同精度的单个数字的格式设置比较慢。第二,这个问题问“为什么它总体上比较慢”,这个问题太宽泛了,无法回答我的问题,而这个问题问的是一个特定的场景,即格式化单个双数字。std::ostringstream调用vsnprintf两次:第一次尝试使用小缓冲区,第二个缓冲区大小正确。参见第1011行附近的locale\u fa

所以我的主要问题是:造成这种双重差异的原因是什么?此外,我如何使
ostringstream
的性能更接近
snprintf


注意:另一个问题与我的不同。首先,这里没有具体的答案,为什么不同精度的单个数字的格式设置比较慢。第二,这个问题问“为什么它总体上比较慢”,这个问题太宽泛了,无法回答我的问题,而这个问题问的是一个特定的场景,即格式化单个
数字。

std::ostringstream
调用
vsnprintf
两次:第一次尝试使用小缓冲区,第二个缓冲区大小正确。参见第1011行附近的
locale\u facets.tcc
(此处
std::\u convert\u from\u v
vsnprintf
的代理):

#如果"GLIBCXX"使用"C99"标准
//除hexfloat格式外,始终使用精度。
康斯特布尔(const bool)使用(const prec)=
(u io.flags()&ios_base::floatfield)!=ios_base::floatfield;
//首先尝试一个足够大的缓冲区(最有可能是足够的)
//对于非ios_base::固定输出)
int uuu cs_size=uuu max_位*3;
char*uuu cs=静态强制转换(uuu内置alloca(uuu cs_大小));
如果(使用预处理)
__len=std::uu从_v(_S_get_c_locale(),uu cs,u cs_size,
__fbuf、prec、v);
其他的
__len=std::uu从_v(_S_get_c_locale(),uu cs,u cs_size,
__fbuf,uv);
//如果缓冲区不够大,请使用正确的大小重试。
如果(\uuuu len>=\uuuu cs\u size)
{
__cs_大小=u长度+1;
__cs=静态铸造(内置合金(尺寸));
如果(使用预处理)
__len=std::uu从_v(_S_get_c_locale(),uu cs,u cs_size,
__fbuf、prec、v);
其他的
__len=std::uu从_v(_S_get_c_locale(),uu cs,u cs_size,
__fbuf,uv);
}
这完全符合以下观察结果:对于较小的请求精度,性能与
snprintf
相同,而对于较大的精度,性能差2倍

此外,由于所使用的缓冲区不依赖于
std::ostringstream
缓冲区的任何属性,只依赖于
\uu max\u digits
,它被定义为
\uu gnu\u cxx::\uu numeric\u traits::\uu digits10
,因此除了修复
libstdc++/code>本身之外,似乎没有任何自然修复方法


我把它当作libstdc++的bug。

可能是@Flopp的复制品。它不是:首先,没有具体的答案为什么不同精度的单个数字的格式化速度较慢。第二,它会问“为什么总体上比较慢”,这太过繁琐,没有任何意义,而我的问题是关于一个特定的场景。我怀疑您正在构建一个调试构建。当我使用Visual Studio构建发行版时,snprintf和ostringstream之间的性能数字只有细微的不同。@selbie请参阅我关于编译选项的评论:
-march=native-O3
。这绝对不是调试模式。@Ruslan:那么也许GCC的
stringstream
实现是垃圾。或者visualstudio的
snprintf
实现是垃圾。
snprintf
Precision 10: 0.0262963 s
Precision 20: 0.035437 s
Precision 30: 0.0468597 s
Precision 40: 0.0584917 s
Precision 50: 0.0699653 s
Precision 60: 0.081446 s
Precision 70: 0.0925062 s
Precision 80: 0.104068 s
Precision 90: 0.115419 s
Precision 100: 0.128886 s
Precision 110: 0.138073 s
Precision 120: 0.149591 s
Precision 130: 0.161005 s
Precision 140: 0.17254 s
Precision 150: 0.184622 s
Precision 160: 0.195268 s
Precision 170: 0.206673 s
Precision 180: 0.218756 s
Precision 190: 0.230428 s
Precision 200: 0.241654 s
ostringstream
Precision 10: 0.0269695 s
Precision 20: 0.0383902 s
Precision 30: 0.0497328 s
Precision 40: 0.12028 s
Precision 50: 0.143746 s
Precision 60: 0.167633 s
Precision 70: 0.190878 s
Precision 80: 0.214735 s
Precision 90: 0.238105 s
Precision 100: 0.261641 s
Precision 110: 0.285149 s
Precision 120: 0.309025 s
Precision 130: 0.332283 s
Precision 140: 0.355797 s
Precision 150: 0.379415 s
Precision 160: 0.403452 s
Precision 170: 0.427337 s
Precision 180: 0.450668 s
Precision 190: 0.474012 s
Precision 200: 0.498061 s
#if _GLIBCXX_USE_C99_STDIO
    // Precision is always used except for hexfloat format.
    const bool __use_prec =
      (__io.flags() & ios_base::floatfield) != ios_base::floatfield;

    // First try a buffer perhaps big enough (most probably sufficient
    // for non-ios_base::fixed outputs)
    int __cs_size = __max_digits * 3;
    char* __cs = static_cast<char*>(__builtin_alloca(__cs_size));
    if (__use_prec)
      __len = std::__convert_from_v(_S_get_c_locale(), __cs, __cs_size,
                    __fbuf, __prec, __v);
    else
      __len = std::__convert_from_v(_S_get_c_locale(), __cs, __cs_size,
                    __fbuf, __v);

    // If the buffer was not large enough, try again with the correct size.
    if (__len >= __cs_size)
      {
        __cs_size = __len + 1;
        __cs = static_cast<char*>(__builtin_alloca(__cs_size));
        if (__use_prec)
          __len = std::__convert_from_v(_S_get_c_locale(), __cs, __cs_size,
                        __fbuf, __prec, __v);
        else
          __len = std::__convert_from_v(_S_get_c_locale(), __cs, __cs_size,
                        __fbuf, __v);
      }