c ++ 11 Variadic Printf性能

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17671772/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 21:20:49  来源:igfitidea点击:

c++11 Variadic Printf performance

c++c++11formattingprintfvariadic-functions

提问by Michael D

variadic template is introduced in c++11. And I found the printf function can be replaced using it. However, cout is used in the implementation. I am wondering if it is possible to use something else to achieve type safe but not sacrifice too much performance.

可变参数模板是在 c++11 中引入的。我发现可以使用它替换 printf 函数。但是,在实现中使用了 cout。我想知道是否有可能使用其他东西来实现类型安全但不牺牲太多性能。

void safe_printf(const char *s)
{
    while (*s) {
        if (*s == '%') {
            if (*(s + 1) == '%') {
                ++s;
            }
            else {
                throw "invalid format string: missing arguments";
            }
        }
        std::cout << *s++;
    }
}

template<typename T, typename... Args>
void safe_printf(const char *s, T& value, Args... args)
{
    while (*s) {
        if (*s == '%') {
            if (*(s + 1) == '%') {
                ++s;
            }
            else {
                std::cout << value;
                safe_printf(s + 1, args...); // call even when *s == 0 to detect extra arguments
                return;
            }
        }
        std::cout << *s++;
    }
    throw "extra arguments provided to printf";
}

EDIT:OK as request, I perform a test to compare the performance between cout and printf. gcc version 4.8.1, x86_64-redhat-linux

编辑:按要求,我执行了一个测试来比较 cout 和 printf 之间的性能。gcc 版本 4.8.1,x86_64-redhat-linux

#include <iostream>
#include <cstdio>
#include <ctime>
#include <string>
#include <stdint.h>
#include <sstream>

using namespace std;
int32_t val = 10;
string s = "avcccc";

void TestOne(){
    for(int i = 0; i < 1000000; i++){
        printf("This is the value: %d, %s\n", i, s.c_str());
    }
}

void TestTwo(){
    ios_base::sync_with_stdio(false);

    for(int i = 0; i < 1000000; i++){
        cout << "This is the value: " << i << ", " << s << "\n";
    }
}

int main( int argc, char * argcv[] ) 
{
    timespec start;
    clock_gettime(CLOCK_REALTIME, &start);

    TestOne();

   timespec end;
   clock_gettime(CLOCK_REALTIME, &end);

   timespec start2;
   clock_gettime(CLOCK_REALTIME, &start2);

   TestTwo();

   timespec end2;
   clock_gettime(CLOCK_REALTIME, &end2);

   int64_t d1 = ((end.tv_sec - start.tv_sec) * 1000000000) + (end.tv_nsec - start.tv_nsec);

   int64_t d2 = ((end2.tv_sec - start2.tv_sec) * 1000000000) + (end2.tv_nsec - start2.tv_nsec);

   cout << "printf:" << d1 << " cout:" << d2 <<  ", diff(prinf-cout) " << d1-d2 << endl;
}

Without turn sync_with_stdio off.

不关闭sync_with_stdio。

