この記事は「大分高専 Advent Calendar 2024」の18日の記事です。
はじめに
大分高専 Advent Calendar 2024 に参加させていただきました、大分高専 2年 機械工学科のkazuma11121125です。
動作環境
・Ubuntu 22.04 LTS
このページの内容
・printf
、std::cout
などの実行速度の比較。
きっかけ
ターミナルで動画を再生できるものをC++で開発した際に、printf
の速度が足りないという問題が発生しました。これをどうにかするべく、std::cout
などあらゆるものを比較し、最速なものを探しだそうとしました。
計測方法
以下のコードを実行し、計測しました。
#include <iostream>
#include <unistd.h> // write
#include <cstdio> // printf, fprintf
#include <fcntl.h> // syscall
#include <chrono>
// 時間計測用関数
template <typename Func>
double measure_time(Func func) {
auto start = std::chrono::high_resolution_clock::now();
func();
auto end = std::chrono::high_resolution_clock::now();
return std::chrono::duration<double>(end - start).count();
}
// printfを使用した出力
void test_printf() {
for (int i = 0; i < 100000; i++) {
printf("Hello, World!\n");
}
fflush(stdout);
}
// fprintfを使用した出力
void test_fprintf() {
for (int i = 0; i < 100000; i++) {
fprintf(stdout, "Hello, World!\n");
}
fflush(stdout);
}
// writeを使用した出力
void test_write() {
for (int i = 0; i < 100000; i++) {
write(1, "Hello, World!\n", 14);
}
}
// std::coutを使用した出力
void test_cout() {
for (int i = 0; i < 100000; i++) {
std::cout << "Hello, World!\n";
std::cout.flush();
}
}
// std::wcoutを使用した出力
void test_wcout() {
for (int i = 0; i < 100000; i++) {
std::wcout << L"Hello, World!\n";
std::wcout.flush();
}
}
// アセンブリでのsyscallを使用した出力
void test_syscall() {
const char *msg = "Hello, World!\n";
for (int i = 0; i < 100000; i++) {
asm volatile (
"mov $1, %%rax\n\t" // syscall番号: write
"mov $1, %%rdi\n\t" // ファイルディスクリプタ: stdout
"mov %0, %%rsi\n\t" // 出力文字列
"mov $14, %%rdx\n\t" // 出力文字数
"syscall"
:
: "r"(msg)
: "%rax", "%rdi", "%rsi", "%rdx"
);
}
}
int main() {
std::cout << "Measuring output methods with 100,000 iterations:\n";
double time_printf = measure_time(test_printf);
double time_fprintf = measure_time(test_fprintf);
double time_write = measure_time(test_write);
double time_cout = measure_time(test_cout);
double time_wcout = measure_time(test_wcout);
double time_syscall = measure_time(test_syscall);
std::cout << "printf: " << time_printf << " seconds\n";
std::cout << "fprintf: " << time_fprintf << " seconds\n";
std::cout << "write: " << time_write << " seconds\n";
std::cout << "std::cout: " << time_cout << " seconds\n";
std::cout << "std::wcout: " << time_wcout << " seconds\n";
std::cout << "syscall: " << time_syscall << " seconds\n";
return 0;
}
コンパイル
g++ -o output_speed_test output_speed_test.cpp
実行
./output_speed_test
複数の出力方法を用いて同じ文字列を100,000回出力し、それぞれの処理時間を比較しています。
結果
Alacrittyというターミナルで実行
seconds | |
---|---|
printf | 0.100653 |
fprintf | 0.0965482 |
write | 0.0775186 |
std::cout | 0.114578 |
std::wcout | 0.134293 |
syscall | 0.108151 |
1位 write
2位 fprintf
3位 printf
4位 syscall
5位 std::cout
6位 std::wcout
!注意!
ターミナルや実行環境、出力するものによって前後します。
参考程度にしてください。最適化設定などは特にしていません。
恐らくした場合、相当変わると思います。
コードも参考程度にしてください。初心者のコードです。
使用しているターミナルについて
Bad Apple !!や今回の検証で使ったものは「Alacritty」というRustで開発されているものを使っています。これは物凄く速いです。
Bad Apple!!
これにはwrite関数を使用しています。〜60FPSは1フレームあたり大体200万文字が限界で、120fpsに設定すると100万文字が限界でした。
これは実行しているパソコンのスペック不足であるかなと思っています。(メモリ容量不足)
現状、1フレームあたり数百万文字を、7000フレームぐらいメモリ上に全て保存しているので30GBは最低でも必要です。
結論
出力するものによりますが、速いものがいる場合、write関数
やfprintf関数
が良いと思います。
他にも出力系はありますので、この記事は参考程度にしてください。
なにか間違っているところ、誤字脱字があればkazuma11121125までよろしくおねがいします。