总结

print 是一个比字符串连接慢的操作 (一个一个print比全部连在一起print慢)

printf 开销还是挺大的

String.format 后再 print 比 printf 快 (Java)

sprintf 后再 fputs 也比 printf 快 (C)

C/C++ 里最快且比较方便的输出方式

需要换行: puts(const char *string); // in stdout.h 宽字符版_putws

无需换行: fputs(const char *string, stdout); // in stdout.h 宽字符版fputws

输出单个字符: putchar(int char); // in stdout.h 宽字符版putwchar

2021/9/12补充: 在Linux下使用fputs和putchar有必要在后面加上 fflush(FILE *STREAM) 不然字符串可能不会马上输出出来, Windows则不用

Windows下还能用Win32 API来输出WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), const VOID *lpBuffer, DWORD nNumberOfCharsToWrite, LPDWORD lpNumberOfCharsWritten, NULL); // in WinCon.h, include Windows.h 宽字符版WriteConsoleW (未测试速度)

*nix下可用 write(int fd, const void *buf, size_t nbyte); // in unistd.h

用 std::cout 等流操作比 printf 快。

运行结果 – print_test.cpp
(ms, CMD.EXE)
TIMES=100000
T1=40056
T2=17407
T3=17505
T5=17730
T6=12430
T7=16906
T8=10485
T9=463
T10=61242
T11=5456

(ms, CMD>nul)
TIMES=100000
T1=1452
T2=735
T3=743
T5=841
T6=567
T7=725
T8=519
T9=14
T10=2965
T11=271

(μs, CMD.EXE)
TIMES=100
T1=98656
T2=97342
T3=79117
T5=84351
T6=81348
T7=91512
T8=94304
T9=1452
T10=135623
T11=77410

(μs, CMD>nul)
TIMES=100
T1=1468
T2=734
T3=723
T5=800
T6=557
T7=731
T8=470
T9=31
T10=2844
T11=269
C++ Code
#include <iostream>
#include <chrono>
#include <sstream>
using namespace std;
typedef long long time_t;

time_t getTime() {
    // return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); // 毫秒
    return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); // 微秒
}
#define TIMES 100
int main() {
    string str1 = "Hello";
    string str2 = " ";
    string str3 = "world";

    time_t t1 = getTime();
    // T1
    for (int i = 0; i < TIMES; i++) {
        cout << str1 << str2 << str3 << endl;
    }
    time_t t2 = getTime();
    // T2
    for (int i = 0; i < TIMES; i++) {
        cout << str1 + str2 + str3 << endl; 
    }
    time_t t3 = getTime();
    // T3
    for (int i = 0; i < TIMES; i++) {
        cout << str1 + str2 + str3;
        cout << endl;
    }
    time_t t4 = getTime();
    // T4
    // 
    time_t t5 = getTime();
    // T5
    for (int i = 0; i < TIMES; i++) {
        ostringstream tmp;
        tmp << str1 << str2 << str3;
        cout << tmp.str() << endl;
    }
    time_t t6 = getTime();
    // T6
    for (int i = 0; i < TIMES; i++) {
        ostringstream tmp;
        tmp << str1 << str2 << str3 << endl;
        cout << tmp.str();
    }
    time_t t7 = getTime();
    // T7
    string tmp1 = str1 + str2 + str3;
    for (int i = 0; i < TIMES; i++) {
        cout << tmp1 << endl;
    }
    time_t t8 = getTime();
    // T8
    string tmp2 = str1 + str2 + str3 + '\n';
    for (int i = 0; i < TIMES; i++) {
        cout << tmp2;
    }
    time_t t9 = getTime();
    // T9
    ostringstream oss;
    for (int i = 0; i < TIMES; i++) {
        oss << str1 << str2 << str3 << endl;
    }
    cout << oss.str();
    time_t t10 = getTime();
    // T10
    for (int i = 0; i < TIMES; i++) {
        printf("%s%s%s\n", str1.c_str(), str2.c_str(), str3.c_str());
    }
    time_t t11 = getTime();
    // T11
    for (int i = 0; i < TIMES; i++) {
        char tmp[20];
        sprintf(tmp, "%s%s%s\n", str1.c_str(), str2.c_str(), str3.c_str());
        fputs(tmp, stdout);
    }

    time_t t12 = getTime();

    cerr << "TIMES=" << TIMES << endl;
    cerr << "T1=" << t2 - t1 << endl;
    cerr << "T2=" << t3 - t2 << endl;
    cerr << "T3=" << t4 - t3 << endl;
    // cerr << "T4=" << t5 - t4;
    cerr << "T5=" << t6 - t5 << endl;
    cerr << "T6=" << t7 - t6 << endl;
    cerr << "T7=" << t8 - t7 << endl;
    cerr << "T8=" << t9 - t8 << endl;
    cerr << "T9=" << t10 - t9 << endl;
    cerr << "T10=" << t11 - t10 << endl;
    cerr << "T11=" << t12 - t11 << endl;
    return 0;
}
运行结果 – PrintTest.java
    // 感觉6应该要比8慢的,我也不知道为什么
    /*
     * (ms, IDE Console) TIMES=100000 T1=1176 T2=547 T3=539 T4=545 T5=534 T6=400
     * T7=533 T8=416 T9=348 T10=1485
     * 
     ***** (ms, CMD.EXE) TIMES=100000 T1=18850 T2=6120 T3=6014 T4=6040 T5=5853 T6=3244
     * T7=5986 T8=3242 T9=450 T10=12600
     * 
     ***** (ms, CMD>NUL) TIMES=100000 T1=395 T2=220 T3=212 T4=200 T5=211 T6=115 T7=178
     * T8=96 T9=46 T10=1012
     * 
     * (μs, IDE Console) TIMES=100 T1=6581 T2=2826 T3=1607 T4=1558 T5=1209 T6=625
     * T7=743 T8=356 T9=79 T10=52365
     * 
     ***** (μs, CMD.EXE) TIMES=100 T1=16431 T2=8309 T3=4986 T4=7384 T5=7118 T6=2860
     * T7=4649 T8=4850 T9=444 T10=63023
     * 
     ***** (μs, CMD>NUL) TIMES=100 T1=4313 T2=2079 T3=1527 T4=1321 T5=840 T6=307 T7=411
     * T8=192 T9=67 T10=42275
     */