printf:242862382 cout:275197806, diff(prinf-cout) -32335424
printf:201481941 cout:226177849, diff(prinf-cout) -24695908
printf:244915215 cout:275071790, diff(prinf-cout) -30156575
printf:235603694 cout:286859058, diff(prinf-cout) -51255364
printf:230076992 cout:248738879, diff(prinf-cout) -18661887
printf:223023056 cout:246486805, diff(prinf-cout) -23463749
printf:229922272 cout:269340309, diff(prinf-cout) -39418037
printf:228955230 cout:270675387, diff(prinf-cout) -41720157
printf:228390291 cout:228521421, diff(prinf-cout) -131130
printf:248419954 cout:268399737, diff(prinf-cout) -19979783
printf:229476729 cout:254635928, diff(prinf-cout) -25159199
printf:229646751 cout:252797729, diff(prinf-cout) -23150978
printf:227093478 cout:255390754, diff(prinf-cout) -28297276
printf:199161604 cout:224850870, diff(prinf-cout) -25689266
printf:198763304 cout:225709890, diff(prinf-cout) -26946586
printf:200445025 cout:223221944, diff(prinf-cout) -22776919
printf:202760174 cout:232363225, diff(prinf-cout) -29603051
printf:201989909 cout:225384923, diff(prinf-cout) -23395014
printf:202773400 cout:221465068, diff(prinf-cout) -18691668
printf:199076506 cout:223602064, diff(prinf-cout) -24525558
printf:200153338 cout:228610432, diff(prinf-cout) -28457094
printf:202794950 cout:223272108, diff(prinf-cout) -20477158
printf:209928648 cout:232300225, diff(prinf-cout) -22371577
printf:209722611 cout:233044140, diff(prinf-cout) -23321529
printf:248807680 cout:264406580, diff(prinf-cout) -15598900
printf:242916731 cout:246345645, diff(prinf-cout) -3428914
printf:221375038 cout:252251979, diff(prinf-cout) -30876941
printf:232892684 cout:273617728, diff(prinf-cout) -40725044
printf:238734317 cout:294238303, diff(prinf-cout) -55503986
printf:243180284 cout:284243039, diff(prinf-cout) -41062755
printf:245863056 cout:270413734, diff(prinf-cout) -24550678
printf:229300100 cout:259492696, diff(prinf-cout) -30192596
printf:227563904 cout:247802177, diff(prinf-cout) -20238273
printf:206847682 cout:230084237, diff(prinf-cout) -23236555
printf:198457595 cout:227655859, diff(prinf-cout) -29198264
printf:200265832 cout:224559564, diff(prinf-cout) -24293732
printf:201723563 cout:221764167, diff(prinf-cout) -20040604
printf:203430800 cout:221937208, diff(prinf-cout) -18506408
printf:198619127 cout:222313750, diff(prinf-cout) -23694623
printf:197686981 cout:233131170, diff(prinf-cout) -35444189
printf:199909645 cout:222764002, diff(prinf-cout) -22854357
printf:198050669 cout:221853069, diff(prinf-cout) -23802400
printf:200002449 cout:222743999, diff(prinf-cout) -22741550
printf:198573481 cout:221842796, diff(prinf-cout) -23269315
printf:200349434 cout:221501830, diff(prinf-cout) -21152396
printf:202039344 cout:238001170, diff(prinf-cout) -35961826
printf:209269614 cout:223050036, diff(prinf-cout) -13780422
printf:202794419 cout:219673749, diff(prinf-cout) -16879330
printf:223558422 cout:248919110, diff(prinf-cout) -25360688
printf:204139028 cout:231116364, diff(prinf-cout) -26977336

turn off sync_with_stdio

关闭sync_with_stdio

printf:228312005 cout:203814832, diff(prinf-cout) 24497173
printf:197367087 cout:170933621, diff(prinf-cout) 26433466
printf:203728802 cout:177833838, diff(prinf-cout) 25894964
printf:205758316 cout:180175924, diff(prinf-cout) 25582392
printf:251267252 cout:218081227, diff(prinf-cout) 33186025
printf:232029455 cout:199609356, diff(prinf-cout) 32420099
printf:246392438 cout:217010908, diff(prinf-cout) 29381530
printf:241149944 cout:202337266, diff(prinf-cout) 38812678
printf:222466515 cout:195872690, diff(prinf-cout) 26593825
printf:221633699 cout:206460840, diff(prinf-cout) 15172859
printf:229958105 cout:200284936, diff(prinf-cout) 29673169
printf:227201946 cout:195657372, diff(prinf-cout) 31544574
printf:244297134 cout:219042472, diff(prinf-cout) 25254662
printf:251881002 cout:216385940, diff(prinf-cout) 35495062
printf:227101117 cout:205227849, diff(prinf-cout) 21873268
printf:203951925 cout:177981416, diff(prinf-cout) 25970509
printf:201715048 cout:178496585, diff(prinf-cout) 23218463
printf:197549273 cout:175482278, diff(prinf-cout) 22066995
printf:239984907 cout:203745670, diff(prinf-cout) 36239237
printf:205235507 cout:181894912, diff(prinf-cout) 23340595
printf:203233202 cout:178961277, diff(prinf-cout) 24271925
printf:206434353 cout:173567831, diff(prinf-cout) 32866522
printf:202660160 cout:176737473, diff(prinf-cout) 25922687
printf:201831315 cout:175735846, diff(prinf-cout) 26095469
printf:200164787 cout:175192017, diff(prinf-cout) 24972770
printf:202584304 cout:177037255, diff(prinf-cout) 25547049
printf:204783650 cout:177513872, diff(prinf-cout) 27269778
printf:241033670 cout:220023173, diff(prinf-cout) 21010497
printf:247117709 cout:189569108, diff(prinf-cout) 57548601
printf:248105314 cout:219065335, diff(prinf-cout) 29039979
printf:236633133 cout:212964896, diff(prinf-cout) 23668237
printf:223496747 cout:195898087, diff(prinf-cout) 27598660
printf:224936154 cout:199397373, diff(prinf-cout) 25538781
printf:234572132 cout:216805022, diff(prinf-cout) 17767110
printf:246738504 cout:216305235, diff(prinf-cout) 30433269
printf:250904282 cout:217312285, diff(prinf-cout) 33591997
printf:208721377 cout:181195577, diff(prinf-cout) 27525800
printf:236245042 cout:203088785, diff(prinf-cout) 33156257
printf:204058026 cout:179401451, diff(prinf-cout) 24656575
printf:201038295 cout:176858089, diff(prinf-cout) 24180206
printf:204590031 cout:186586920, diff(prinf-cout) 18003111
printf:200774025 cout:177748984, diff(prinf-cout) 23025041
printf:203636203 cout:179084720, diff(prinf-cout) 24551483
printf:202479901 cout:179167171, diff(prinf-cout) 23312730
printf:202329892 cout:180674842, diff(prinf-cout) 21655050
printf:201305734 cout:179149029, diff(prinf-cout) 22156705
printf:200157089 cout:178860913, diff(prinf-cout) 21296176
printf:202803518 cout:177353141, diff(prinf-cout) 25450377
printf:204768180 cout:175608340, diff(prinf-cout) 29159840
printf:237805444 cout:205725465, diff(prinf-cout) 32079979

