The evolution of writing output (C++)

Scenarios

#include <chrono>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <print>
#include <string>
#include <string_view>

int main()
{
    using namespace std::chrono;

    // -------------- C style (std::printf / std::fprintf) ----------
    {
        auto start = high_resolution_clock::now();

        const char* msg = "message content";
        std::FILE* fp = fopen("file_c.txt", "w");
        for (uint32_t i = 0; i < 100000; ++i)
        {
            auto now = system_clock::now();
            std::time_t time = system_clock::to_time_t(now);
            std::tm local = *std::localtime(&time);

            std::fprintf(fp, "%04d-%02d-%02dT%02d:%02d:%02d %s\n",
                local.tm_year + 1900, local.tm_mon + 1, local.tm_mday,
                local.tm_hour, local.tm_min, local.tm_sec, msg);
            // std::printf for stdout
        }
        std::fflush(fp);
        std::fclose(fp);

        auto end = high_resolution_clock::now();
        std::println("{:<20}{:>10}ns",
            std::string{"C-style took:"},
            duration_cast<nanoseconds>(end - start).count());
    }

    // -------------- C++ style (std::cout / std::ofstream) ---------
    {
        auto start = high_resolution_clock::now();

        std::string msg = "message content";
        std::ofstream ofs("file_cpp_old.txt");
        for (uint32_t i = 0; i < 100000; ++i)
        {
            auto now = system_clock::now();
            std::time_t time = system_clock::to_time_t(now);
            std::tm local = *std::localtime(&time);

            ofs << std::setfill('0')
                << std::setw(4) << (local.tm_year + 1900) << "-"
                << std::setw(2) << (local.tm_mon + 1) << "-"
                << std::setw(2) << local.tm_mday << "T"
                << std::setw(2) << local.tm_hour << ":"
                << std::setw(2) << local.tm_min << ":"
                << std::setw(2) << local.tm_sec << " "
                << msg << "\n";
            // std::cout for stdout
        }
        ofs.flush();
        ofs.close();

        auto end = high_resolution_clock::now();
        std::println("{:<20}{:>10}ns",
            std::string{"Old C++-style took:"},
            duration_cast<nanoseconds>(end - start).count());
    }

    // -------------- C++ style (std::print / std::print) -----------
    {
        auto start = high_resolution_clock::now();

        std::string_view msg = "message content";
        std::FILE* fp = std::fopen("file_c++_new.txt", "w");
        for (uint32_t i = 0; i < 100000; ++i)
        {
            auto now = system_clock::now();
            std::time_t time = system_clock::to_time_t(now);
            std::tm local = *std::localtime(&time);

            std::print(fp, "{:04}-{:02}-{:02}T{:02}:{:02}:{:02} {}\n",
                local.tm_year + 1900, local.tm_mon + 1, local.tm_mday,
                local.tm_hour, local.tm_min, local.tm_sec, msg);
            // std::print for stdout
        }
        std::fflush(fp);
        std::fclose(fp);

        auto end = high_resolution_clock::now();
        std::println("{:<20}{:>10}ns",
            std::string{"New C++-style took:"},
            duration_cast<nanoseconds>(end - start).count());
    }

    return 0;
}
% uname -a
Darwin FMXK77H3WK 24.6.0 Darwin Kernel Version 24.6.0: Mon Jul 14 11:30:55 PDT 2025; root:xnu-11417.140.69~1/RELEASE_ARM64_T6031 arm64
% g++ --version
Apple clang version 17.0.0 (clang-1700.0.13.5)
Target: arm64-apple-darwin24.6.0
% g++ main.cpp -O3 --std=c++23
% ./a.out                     
C-style took:         48350334ns
Old C++-style took:   54821291ns
New C++-style took:   84056042ns

Comparison

Aspect C-style C++-style (< C++23) C++-style (>= C++23)
Type safety Not type-safe Type-safe Strong type-safety
Verbosity Low High (lots of “>>“) Very low
Performance * Best Good Worst

* Depends a lot on the use case. I will stick with the result of the test. Lots of writes into a file.