2013-07-16 39 views
10

Mẫu variadic được giới thiệu trong C++ 11. Và tôi tìm thấy chức năng printf có thể được thay thế bằng cách sử dụng nó. Tuy nhiên, cout được sử dụng trong quá trình triển khai. Tôi tự hỏi nếu nó có thể sử dụng cái gì khác để đạt được loại an toàn nhưng không hy sinh quá nhiều hiệu suất.C++ 11 Hiệu suất in Variadic

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 theo yêu cầu, tôi thực hiện kiểm tra để so sánh hiệu suất giữa cout và printf. gcc phiên bản 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 lượt sync_with_stdio tắt.

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 

tắt 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 

Vì vậy cout có thể ra printf hiệu suất nếu lượt sync_with_stdio tắt mà tôi chưa từng nghe sync_with_stdio trước. Tôi không biết nếu có tác dụng phụ để tắt.

Biến thể in offset như trên được bao gồm trong thử nghiệm.

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()); 
    } 
} 

Lần in còn nhanh hơn ngay cả sync_with_stdio bị tắt mà tôi không hiểu lắm.

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 
+9

Bạn đã đo điểm chuẩn này hay đây là một trong số đó "Tôi chưa từng chạm vào điều này chút nào, nhưng vì nó không phải là C Tôi nghĩ rằng đó là tình huống" chậm chạp "? – Rapptz

+0

Không, tôi chưa có. Kể từ std :: count là chậm hơn printf, hiệu suất không thể nhanh như sử dụng printf. Vì vậy, tôi tự hỏi nếu có bất kỳ thay thế hoặc cách thông minh. –

+0

@MichaelD Không có. – Rapptz

Trả lời

15

các safe_printf chức năng của Andrei Alexandrescu là khá thông minh, nhưng tiếc là có những hạn chế nghiêm trọng:

  1. Mỗi đối số được xử lý hai lần, một lần để kiểm tra nó hiệu lực và lần thứ hai để định dạng nó với printf. Việc kiểm tra có thể được vô hiệu hóa trong chế độ phát hành để tránh chi phí, nhưng điều này nghiêm trọng làm suy yếu an toàn.

  2. Nó không hoạt động với các đối số vị trí.

Có một số cách làm thế nào bạn có thể cải thiện nó:

  1. Đừng luôn mong định dạng cho printf lần kiểu lập luận được thành lập. Ví dụ: this benchmark cho thấy rằng có thể triển khai định dạng số nguyên nhanh hơn 6,7 lần so với sprintf.

  2. Để triển khai đối số vị trí, bạn cần phải lưu trữ đối số trong một mảng vì chúng cần được chỉ mục bằng chỉ mục.

Dưới đây là một ví dụ về cách nó có thể được thực hiện:

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)); 
} 

Ngoài việc hỗ trợ đối số vị trí, phương pháp này cũng sẽ hạn chế tối đa các khóa thừa mã như tất cả công việc được thực hiện bởi một chức năng duy nhất do_safe_printf khi safe_printf mẫu chức năng chỉ đặt các đối số trong một mảng.

Những cải tiến này và các cải tiến khác đã được triển khai trong fmt library. Theo tiêu chuẩn đó có thể so sánh cả trong speedcompiled code size để mẹ đẻ printf thực hiện

Disclaimer: Tôi là tác giả của thư viện này.

+3

Có vẻ tốt! Bạn đã đề xuất điều này để Tăng/danh sách gửi thư đề xuất std? (giấy phép Boost khá giống với giấy phép BSD). – TemplateRex

+0

@TemplateRex Cảm ơn. Tôi đã không đề xuất nó để Tăng hoặc đề xuất std, nhưng tôi sẽ xem xét điều này. – vitaut

+3

Tuyên bố từ chối trách nhiệm -> Tôn trọng. – kevinarpe

6

Tại GoingNative2012, Andrei Alexandrescu gave an implementation của variadic safe_printf(). Anh ta sử dụng cách tiếp cận hai bước. Trước tiên, hãy kiểm tra các định dạng thông số; và thứ hai, bình thường hóa các đối số được truyền đi. Bởi vì các đại biểu thực hiện để printf() với trình định dạng kiểm tra và lập luận, không có std::cout trong tầm nhìn và hầu như không có chi phí thời gian chạy (con đường ngoại lệ không cần được thực hiện thường xuyên trong mã thường xuyên) tóm tắt

Code:

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 
+1

Bất kỳ ý tưởng nào nếu có bất kỳ triển khai hoàn chỉnh ý tưởng của Andrei trong sự tồn tại? – RushPL

+0

@RushPL Tôi không biết! – TemplateRex

+2

@RushPL https://github.com/cppformat/cppformat - không thực sự là triển khai ý tưởng của Andrei, nhưng cái gì đó tốt hơn =) – vitaut