So cout can out performance printf if turn sync_with_stdio off that I have not heard sync_with_stdio before. I don't know if there is side effect for turning off.

因此,如果关闭我之前没有听说过的 sync_with_stdio 关闭 sync_with_stdio,cout 可以输出性能 printf。不知道关掉有没有副作用。

variadic printf as above is included in the test.

上面的可变参数 printf 包含在测试中。

void TestThree(){
   ios_base::sync_with_stdio(false);

   for(int i = 0; i < 1000000; i++){
       safe_printf("This is the value: %d, %s\n", i, s.c_str());
   }
}

Again print is still faster even sync_with_stdio is turn off that I don't quite understand.

再次打印仍然更快,即使我不太明白 sync_with_stdio 关闭。

printf:204099118 cout:684881656, diff(prinf-cout) -480782538
printf:227662054 cout:758110546, diff(prinf-cout) -530448492
printf:221534729 cout:732899502, diff(prinf-cout) -511364773
printf:203223553 cout:669386010, diff(prinf-cout) -466162457
printf:199923091 cout:662735504, diff(prinf-cout) -462812413
printf:207555026 cout:676522292, diff(prinf-cout) -468967266
printf:204846108 cout:655571171, diff(prinf-cout) -450725063
printf:199512948 cout:660717898, diff(prinf-cout) -461204950
printf:204251614 cout:676688426, diff(prinf-cout) -472436812
printf:201601655 cout:677217080, diff(prinf-cout) -475615425
printf:200311160 cout:674022044, diff(prinf-cout) -473710884
printf:202562272 cout:658126890, diff(prinf-cout) -455564618
printf:203100080 cout:660348312, diff(prinf-cout) -457248232
printf:228058969 cout:743305386, diff(prinf-cout) -515246417
printf:223976399 cout:742205352, diff(prinf-cout) -518228953
printf:204651603 cout:681893639, diff(prinf-cout) -477242036
printf:222386690 cout:745000014, diff(prinf-cout) -522613324
printf:217610811 cout:790129902, diff(prinf-cout) -572519091
printf:224156133 cout:758849874, diff(prinf-cout) -534693741
printf:204485998 cout:660430561, diff(prinf-cout) -455944563
printf:201259591 cout:667987480, diff(prinf-cout) -466727889
printf:204473955 cout:658096409, diff(prinf-cout) -453622454
printf:201986282 cout:670503647, diff(prinf-cout) -468517365
printf:204846379 cout:687089511, diff(prinf-cout) -482243132
printf:204962342 cout:681630774, diff(prinf-cout) -476668432
printf:202546744 cout:662300255, diff(prinf-cout) -459753511
printf:220856194 cout:757583221, diff(prinf-cout) -536727027
printf:232926622 cout:837024111, diff(prinf-cout) -604097489
printf:234187491 cout:801665699, diff(prinf-cout) -567478208
printf:244214945 cout:787558503, diff(prinf-cout) -543343558
printf:220227724 cout:788444837, diff(prinf-cout) -568217113
printf:227440252 cout:754596137, diff(prinf-cout) -527155885
printf:238158449 cout:814421312, diff(prinf-cout) -576262863
printf:205372147 cout:676782112, diff(prinf-cout) -471409965
printf:197566880 cout:662635516, diff(prinf-cout) -465068636
printf:199640024 cout:726088581, diff(prinf-cout) -526448557
printf:201249659 cout:660196078, diff(prinf-cout) -458946419
printf:201578923 cout:676444130, diff(prinf-cout) -474865207
printf:201034436 cout:690638993, diff(prinf-cout) -489604557
printf:208951246 cout:682032486, diff(prinf-cout) -473081240
printf:199928991 cout:673437083, diff(prinf-cout) -473508092
printf:233538795 cout:810890687, diff(prinf-cout) -577351892
printf:243872766 cout:847098807, diff(prinf-cout) -603226041
printf:243341748 cout:769877622, diff(prinf-cout) -526535874
printf:230232172 cout:733867319, diff(prinf-cout) -503635147
printf:220371109 cout:709704232, diff(prinf-cout) -489333123
printf:251803876 cout:790624221, diff(prinf-cout) -538820345
printf:202999188 cout:910149931, diff(prinf-cout) -707150743
printf:201931656 cout:669276274, diff(prinf-cout) -467344618
printf:200511370 cout:677748018, diff(prinf-cout) -477236648