Java Code
public class PrintTest {
    public static void main(String[] args) {
        new PrintTest();
    }

    public long getTime() {
        return System.currentTimeMillis();
//        return System.nanoTime() / 1000; // 微秒
    }

    int times = 100000;

    public PrintTest() {
        String str1 = "Hello";
        String str2 = " ";
        String str3 = "world";

        long t1 = getTime();
        // T1
        for (int i = 0; i < times; i++) {
            System.out.print(str1);
            System.out.print(str2);
            System.out.print(str3);
            System.out.print('\n');
        }

        long t2 = getTime();
        // T2
        for (int i = 0; i < times; i++) {
            System.out.print(str1 + str2 + str3);
            System.out.println();
        }

        long t3 = getTime();
        // T3
        for (int i = 0; i < times; i++) {
            System.out.print(str1 + str2 + str3);
            System.out.print('\n');
        }

        long t4 = getTime();
        // T4
        for (int i = 0; i < times; i++) {
            System.out.println(str1 + str2 + str3);
        }

        long t5 = getTime();
        // T5
        for (int i = 0; i < times; i++) {
            StringBuilder tmp = new StringBuilder();
            System.out.println(tmp.append(str1).append(str2).append(str3));
        }

        long t6 = getTime();
        // T6
        for (int i = 0; i < times; i++) {
            StringBuilder tmp = new StringBuilder();
            System.out.print(tmp.append(str1).append(str2).append(str3).append('\n'));
        }

        long t7 = getTime();
        // T7
        String tmp1 = str1 + str2 + str3;
        for (int i = 0; i < times; i++) {
            System.out.println(tmp1);
        }

        long t8 = getTime();
        // T8
        String tmp2 = str1 + str2 + str3 + '\n';
        for (int i = 0; i < times; i++) {
            System.out.print(tmp2);
        }

        long t9 = getTime();
        // T9
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < times; i++) {
            sb.append(str1);
            sb.append(str2);
            sb.append(str3);
            sb.append('\n');
        }
        System.out.print(sb);

        long t10 = getTime();

        for (int i = 0; i < times; i++) {
            System.out.printf("%s%s%s\n", str1, str2, str3);
        }

        long t11 = getTime();

        for (int i = 0; i < times; i++) {
            System.out.print(String.format("%s%s%s\n", str1, str2, str3));
        }

        long t12 = getTime();

        System.err.println("TIMES=" + times);
        System.err.println("T1=" + (t2 - t1));
        System.err.println("T2=" + (t3 - t2));
        System.err.println("T3=" + (t4 - t3));
        System.err.println("T4=" + (t5 - t4));
        System.err.println("T5=" + (t6 - t5));
        System.err.println("T6=" + (t7 - t6));
        System.err.println("T7=" + (t8 - t7));
        System.err.println("T8=" + (t9 - t8));
        System.err.println("T9=" + (t10 - t9));
        System.err.println("T10=" + (t11 - t10));
        System.err.println("T11=" + (t12 - t11));
    }
}
运行结果 – c_exclusive.cpp
(ms, CMD.EXE)
TIMES=100000
T1=25176
T2=18729
T3=18837
T4=30520
T5=6679
T6=12478
T7=18712

(ms, CMD>nul)
TIMES=100000
T1=729
T2=18920
T3=19638
T4=1234
T5=251
T6=493
T7=720

(μs, CMD.EXE)
TIMES=100
T1=86300
T2=86447
T3=86957
T4=99902
T5=77321
T6=84903
T7=80665

