C++ 使 windows 中的控制台应用程序打印速度与 linux 中一样快

C++ getting console app in windows to print as fast as in linux

此代码:

#include <iostream>
#include <chrono>
#include <functional>
#include <time.h>

int main() {
    time_t b4 = time(NULL);
    for (int i = 0; i < 50000; i++)
        std::cout << i << " ";
    std::cout << std::endl;
    time_t a4 = time(NULL);
    std::cout << "Time taken is " << difftime(a4, b4);
    getchar();
}

in Windows when compiled/built/run with Visual Studio with commands:

CL.exe /c /Zi /nologo /W3 /WX- /diagnostics:column /sdl /O2 /Oi /GL /D _MBCS /Gm- /EHsc /MD /GS /Gy /fp:precise /permissive- /Zc:wchar_t /Zc:forScope /Zc:inline /FA /Fa"x64\Release\" /Fo"x64\Release\" /Fd"x64\Release\vc142.pdb" /Gd /TP /FC /errorReport:prompt ..\src\console_printf.cpp
         console_printf.cpp
       Link:
link.exe /ERRORREPORT:PROMPT /OUT:"Release\windows.exe" /NOLOGO kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /MANIFEST /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /manifest:embed /DEBUG:FULL /PDB:"Release\windows.pdb" /SUBSYSTEM:CONSOLE /OPT:REF /OPT:ICF /LTCG:incremental /TLBID:1 /DYNAMICBASE /NXCOMPAT /IMPLIB:"Release\windows.lib" /MACHINE:X64 x64\Release\console_printf.obj

最终打印(打印后 ... 49998 49999

Time taken is 15

Linux 上的 compiled/built/run 与以下代码相同:

g++    -c -O2 -MMD -MP -MF "build/Release/GNU-Linux/_ext/511e4115/console_printf.o.d" -o build/Release/GNU-Linux/_ext/511e4115/console_printf.o ../src/console_printf.cpp
mkdir -p dist/Release/GNU-Linux

最终打印(打印后 ... 49998 49999

Time taken is 1

也就是说,console/terminal 在 Linux 中的打印速度要快得多。这两个测试都在发布模式下启用了优化。虽然测试是在两台不同的机器上完成的(一台 运行ning Windows/Visual Studio,另一台 运行ning Linux),但两者的计算能力相当。

有没有办法让 Windows 控制台打印速度与 Linux 一样快?我 运行 一个数字 intensive/iterative 定期在控制台上显示进度的代码,我现在担心不必要的 Windows 控制台打印可能会因为算法的错误而弄乱记录的时间但是因为 Windows 控制台打印无意中成了瓶颈。

如果您想改进控制台 I/O(这是大多数 I/O 绑定应用程序的瓶颈)打印到缓冲区然后阻止将缓冲区写入控制台。

#include <string>
#include <iostream>
#include <sstream>

int main ()
{
    std::string buffer;
    buffer.reserve(5000);
    std::ostringstream number_stream(buffer);
    for (unsigned int i = 0; i < 50000; ++i)
    {
        number_stream << i << " ";
    }
    number_stream << "\n";
    const unsigned int length = buffer.length();
    std::cout.write(buffer.c_str(), length);

    return 0;
}

以上代码使用 std::string 作为其缓冲区。所有数字都被格式化(人类可读)到缓冲区中。然后使用块写入将缓冲区写入控制台。

buffer.reserve()背后的想法是分配足够大的缓冲区以减少重新分配。

您的标准库实现可能是您问题的一部分。我 运行 使用普通 Visual C++ 编写以下代码:

#define WRITE_CONSOLE_API
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <chrono>
#include <functional>
#include <time.h>
#include <windows.h>



int main() {
    LARGE_INTEGER freq;
    QueryPerformanceFrequency(&freq);
    LARGE_INTEGER start;
    LARGE_INTEGER stop;
    std::ios_base::sync_with_stdio(true);
#ifdef WRITE_CONSOLE_API
    char buf[20];
    static char buf2[2] = { '\r', '[=10=]' };
    std::uninitialized_fill_n(buf, 20, '[=10=]');
    auto con = GetStdHandle(STD_OUTPUT_HANDLE);
    DWORD l;
    DWORD lr;
#endif
    QueryPerformanceCounter(&start);
#ifdef WRITE_CONSOLE_API
    for (int i = 0; i < 50000; i++)
    {
       if (i)
          WriteConsoleA(con, buf2, 1, &lr, NULL);
       _itoa(i, buf, 10);
       l = strlen(buf);
       WriteConsoleA(con, buf, l, &lr, NULL);
    }
    buf2[0] = '\n';
    WriteConsoleA(con, buf2, 1, &lr, NULL);
#else
    for (int i = 0; i < 50000; i++)
        std::cout << '\r' << i;
    std::cout << std::endl;
#endif
    QueryPerformanceCounter(&stop);
    double diff = stop.QuadPart - start.QuadPart;
    std::cout << "Time taken is " << diff / freq.QuadPart << " secs\n";
    std::cin.ignore(1);
}

定义了 WRITE_CONSOLE_API(它使用了 Windows API 调用 WriteConsole),也没有定义(它使用了 std::cout) .

定义了WRITE_CONSOLE_API,结果是

Time taken is 2.12448 secs

未定义 WRITE_CONSOLE_API,结果为

Time taken is 6.25676 secs

如果您使用 space 而不是 \r(即强制控制台 window 滚动),您会得到

Time taken is 3.02435 secs

定义了WRITE_CONSOLE_API,并且

Time taken is 7.27557 secs

WRITE_CONSOLE_API 未定义。滚动似乎始终增加 1 秒。

您应该在自己的机器上尝试此操作,因为时间可能会有所不同。

我进行了调试,所以没有优化。通过优化,标准库版本减少到 6.8 秒(滚动)和 5.6 秒(非滚动),但 Windows API 版本没有变化。

如果您真的想将程序的实际工作与操作系统的变化无常分开,您可以创建一个线程来完成这项工作,并使用另一个线程将进度写入控制台。你真的只需要将它们与实际进度计数联系起来,如 std::atomic<some_int_type>).