回答by vitaut

The safe_printffunction by Andrei Alexandrescu is quite clever, but unfortunately has serious limitations:

safe_printfAndrei Alexandrescu的函数非常聪明,但不幸的是有严重的局限性:

  1. Each argument is processed twice, once to check its validity and the second time to format it with printf. The check can be disabled in release mode to avoid overhead, but this seriously undermines safety.

  2. It doesn't work with positional arguments.

  1. 每个参数被处理两次,一次检查其有效性,第二次用printf. 可以在发布模式下禁用检查以避免开销,但这会严重破坏安全性。

  2. 它不适用于位置参数。

There is a number of ways how you can improve on it:

有很多方法可以改进它:

  1. Don't always forward formatting to printfonce the argument type is established. For example, this benchmarkshows that it's possible to implement integer formatting which is up to 6.7 times faster than sprintf.

  2. To implement positional arguments you need to store arguments in an array because they need to be addressed by an index.

  1. printf一旦建立了参数类型,不要总是将格式转发到。例如,该基准测试表明可以实现比sprintf.

  2. 要实现位置参数,您需要将参数存储在数组中,因为它们需要通过索引进行寻址。

Here's an example of how it can be done:

这是一个如何完成的示例:

class Arg {
 private:
  enum Type { INT, DOUBLE };
  Type type;
  union {
    int int_value;
    double dbl_value;
  } u;
 public:
  Arg(int value) : type(INT) { u.int_value = value; }
  Arg(double value) : type(DOUBLE) { u.dbl_value = value; }
  // other types
};

void do_safe_printf(const char *format, const Arg *args, size_t num_args) {
  // here we can access arguments by index
}

template <typename... Args>
void safe_printf(const char *format, const Args&... args) {
  Arg arg_array[] = {args...};
  do_safe_printf(format, arg_array, sizeof...(Args));
}

Apart from supporting positional arguments, this approach will also minimize the code bloat as all the work is done by a single function do_safe_printfwhile safe_printffunction template only places the arguments in an array.

除了支持位置参数外,这种方法还将最大限度地减少代码膨胀,因为所有工作都由单个函数完成,do_safe_printfsafe_printf函数模板仅将参数放在数组中。

These and other improvements have been implemented in the fmt library. According to benchmarks it is comparable both in speedand compiled code sizeto native printfimplementation

这些和其他改进已在fmt 库中实现。根据基准测试,它在速度编译代码大小方面都可以与本机printf实现相媲美

Disclaimer: I'm the author of this library.

免责声明:我是这个库的作者。

回答by TemplateRex

At GoingNative2012, Andrei Alexandrescu gave an implementationof a variadic safe_printf(). He uses a two-step approach. First, check the format specifiers; and second, normalize the arguments being passed. Because the implementation delegates to printf()with checked formatters and arguments, there is no std::coutin sight and hardly any runtime overhead (the exception path should not be taken often in regular code)

在 GoingNative2012 上,Andrei Alexandrescu给出了一个可变参数safe_printf(). 他使用两步法。首先,检查格式说明符;其次,规范化传递的参数。因为实现委托给printf()检查过的格式化程序和参数,所以std::cout看不到并且几乎没有任何运行时开销(在常规代码中不应该经常采用异常路径)

Code summary:

代码摘要:

template <typename... Ts>
int safe_printf(const char * f, const Ts&... ts) 
{
    check_printf(f, normalizeArg(ts)...);  // check format specifiers
    return printf(f, normalizeArg(ts)...); // output with clean arguments
}

void check_printf(const char * f) 
{
    // checking is O(N) in length of format specifiers
    for (; *f; ++f) {
        if (*f != '%' || *++f == '%') continue;
        throw Exc("Bad format");
    }
}

// variadic check_print(const char*, T...) omitted, see slides

template <class T>
typename enable_if<is_integral<T>::value, long>::type
normalizeArg(T arg) 
{ 
    return arg; 
}

// more overloads for float, T* and std::string omitted, see slides