(μs, CMD>nul)
TIMES=100
T1=758
T2=90062
T3=83273
T4=1206
T5=245
T6=477
T7=702
C/C++ Exclusive Code
#include <iostream>
#include <chrono>
using namespace std;
typedef long long time_t;
time_t getTime() {
    // return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); // 毫秒
    return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); // 微秒
}
#define TIMES 100
int main() {
    string str = "TEST";
    size_t len = str.length();

    time_t t1 = getTime();
    // T1
    for (int i = 0; i < TIMES; i++) {
        cout << str << endl;
    }
    time_t t2 = getTime();
    // T2
    for (int i = 0; i < TIMES; i++) {
        cerr << str << endl;
    }
    time_t t3 = getTime();
    // T3
    for (int i = 0; i < TIMES; i++) {
        clog << str << endl;
    }
    time_t t4 = getTime();
    // T4
    for (int i = 0; i < TIMES; i++) {
        printf("%s\n", str.c_str());
    }
    time_t t5 = getTime();
    // T5
    for (int i = 0; i < TIMES; i++) {
        puts(str.c_str());
    }
    time_t t6 = getTime();
    // T6
    for (int i = 0; i < TIMES; i++) {
        fputs(str.c_str(), stdout);
        putchar('\n');
    }
    time_t t7 = getTime();
    // T7
    for (int i = 0; i < TIMES; i++) {
        fwrite(str.c_str(), sizeof(char), len, stdout);
        putchar('\n');
    }
    time_t t8 = getTime();

    cerr << "TIMES=" << TIMES << endl;
    cerr << "T1=" << t2 - t1 << endl;
    cerr << "T2=" << t3 - t2 << endl;
    cerr << "T3=" << t4 - t3 << endl;
    cerr << "T4=" << t5 - t4 << endl;
    cerr << "T5=" << t6 - t5 << endl;
    cerr << "T6=" << t7 - t6 << endl;
    cerr << "T7=" << t8 - t7 << endl;
    return 0;
}
最后修改日期: 2021年9月23日

作者

0 0 投票数
文章评分
订阅评论
提醒
1 评论
内联反馈
查看所有评论
方笛
管理员
1 年 前

很有意思的一篇文章。Java我不太熟悉,只说下C。 与你所的出来的结论不同,一般认为,在绝大多数情况下,因为文件指针同步的缘故,printf这种C-sytle的输入输出,要比cout这种CPP-style的输入输出稍快。在算法竞赛中,面对极大的输出量的情况下,为了争取时间,选手会尽量少用std::cout而使用printf、puts这样的C-style函数(比如我本人在打比赛的时候从未用过std::cin、std::cout),甚至有的选手还会自行使用putchar实现输出,以追求更快的速度。但你这篇文章用了一个很好地特例,来得出了这个看似与主流观点相矛盾的结论。 假设std::string对象str中不包含任何占位符(只是普通字符串,非格式化字符串),考察下面两个printf函数的写法,printf(str.c_str())和printf(“%s”, str.c_str())。这两种写法的区别是什么呢?显然,后者较前者多了一个字符串格式化的过程。可能,在编译器对printf的实现上,前者经检查无占位符后就直接输出,后者要重新开辟内存空间,将%s对应的内容写入合适的位置,再一并输出——这将可能导致后者的速度慢于前者(事实上,如果是短的字符串的话,前者可能较快;如果是较长的字符串的话,后者可能更快,这可能是由于printf函数需要检查、匹配占位符的缘故)。同时,这也是puts和putchar速度快的原因。 事实上,C++也为我们提供了关闭文件指针同步的方法std::ios::sync_with_stdio(false)。大多数人的观点认为只有在关闭了文件指针同步的情况下,cin、cout才能和scanf、printf有的一拼,相关的性能测试网上有很多,不多赘述。然而,你的文章以一个新奇的角度,讨论了对于复杂字符串文本的输出问题。换而言之,C++的这种stream风格的字符串输出,可能天生就更适合于需要格式化输出的字符串。 很高兴你注意到了多次重复测试的问题,但如果你能测试一些更长的字符串,比如说拥有十万个字符的随机字符串,结论会更具有普遍性(在大规模的输出下,文件指针同步带来的性能差距可能会变得非常明显)。另外,希望你能注意到不同编译器和优化选项对不同输出方法的差异。事实上,在VS 2022中(MSVC 19.30),使用Release编译程序,在编译器的优化下,即便是printf(“%s\n”, str.c_str())这种写法也要比cout << str << endl;要快(而前者我在mingw-w64 GCC 11.2下测试,确实要比后者慢)。另外呢,cout << str和cout << str.c_str()可能在性能上有所差距(如果不开优化开关的话差距甚至还不小),因为前者和后者,从运算符重载的角度来看,是完全不同的。建议统一以C-style风格的字符串进行测试,或许结论会更具有普遍性一些。