Rust・Python・Java・TSなど10言語をAI実装で実測比較!実行時間とメモリ差
作成日:

Rust・Python・Java・TSなど10言語をAI実装で実測比較!実行時間とメモリ差


🔧 論旨

3 年ほど前までは、日々の暮らしやパソコン作業の中で「ここが不便だな」と感じても、自分でプログラムを書けなければ我慢するか、高いお金を払って誰かに作ってもらうしかありませんでした。しかし今は、手元の不便を解消する道具なら AI に作ってもらう のが一番早くて確実な時代です。

これまで Python や Go が広く使われ、多くの企業が判で押したように Java を選び続けてきたのは、それらの性能が高かったからではありません。人間にとって読み書きしやすく、担当者が変わっても穴埋めしやすい「人間の都合」を優先してきた結果です。一方、Rust(ラスト) は圧倒的な性能と安全性を持ちながら、人間には難しすぎて敬遠されてきました。しかし AI は Rust が大得意です。

結論からお伝えすると、全 10 大言語(Rust・C++・C・Go・Java・Swift・JavaScript・TypeScript・Ruby・Python)にまったく同じ 3 種目の実務処理を実装して実測した結果、Rust は実行時間 108.8 ms・最大メモリ 12.3 MB で C 言語や C++ すら抜いて総合 1 位(Python 比 155 倍速、Java 比メモリ 10 分の 1、TypeScript の型検査よりコンパイルも 3.1 倍高速)を記録しました。本記事では、すべての計測データと全 10 言語のソースコードを公開します。

🔧 合わせて読みたい深まる関連情報
🔧 対象となる方
  • 日常のちょっとした不便を 「外注せず AI に専用ツールを作らせて解決したい」 と考えている方

  • Python・TypeScript・Go・Java・C++・Rust のどれで AI にコードを書かせるのが一番快適なのか、実測値で白黒つけたい方

  • 10 言語それぞれの コンパイル時間・実行速度・メモリ消費量・ファイル容量 を、自分の手元でも同じソースコードで確かめてみたい方

人間向けの言語選びがAI時代に逆転した理由

世の中で流行してきたプログラミング言語の歴史を振り返ると、ひとつの明確な法則が見えてきます。それは、コンピュータにとって気持ちよく動く言語ではなく、「人間にとって書きやすく、読みやすい言語」 ばかりが選ばれてきたという事実です。

たとえば AI 分野や業務自動化で定番の Python や、Web 開発で愛されてきた Ruby、クラウド開発で普及した Go は、人間が短い行数で直感的に書けるように設計されています。また日本の大企業やシステム開発会社が何十年も Java ばかり採用してきたのも、新卒や他社から集めた技術者でも同じ型にはめて人月単位で交代させやすいからにほかなりません。

従来の人間主導(Python / Java / Go 等)
  • 最優先される基準 :人間の読みやすさ・学習のやさしさ・人員交代のしやすさ

  • 裏側の代償 :実行時の型確認やゴミ回収(GC)のために CPU とメモリを大量消費

  • 配布の壁 :動かすために数百 MB の実行環境(JDK や Node、Python 等)が必要

現代のAI駆動(AI × Rust 単体バイナリ)
  • 最優先される基準 :機械としての最高速度・最小メモリ・メモリ破壊バグの完全排除

  • 逆転の理由 :人間が苦しんだ厳格なコンパイラが「AI のミスを弾く最強の検問官」になる

  • 配布の快適さ :わずか数百 KB の実行ファイル 1 個で、追加環境なしに一瞬で動く

質問するおじさん

たしかに昔は、自分が書けんかったら誰かにお金を払って頼むしかなかったし、自分で触るにしても Python くらいしか手が出んかったんよね。Rust は本を開いた瞬間に難しすぎてそっと閉じた記憶があるわ。

解説ナビゲーター

人間がゼロから書こうとすると、「所有権(Ownership)」や「借用規則(Borrow Checker)」というメモリ管理ルールでコンパイラに何度も怒られますからね。でも AI に書かせると、その厳しいエラーメッセージ自体が AI への的確な修正指示になるため、むしろ一発で安全な高速コードが仕上がります!

「人間が読むための優しさ」と引き換えに、どれだけ処理速度やメモリが犠牲になっているのか。そして AI に Rust で書かせると実際どれほど速くて軽いのか を確かめるため、手元の Mac で全 10 大言語のガチンコ計測を行いました。

実験条件と全10言語が挑む3種目ベンチマーク

公平な比較を行うため、裏で他の重たい作業が動いていないタイミングで、SSD の読み書き遅延すら排除できる RAM ディスク(メモリ上の仮想ドライブ) に作業領域を作って一括計測しました。

計測母艦の環境とIntel版Macの注意点

今回のテストに使用したマシンと、全 10 言語のコンパイラ・実行環境のバージョンは以下の通りです。すべての言語で外部パッケージや拡張ライブラリは一切使わず、各言語の標準機能のみ で統一しています。

項目 / 言語バージョン・実行方式備考
計測マシンiMac (2020) Core i5-10600 (6C/12T) / 64GB RAM16GB RAMDisk 上で全計測を実施
Rustrustc 1.98.1 (-C opt-level=3)単一ファイル直接最適化ビルド
C / C++Apple clang / clang++ 17.0.0 (-O3 / C++20)Command Line Tools 実体直接呼び出し
SwiftApple Swift 6.1.2 (swiftc -O)モジュールキャッシュ事前生成後に計測
Gogo 1.27.3 (darwin/amd64)Google 公式ビルド済みバイナリ
JavaOpenJDK 26.0.1 (javac / HotSpot 64-Bit VM)標準クラスファイル生成・実行
JavaScriptNode.js v24.20.0 (V8 エンジン)ES Modules 形式
TypeScriptTypeScript 5.9.3 (tsc --strict) / Node v24 / Deno 2.9.6型検査変換・Node 直接実行・Deno の 3 方式を計測
Python / RubyPython 3.14.7 / Ruby 2.6.10標準インタプリタ実行
🔧 💡 検証中に判明した 2026 年秋以降の Intel Mac 環境構築の注意点
  • Homebrew のボトル提供終了によるソースビルドの罠 : 今回 Go と Java を準備する際、Homebrew が 2026 年 9 月をもって Intel Mac(x86_64)向けのビルド済みパッケージ(Bottle)提供を終了していたため、通常の brew install を叩くと依存ツール 15 個のゼロからのソースビルドと既存ライブラリ 18 個の更新が走りそうになりました。

  • 公式ビルド済みバイナリの直接配置が安全・高速 : Intel Mac を現役で活用している方は、安易に brew install で時間を溶かす前に brew info でソースビルド対象になっていないかを確認し、Go 公式アーカイブ(darwin-amd64.tar.gz)のような配布元公式のビルド済みバイナリを /usr/local/opt/ 等へ直接展開するのが既存環境を汚さない鉄則です。

ズルが効かない3種目トライアスロンの設計

プログラミング言語の比較でよくある失敗が、「1 から 10 億 まで足すだけ」のような単純すぎるループで測ってしまうことです。これだと Rust や C/C++ の最適化エンジン(LLVM)が賢すぎて、コンパイル中に計算結果を見抜いてループ自体を消滅させ、実行時間 0.00 ms を出してしまう(定数畳み込み) という反則が起きます。

そこで今回は、コンパイラのカンニングが一切効かず、しかも日常のツール開発でよく使う 「計算・メモリ整列・ファイル文字列集計」の 3 種目通し計測 を設計しました。

3,000万回 ビット演算&条件分岐

実行時引数シードから Xorshift 疑似乱数を回し、偶数・奇数で分岐計算。CPU 命令最適化と分岐処理の純粋な地力を測ります。

100万件 レコード生成&標準ソート

ID・分類・金額を持つ 100 万件のデータをメモリ上に確保し、金額順(同額なら ID 順)に整列。メモリ消費とゴミ回収(GC)の重さを炙り出します。

20万行 ログ読込・抽出・連想配列集計

13.14 MB(20 万行)のログファイルから ERROR と WARN 行だけを抜き出し、サービス別の応答時間を連想配列(HashMap 等)で合算します。

さらに JavaScript の 53 ビット安全整数上限(9,007,199,254,740,991)や Java の符号付き 32 ビット整数でも丸め誤差や桁あふれ不一致が起きないよう数式を厳密に揃え、全 10 言語すべてで最終チェックサム(752268290-64250004851844-36129227387)が 1 桁の狂いもなく完全一致すること を確認した上で、複数回実行の中央値と macOS 標準 /usr/bin/time -l による最大メモリ消費量(Peak RSS)を記録しました。

全10言語のコンパイル・実行速度・メモリ比較表

それでは、全 10 大言語(TypeScript は 3 つの実行モードすべてを併記)の実測結果をご覧ください。総合実行時間が速い順に並べています。

順位言語総合実行時間(起動込)① 計算 (ms)② 整列 (ms)③ 集計 (ms)最大メモリコンパイル / 型検査生成ファイル容量(strip後)実効行数
1Rust108.8 ms (115.2 ms)51.439.917.712.3 MB532.0 ms500 KB (369 KB)92 行
2C++182.8 ms (192.8 ms)50.573.359.912.2 MB1,425.2 ms30.6 KB (28.1 KB)95 行
3C222.2 ms (229.3 ms)50.5157.514.012.1 MB100.2 ms9.2 KB (9.1 KB)150 行
4Go279.4 ms (289.0 ms)119.4140.219.626.7 MB75.5 ms2,514 KB (1,631 KB)125 行
5Java678.8 ms (739.1 ms)117.1461.196.6128.5 MB487.4 ms4.8 KB (要JDK 381MB)99 行
6Swift803.5 ms (812.2 ms)50.496.5656.918.4 MB1,801.7 ms48.2 KB (40.0 KB)91 行
7JavaScript (Node v24)849.3 ms (926.9 ms)127.2682.042.9185.5 MB0 ms2.7 KB (要Node 119MB)86 行
8TypeScript (Node v24直接)852.3 ms (961.0 ms)129.9683.538.9191.3 MB0 ms (型検査なし)3.1 KB (要Node 119MB)93 行
(参考)TypeScript (tsc → Node)942.1 ms (1,010.5 ms)144.2749.547.5173.9 MB1,669.4 ms3.3 KB (要Node 119MB)93 行
(参考)TypeScript (Deno 2.9)825.9 ms (1,128.1 ms)119.4659.044.6169.3 MB起動時内包3.1 KB (要Deno)93 行
9Ruby9,770.8 ms (9,913.5 ms)4,689.94,814.3264.1188.9 MB0 ms1.8 KB (要ランタイム)70 行
10Python16,862.6 ms (17,182.5 ms)14,714.92,024.5116.7235.7 MB0 ms2.3 KB (要ランタイム)67 行

数字を並べた瞬間、各言語の「設計思想」と「隠れたコスト」が驚くほど残酷に浮かび上がりました。種目ごとの決定的なポイントを解剖していきます。

なぜRustがCやC++を抜いて総合1位なのか

「実行速度なら C 言語や C++ が最速のはずでは?」と思った方も多いはずです。実際、①計算ループ を見ると、Swift(50.4 ms)・C(50.5 ms)・C++(50.5 ms)・Rust(51.4 ms)の 4 言語は 1 ミリ秒差以内で完全に横並びでした。これは 4 言語とも裏側で同じ最適化エンジン(LLVM)を使っており、ループ内の if 分岐を CPU の条件付き移動命令へ完璧に変換できたからです。

では、なぜ総合タイムで Rust(108.8 ms)が C++(182.8 ms)に 1.7 倍、C 言語(222.2 ms)に 2.0 倍もの差をつけて圧勝した のでしょうか。

🔧 🔍 標準機能だけで戦ったときに Rust が C / C++ を上回る 3 つの理由
  1. C 言語の標準 qsort が抱える関数ポインタの壁(種目②) : C 言語の標準ソート関数 qsort は、汎用性を保つために void* 関数ポインタ経由で比較関数を何千万回も呼び出す古い設計になっており、コンパイラのインライン展開(関数の埋め込み最適化)が効きません(157.5 ms)。一方、Rust の標準 sort_unstable_by は最新鋭のソートアルゴリズムが型専用に完全インライン展開されるため、約 4 倍速い 39.9 ms で終わりました。

  2. C++ の標準 std::unordered_map の設計の古さ(種目③) : C++ の標準連想配列は規格上の制約からメモリ効率の悪いチェイン法(リンクリスト方式)を採用しており、文字列スライスによるゼロコピー検索も標準では扱いづらいため、ログ集計で 59.9 ms かかりました。Rust の標準 HashMap は高速な SwissTable 方式を採用しているため 17.7 ms と 3.4 倍高速です。

  3. 生ポインタの自前実装が不要でメモリ破壊リスクがゼロ : C 言語には標準で連想配列すら存在しないため、今回のテストでもハッシュテーブルを生ポインタと malloc / free で自前実装する必要があり、コードが最長の 150 行に膨らみました。もし AI が書いた C/C++ コードに 1 箇所でも解放漏れや境界ミスが潜んでいてもコンパイラは見逃しますが、Rust なら危険なコードはコンパイル時点で 100% 拒否されます。

さらに注目すべきは コンパイル時間 です。「Rust はコンパイルが遅い」と言われがちですが、余計な外部パッケージを入れず標準機能だけで AI に書かせれば、わずか 532.0 ms(0.53 秒) でビルドが完了し、膨大な標準テンプレートヘッダを読み込む C++(1,425.2 ms)より 2.7 倍も速く 焼き上がりました。

大企業のJavaとモダンなGoの隠れたコスト

次に、業務システムやクラウド開発でよく使われる Java(5 位:678.8 ms) と Go(4 位:279.4 ms) を見てみましょう。

まず Java で目を疑うのが 最大メモリ消費量 128.5 MB(Rust の 10.4 倍) です。今回生成した 100 万件のレコードは、純粋なデータ量なら 12 バイト × 100 万件 = 約 11.4 MB しかありません。Rust(12.3 MB)や C(12.1 MB)はデータの器そのものしかメモリを使わないのに対し、Java は 100 万個のオブジェクト 1 つひとつに管理用の飾り(ヘッダ)と参照ポインタをぶら下げるため、メモリが 10 倍以上に膨れ上がり、ポインタ追跡のキャッシュミスでソート時間も Rust の 11.6 倍(461.1 ms) かかっています。しかもわずか 4.8 KB のクラスファイルを動かすために、裏側で 381 MB もの巨大な OpenJDK 環境 が必要でした。

質問するおばさん

たった 0.1 秒・メモリ 12MB で終わる仕事に、381MB の土台を入れて毎回 128MB もメモリを食わせとるん!? 大企業のシステムが何十億円かけてもモッサリしとる理由が数字で見えてしもうたわ……。

一方の Go は、コンパイル時間が 75.5 ms(0.075 秒) と全言語でダントツの 1 位でした。しかし Go コンパイラは「ビルドの速さ」を最優先して高度な機械語最適化を割り切っているため、①計算ループ(119.4 ms)では Java(117.1 ms)や JavaScript(127.2 ms)と同等の速度 に留まり、Rust や C の半分以下のスピードしか出ません。またゴミ回収(GC)機構を実行ファイル内に丸ごと抱え込むため、最小構成でもファイル容量が 2,514 KB(約 2.5 MB) に太るという明確なトレードオフがあります。

TypeScriptの型が実行を速くしない理由

現代の個人開発や Web 業界で最も人気がある TypeScript を加えたことで、非常に興味深い逆転現象も確認できました。

結論から言うと、TypeScript でデータ構造や変数にどれだけ厳格な「型(interface や : number)」を書いても、実行速度は素の JavaScript とまったく同じ(約 852〜942 ms:Rust の約 8〜9 倍遅い)であり、メモリ消費量も約 174〜191 MB(Rust の約 15 倍)から 1 ミリも減りません。

🔧 📌 なぜ TypeScript の『型』は速くならず、Rust の『型』は爆速になるのか
  • TypeScript の型 =「人間とエディタが見るための付箋」 : 実行する瞬間にはすべての型記述が綺麗に剥がされて捨てられる(Type Erasure)ため、裏側の V8 エンジンは結局「何が入っているか分からない動的な JavaScript オブジェクト」として大量のメモリを確保します。Node.js v24 の直接実行モードも、型チェックを省略して型文字を空白に置き換えているだけなので、剥がす手間の分だけ素の JS(起動込 926.9 ms)より実時間(961.0 ms)がわずかに遅くなります。

  • しかも tsc の型検査(1,669.4 ms)は Rust の最適化コンパイル(532.0 ms)より 3.1 倍遅い : 標準の TypeScript コンパイラ(tsc --strict)自身が JavaScript 上で動いているため、たった 93 行の型検査だけで 1.67 秒も待たされます。

  • Rust の型 =「CPU とメモリの物理設計図」 : u32 と指定すればメモリ上に隙間なく 4 バイト単位で連続配置し、実行時の型確認もゴミ回収もゼロにするため、同じ「型を書く手間(しかも書くのは AI)」なのに実行性能が 1 桁変わります。

もちろん、当サイト(Astro)のような Web 画面の構築には TypeScript が最高の相棒ですが、「裏側のデータ処理や手元の自動化ツール」まで惰性で TypeScript / Node.js で書く理由は、AI 時代にはもうありません。

PythonとRuby・Swiftで見えた得意不得意

残る Python・Ruby・Swift の 3 言語からも、それぞれの素顔がくっきり見えました。

まず Python(10 位:16,862.6 ms) と Ruby(9 位:9,770.8 ms) は、実効行数がそれぞれ 67 行・70 行 と最も短く、人間が目で読む分には抜群にスッキリしています。しかし 1 ループごとに「この変数は整数か? 桁あふれしないか?」を毎回確認する構造のため、①計算ループ単体では Rust が 0.05 秒 で終わる処理に Ruby は 4.7 秒(91 倍)、Python に至っては 14.7 秒(286 倍) もかかり、メモリも Python がワースト 1 位の 235.7 MB(Rust の 19.2 倍) を消費しました。普段 AI 分野で Python が速く見えるのは、重たい計算部分だけを裏側の C++ や Rust 製ライブラリに丸投げしているからにすぎません。

また意外だったのが Swift(6 位:803.5 ms) です。①計算ループでは 50.4 ms で全言語トップタイ、② 100 万件ソートでも 96.5 ms と好記録を出したのに、③ログ集計だけで 656.9 ms と急減速しました。これは Swift の標準文字列(String)が、絵文字や結合文字などの複雑な文字境界(Extended Grapheme Clusters)を人間向けに 1 文字ずつ厳密判定する安全設計になっているためです。Mac 用の画面付きアプリ(SwiftUI)を作るには最高ですが、純粋な大量テキスト・ログの高速処理ではバイトスライスを自在に扱える Rust に軍配が上がります。

検証に使用した全10言語のソースコード全文公開

今回のベンチマークで使用した全 10 言語のソースコード全文を公開します。主役である Rust 版 をまず掲載し、続く 9 言語(C++・C・Go・Java・Swift・JavaScript・TypeScript・Ruby・Python)と共通テストログ生成コードはクリックで開閉できる折りたたみ枠に 1 行の省略もなく収録しています。手元の PC で再現実験をしたい方はぜひ試してみてください。

// Rust版 (bench.rs): 標準ライブラリのみ・外部クレートゼロ (実効92行)
// ビルド例: rustc -C opt-level=3 bench.rs -o bin_rust
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::time::Instant;

#[derive(Clone, Copy)]
struct Record {
    id: u32,
    category: u32,
    amount: u32,
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let seed: u32 = args.get(1).and_then(|s| s.parse().ok()).unwrap_or(123456789);
    let log_path = args.get(2).map(|s| s.as_str()).unwrap_or("access.log");

    let t0 = Instant::now();

    // Stage 1: 3,000万回 ビット演算&条件分岐ループ
    let mut x: u32 = seed;
    let mut acc: u32 = 0;
    for _ in 0..30_000_000u32 {
        x ^= x << 13;
        x ^= x >> 17;
        x ^= x << 5;
        if (x & 1) != 0 {
            acc = acc.wrapping_add(x ^ 0x9E37_79B9);
        } else {
            acc ^= x >> 2;
        }
    }
    let chk1 = acc;
    let t1 = Instant::now();

    // Stage 2: 100万件 構造体メモリ生成&ソート
    let m: usize = 1_000_000;
    let mut records = Vec::with_capacity(m);
    let mut s: u32 = seed ^ 0xA5A5_A5A5;
    for i in 0..(m as u32) {
        s ^= s << 13;
        s ^= s >> 17;
        s ^= s << 5;
        records.push(Record {
            id: i,
            category: s & 63,
            amount: s % 1_000_000,
        });
    }
    records.sort_unstable_by(|a, b| a.amount.cmp(&b.amount).then_with(|| a.id.cmp(&b.id)));
    let mut chk2: u64 = 0;
    for (i, r) in records.iter().enumerate() {
        chk2 += (r.amount as u64) * (((i & 255) + 1) as u64) + (r.category as u64);
    }
    let t2 = Instant::now();

    // Stage 3: 20万行 ログファイル読込・ゼロコピースライス解析・HashMap集計
    let file = File::open(log_path).expect("failed to open log");
    let mut reader = BufReader::with_capacity(64 * 1024, file);
    let mut line = String::with_capacity(128);
    let mut map: HashMap<String, u64> = HashMap::new();

    while reader.read_line(&mut line).expect("read error") > 0 {
        let trimmed = line.trim_end_matches(&['\r', '\n'][..]);
        let mut parts = trimmed.split('|');
        let _ts = parts.next();
        if let Some(level) = parts.next() {
            if level == "ERROR" || level == "WARN" {
                if let (Some(service), Some(_path), Some(ms_field)) =
                    (parts.next(), parts.next(), parts.next())
                {
                    if let Some(ms_str) = ms_field.strip_prefix("ms=") {
                        let ms_val: u64 = ms_str.parse().unwrap_or(0);
                        if let Some(v) = map.get_mut(service) {
                            *v += ms_val;
                        } else {
                            map.insert(service.to_string(), ms_val);
                        }
                    }
                }
            }
        }
        line.clear();
    }
    let total_ms: u64 = map.values().sum();
    let svc0_ms: u64 = *map.get("service_0").unwrap_or(&0);
    let chk3: u64 = total_ms * 1000 + (svc0_ms % 1000);
    let t3 = Instant::now();

    let s1 = t1.duration_since(t0).as_secs_f64() * 1000.0;
    let s2 = t2.duration_since(t1).as_secs_f64() * 1000.0;
    let s3 = t3.duration_since(t2).as_secs_f64() * 1000.0;
    let tot = t3.duration_since(t0).as_secs_f64() * 1000.0;

    println!(
        "RESULT|Rust|stage1={:.2}|stage2={:.2}|stage3={:.2}|total={:.2}|chk={}-{}-{}",
        s1, s2, s3, tot, chk1, chk2, chk3
    );
}
📂 2位:C++ 版ソースコード全文(bench.cpp / 実効95行)を開く
// ビルド例: clang++ -std=c++20 -O3 bench.cpp -o bin_cpp
#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>

struct Record {
    uint32_t id;
    uint32_t category;
    uint32_t amount;
};

int main(int argc, char **argv) {
    uint32_t seed = (argc > 1) ? static_cast<uint32_t>(std::strtoul(argv[1], nullptr, 10)) : 123456789u;
    const char *log_path = (argc > 2) ? argv[2] : "access.log";

    using clock = std::chrono::steady_clock;
    auto ms_diff = [](clock::time_point a, clock::time_point b) {
        return std::chrono::duration<double, std::milli>(b - a).count();
    };

    auto t0 = clock::now();

    // Stage 1
    uint32_t x = seed;
    uint32_t acc = 0;
    for (uint32_t i = 0; i < 30000000u; ++i) {
        x ^= x << 13;
        x ^= x >> 17;
        x ^= x << 5;
        if (x & 1u) {
            acc += (x ^ 0x9E3779B9u);
        } else {
            acc ^= (x >> 2);
        }
    }
    uint32_t chk1 = acc;
    auto t1 = clock::now();

    // Stage 2
    const size_t m = 1000000;
    std::vector<Record> records;
    records.reserve(m);
    uint32_t s = seed ^ 0xA5A5A5A5u;
    for (uint32_t i = 0; i < static_cast<uint32_t>(m); ++i) {
        s ^= s << 13;
        s ^= s >> 17;
        s ^= s << 5;
        records.push_back(Record{i, s & 63u, s % 1000000u});
    }
    std::sort(records.begin(), records.end(), [](const Record &a, const Record &b) {
        if (a.amount != b.amount) return a.amount < b.amount;
        return a.id < b.id;
    });
    uint64_t chk2 = 0;
    for (size_t i = 0; i < m; ++i) {
        chk2 += static_cast<uint64_t>(records[i].amount) * static_cast<uint64_t>((i & 255u) + 1u) +
                static_cast<uint64_t>(records[i].category);
    }
    auto t2 = clock::now();

    // Stage 3
    std::ifstream ifs(log_path);
    std::unordered_map<std::string, uint64_t> map;
    map.reserve(128);
    std::string line;
    while (std::getline(ifs, line)) {
        if (!line.empty() && line.back() == '\r') line.pop_back();
        std::string_view sv(line);
        size_t p1 = sv.find('|');
        if (p1 == std::string_view::npos) continue;
        size_t p2 = sv.find('|', p1 + 1);
        if (p2 == std::string_view::npos) continue;
        std::string_view lvl = sv.substr(p1 + 1, p2 - p1 - 1);
        if (lvl != "ERROR" && lvl != "WARN") continue;
        size_t p3 = sv.find('|', p2 + 1);
        if (p3 == std::string_view::npos) continue;
        std::string_view svc = sv.substr(p2 + 1, p3 - p2 - 1);
        size_t p4 = sv.find('|', p3 + 1);
        if (p4 == std::string_view::npos) continue;
        std::string_view ms_field = sv.substr(p4 + 1);
        if (ms_field.substr(0, 3) == "ms=") {
            uint64_t ms_val = std::strtoull(ms_field.data() + 3, nullptr, 10);
            map[std::string(svc)] += ms_val;
        }
    }
    uint64_t total_ms = 0;
    for (const auto &kv : map) total_ms += kv.second;
    uint64_t svc0_ms = map["service_0"];
    uint64_t chk3 = total_ms * 1000ULL + (svc0_ms % 1000ULL);
    auto t3 = clock::now();

    std::printf("RESULT|C++|stage1=%.2f|stage2=%.2f|stage3=%.2f|total=%.2f|chk=%u-%llu-%llu\n",
                ms_diff(t0, t1), ms_diff(t1, t2), ms_diff(t2, t3), ms_diff(t0, t3),
                chk1, static_cast<unsigned long long>(chk2), static_cast<unsigned long long>(chk3));
    return 0;
}
📂 3位:C言語 版ソースコード全文(bench.c / 実効150行・ハッシュ表自前実装)を開く
// ビルド例: clang -O3 bench.c -o bin_c
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

typedef struct {
    uint32_t id;
    uint32_t category;
    uint32_t amount;
} Record;

static int cmp_record(const void *pa, const void *pb) {
    const Record *a = (const Record *)pa;
    const Record *b = (const Record *)pb;
    if (a->amount < b->amount) return -1;
    if (a->amount > b->amount) return 1;
    if (a->id < b->id) return -1;
    if (a->id > b->id) return 1;
    return 0;
}

// C言語には標準HashMapが存在しないため、オープンアドレス法の最小ハッシュ表を自前実装
#define HT_CAP 256
typedef struct {
    char *key;
    uint64_t val;
    int used;
} Entry;

static uint32_t hash_str(const char *s, size_t len) {
    uint32_t h = 2166136261u;
    for (size_t i = 0; i < len; i++) {
        h ^= (uint8_t)s[i];
        h *= 16777619u;
    }
    return h;
}

static void ht_add(Entry *ht, const char *key, size_t len, uint64_t delta) {
    uint32_t idx = hash_str(key, len) & (HT_CAP - 1);
    while (ht[idx].used) {
        if (strlen(ht[idx].key) == len && memcmp(ht[idx].key, key, len) == 0) {
            ht[idx].val += delta;
            return;
        }
        idx = (idx + 1) & (HT_CAP - 1);
    }
    char *copy = (char *)malloc(len + 1);
    memcpy(copy, key, len);
    copy[len] = '\0';
    ht[idx].key = copy;
    ht[idx].val = delta;
    ht[idx].used = 1;
}

static uint64_t ht_get(Entry *ht, const char *key) {
    size_t len = strlen(key);
    uint32_t idx = hash_str(key, len) & (HT_CAP - 1);
    while (ht[idx].used) {
        if (strcmp(ht[idx].key, key) == 0) return ht[idx].val;
        idx = (idx + 1) & (HT_CAP - 1);
    }
    return 0;
}

static double now_ms(void) {
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return (double)ts.tv_sec * 1000.0 + (double)ts.tv_nsec / 1000000.0;
}

int main(int argc, char **argv) {
    uint32_t seed = (argc > 1) ? (uint32_t)strtoul(argv[1], NULL, 10) : 123456789u;
    const char *log_path = (argc > 2) ? argv[2] : "access.log";

    double t0 = now_ms();

    // Stage 1
    uint32_t x = seed;
    uint32_t acc = 0;
    for (uint32_t i = 0; i < 30000000u; i++) {
        x ^= x << 13;
        x ^= x >> 17;
        x ^= x << 5;
        if (x & 1u) {
            acc += (x ^ 0x9E3779B9u);
        } else {
            acc ^= (x >> 2);
        }
    }
    uint32_t chk1 = acc;
    double t1 = now_ms();

    // Stage 2
    size_t m = 1000000;
    Record *records = (Record *)malloc(m * sizeof(Record));
    uint32_t s = seed ^ 0xA5A5A5A5u;
    for (uint32_t i = 0; i < (uint32_t)m; i++) {
        s ^= s << 13;
        s ^= s >> 17;
        s ^= s << 5;
        records[i].id = i;
        records[i].category = s & 63u;
        records[i].amount = s % 1000000u;
    }
    qsort(records, m, sizeof(Record), cmp_record);
    uint64_t chk2 = 0;
    for (size_t i = 0; i < m; i++) {
        chk2 += (uint64_t)records[i].amount * (uint64_t)((i & 255u) + 1u) + (uint64_t)records[i].category;
    }
    free(records);
    double t2 = now_ms();

    // Stage 3
    FILE *fp = fopen(log_path, "r");
    if (!fp) return 1;
    char io_buf[65536];
    setvbuf(fp, io_buf, _IOFBF, sizeof(io_buf));

    Entry ht[HT_CAP];
    memset(ht, 0, sizeof(ht));

    char line[256];
    while (fgets(line, sizeof(line), fp)) {
        char *p1 = strchr(line, '|');
        if (!p1) continue;
        char *lvl = p1 + 1;
        char *p2 = strchr(lvl, '|');
        if (!p2) continue;
        size_t lvl_len = (size_t)(p2 - lvl);
        if (!((lvl_len == 5 && memcmp(lvl, "ERROR", 5) == 0) ||
              (lvl_len == 4 && memcmp(lvl, "WARN", 4) == 0))) {
            continue;
        }
        char *svc = p2 + 1;
        char *p3 = strchr(svc, '|');
        if (!p3) continue;
        size_t svc_len = (size_t)(p3 - svc);
        char *p4 = strchr(p3 + 1, '|');
        if (!p4) continue;
        char *ms_field = p4 + 1;
        if (strncmp(ms_field, "ms=", 3) == 0) {
            uint64_t ms_val = strtoull(ms_field + 3, NULL, 10);
            ht_add(ht, svc, svc_len, ms_val);
        }
    }
    fclose(fp);

    uint64_t total_ms = 0;
    for (int i = 0; i < HT_CAP; i++) {
        if (ht[i].used) total_ms += ht[i].val;
    }
    uint64_t svc0_ms = ht_get(ht, "service_0");
    uint64_t chk3 = total_ms * 1000ULL + (svc0_ms % 1000ULL);
    for (int i = 0; i < HT_CAP; i++) {
        if (ht[i].used) free(ht[i].key);
    }
    double t3 = now_ms();

    printf("RESULT|C|stage1=%.2f|stage2=%.2f|stage3=%.2f|total=%.2f|chk=%u-%llu-%llu\n",
           t1 - t0, t2 - t1, t3 - t2, t3 - t0,
           chk1, (unsigned long long)chk2, (unsigned long long)chk3);
    return 0;
}
📂 4位:Go 版ソースコード全文(bench.go / 実効125行)を開く
// ビルド例: go build -o bin_go bench.go
package main

import (
	"bufio"
	"cmp"
	"fmt"
	"os"
	"slices"
	"strconv"
	"strings"
	"time"
)

type Record struct {
	id       uint32
	category uint32
	amount   uint32
}

func main() {
	seed := uint32(123456789)
	logPath := "access.log"
	if len(os.Args) > 1 {
		if v, err := strconv.ParseUint(os.Args[1], 10, 32); err == nil {
			seed = uint32(v)
		}
	}
	if len(os.Args) > 2 {
		logPath = os.Args[2]
	}

	t0 := time.Now()

	// Stage 1
	x := seed
	var acc uint32 = 0
	for i := uint32(0); i < 30000000; i++ {
		x ^= x << 13
		x ^= x >> 17
		x ^= x << 5
		if (x & 1) != 0 {
			acc += x ^ 0x9E3779B9
		} else {
			acc ^= x >> 2
		}
	}
	chk1 := acc
	t1 := time.Now()

	// Stage 2
	const m = 1000000
	records := make([]Record, m)
	s := seed ^ 0xA5A5A5A5
	for i := uint32(0); i < m; i++ {
		s ^= s << 13
		s ^= s >> 17
		s ^= s << 5
		records[i] = Record{
			id:       i,
			category: s & 63,
			amount:   s % 1000000,
		}
	}
	slices.SortFunc(records, func(a, b Record) int {
		if c := cmp.Compare(a.amount, b.amount); c != 0 {
			return c
		}
		return cmp.Compare(a.id, b.id)
	})
	var chk2 uint64 = 0
	for i, r := range records {
		chk2 += uint64(r.amount)*uint64((i&255)+1) + uint64(r.category)
	}
	t2 := time.Now()

	// Stage 3
	f, err := os.Open(logPath)
	if err != nil {
		panic(err)
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	buf := make([]byte, 64*1024)
	scanner.Buffer(buf, 1024*1024)
	counts := make(map[string]uint64, 128)

	for scanner.Scan() {
		line := scanner.Text()
		p1 := strings.IndexByte(line, '|')
		if p1 < 0 {
			continue
		}
		rest1 := line[p1+1:]
		p2 := strings.IndexByte(rest1, '|')
		if p2 < 0 {
			continue
		}
		lvl := rest1[:p2]
		if lvl != "ERROR" && lvl != "WARN" {
			continue
		}
		rest2 := rest1[p2+1:]
		p3 := strings.IndexByte(rest2, '|')
		if p3 < 0 {
			continue
		}
		svc := rest2[:p3]
		rest3 := rest2[p3+1:]
		p4 := strings.IndexByte(rest3, '|')
		if p4 < 0 {
			continue
		}
		msField := rest3[p4+1:]
		if strings.HasPrefix(msField, "ms=") {
			val, _ := strconv.ParseUint(msField[3:], 10, 64)
			counts[svc] += val
		}
	}

	var totalMs uint64 = 0
	for _, v := range counts {
		totalMs += v
	}
	svc0Ms := counts["service_0"]
	chk3 := totalMs*1000 + (svc0Ms % 1000)
	t3 := time.Now()

	s1 := float64(t1.Sub(t0).Nanoseconds()) / 1e6
	s2 := float64(t2.Sub(t1).Nanoseconds()) / 1e6
	s3 := float64(t3.Sub(t2).Nanoseconds()) / 1e6
	tot := float64(t3.Sub(t0).Nanoseconds()) / 1e6

	fmt.Printf("RESULT|Go|stage1=%.2f|stage2=%.2f|stage3=%.2f|total=%.2f|chk=%d-%d-%d\n",
		s1, s2, s3, tot, chk1, chk2, chk3)
}
📂 5位:Java 版ソースコード全文(Bench.java / 実効99行)を開く
// ビルド&実行例: javac Bench.java && java Bench 123456789 access.log
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

public class Bench {
    static final class Record implements Comparable<Record> {
        final int id;
        final int category;
        final int amount;

        Record(int id, int category, int amount) {
            this.id = id;
            this.category = category;
            this.amount = amount;
        }

        @Override
        public int compareTo(Record o) {
            int c = Integer.compareUnsigned(this.amount, o.amount);
            if (c != 0) return c;
            return Integer.compareUnsigned(this.id, o.id);
        }
    }

    public static void main(String[] args) throws Exception {
        int seed = (args.length > 0) ? Integer.parseUnsignedInt(args[0]) : 123456789;
        String logPath = (args.length > 1) ? args[1] : "access.log";

        long t0 = System.nanoTime();

        // Stage 1
        int x = seed;
        int acc = 0;
        for (int i = 0; i < 30_000_000; i++) {
            x ^= (x << 13);
            x ^= (x >>> 17);
            x ^= (x << 5);
            if ((x & 1) != 0) {
                acc += (x ^ 0x9E3779B9);
            } else {
                acc ^= (x >>> 2);
            }
        }
        long chk1 = Integer.toUnsignedLong(acc);
        long t1 = System.nanoTime();

        // Stage 2
        int m = 1_000_000;
        Record[] records = new Record[m];
        int s = seed ^ 0xA5A5A5A5;
        for (int i = 0; i < m; i++) {
            s ^= (s << 13);
            s ^= (s >>> 17);
            s ^= (s << 5);
            int cat = s & 63;
            int amt = (int) (Integer.toUnsignedLong(s) % 1_000_000L);
            records[i] = new Record(i, cat, amt);
        }
        Arrays.sort(records);
        long chk2 = 0L;
        for (int i = 0; i < m; i++) {
            Record r = records[i];
            chk2 += Integer.toUnsignedLong(r.amount) * (long) ((i & 255) + 1)
                    + Integer.toUnsignedLong(r.category);
        }
        long t2 = System.nanoTime();

        // Stage 3
        Map<String, Long> map = new HashMap<>(128);
        try (BufferedReader br = new BufferedReader(new FileReader(logPath), 65536)) {
            String line;
            while ((line = br.readLine()) != null) {
                int p1 = line.indexOf('|');
                if (p1 < 0) continue;
                int p2 = line.indexOf('|', p1 + 1);
                if (p2 < 0) continue;
                String lvl = line.substring(p1 + 1, p2);
                if (!lvl.equals("ERROR") && !lvl.equals("WARN")) continue;
                int p3 = line.indexOf('|', p2 + 1);
                if (p3 < 0) continue;
                String svc = line.substring(p2 + 1, p3);
                int p4 = line.indexOf('|', p3 + 1);
                if (p4 < 0) continue;
                if (line.startsWith("ms=", p4 + 1)) {
                    long msVal = Long.parseLong(line.substring(p4 + 4));
                    map.merge(svc, msVal, Long::sum);
                }
            }
        }
        long totalMs = 0L;
        for (long v : map.values()) totalMs += v;
        long svc0Ms = map.getOrDefault("service_0", 0L);
        long chk3 = totalMs * 1000L + (svc0Ms % 1000L);
        long t3 = System.nanoTime();

        double s1 = (t1 - t0) / 1e6;
        double s2 = (t2 - t1) / 1e6;
        double s3 = (t3 - t2) / 1e6;
        double tot = (t3 - t0) / 1e6;

        System.out.printf(Locale.US,
                "RESULT|Java|stage1=%.2f|stage2=%.2f|stage3=%.2f|total=%.2f|chk=%d-%d-%d%n",
                s1, s2, s3, tot, chk1, chk2, chk3);
    }
}
📂 6位:Swift 版ソースコード全文(bench.swift / 実効91行)を開く
// ビルド例: swiftc -O bench.swift -o bin_swift
import Darwin

struct Record {
    var id: UInt32
    var category: UInt32
    var amount: UInt32
}

@inline(__always)
func nowMs() -> Double {
    var ts = timespec()
    clock_gettime(CLOCK_MONOTONIC, &ts)
    return Double(ts.tv_sec) * 1000.0 + Double(ts.tv_nsec) / 1_000_000.0
}

let args = CommandLine.arguments
let seed: UInt32 = (args.count > 1 ? UInt32(args[1]) : nil) ?? 123_456_789
let logPath = args.count > 2 ? args[2] : "access.log"

let t0 = nowMs()

// Stage 1
var x: UInt32 = seed
var acc: UInt32 = 0
for _ in 0..<30_000_000 {
    x ^= x << 13
    x ^= x >> 17
    x ^= x << 5
    if (x & 1) != 0 {
        acc = acc &+ (x ^ 0x9E37_79B9)
    } else {
        acc ^= (x >> 2)
    }
}
let chk1 = acc
let t1 = nowMs()

// Stage 2
let m = 1_000_000
var records = [Record]()
records.reserveCapacity(m)
var s: UInt32 = seed ^ 0xA5A5_A5A5
for i in 0..<UInt32(m) {
    s ^= s << 13
    s ^= s >> 17
    s ^= s << 5
    records.append(Record(id: i, category: s & 63, amount: s % 1_000_000))
}
records.sort { a, b in
    if a.amount != b.amount { return a.amount < b.amount }
    return a.id < b.id
}
var chk2: UInt64 = 0
for (i, r) in records.enumerated() {
    chk2 &+= UInt64(r.amount) &* UInt64((i & 255) + 1) &+ UInt64(r.category)
}
let t2 = nowMs()

// Stage 3
var map = [String: UInt64](minimumCapacity: 128)
if let fp = fopen(logPath, "r") {
    var ioBuf = [CChar](repeating: 0, count: 65536)
    setvbuf(fp, &ioBuf, _IOFBF, ioBuf.count)
    var lineBuf = [CChar](repeating: 0, count: 256)
    while fgets(&lineBuf, Int32(lineBuf.count), fp) != nil {
        var line = String(cString: lineBuf)
        while let last = line.last, last == "\n" || last == "\r" {
            line.removeLast()
        }
        let parts = line.split(separator: "|", omittingEmptySubsequences: false)
        if parts.count >= 5 {
            let lvl = parts[1]
            if lvl == "ERROR" || lvl == "WARN" {
                let svc = String(parts[2])
                let msField = parts[4]
                if msField.hasPrefix("ms=") {
                    if let val = UInt64(msField.dropFirst(3)) {
                        map[svc, default: 0] += val
                    }
                }
            }
        }
    }
    fclose(fp)
}
var totalMs: UInt64 = 0
for v in map.values { totalMs += v }
let svc0Ms = map["service_0"] ?? 0
let chk3 = totalMs * 1000 + (svc0Ms % 1000)
let t3 = nowMs()

func fmt2(_ v: Double) -> String {
    let scaled = Int64((v * 100.0).rounded())
    let intPart = scaled / 100
    let fracPart = abs(scaled % 100)
    return fracPart < 10 ? "\(intPart).0\(fracPart)" : "\(intPart).\(fracPart)"
}

print("RESULT|Swift|stage1=\(fmt2(t1 - t0))|stage2=\(fmt2(t2 - t1))|stage3=\(fmt2(t3 - t2))|total=\(fmt2(t3 - t0))|chk=\(chk1)-\(chk2)-\(chk3)")
📂 7位:JavaScript (Node.js) 版ソースコード全文(bench.mjs / 実効86行)を開く
// 実行例: node bench.mjs 123456789 access.log
import fs from "node:fs";
import { performance } from "node:perf_hooks";

const seed = (process.argv[2] ? Number(process.argv[2]) : 123456789) >>> 0;
const logPath = process.argv[3] || "access.log";

const t0 = performance.now();

// Stage 1
let x = seed;
let acc = 0;
for (let i = 0; i < 30_000_000; i++) {
  x = (x ^ (x << 13)) >>> 0;
  x = (x ^ (x >>> 17)) >>> 0;
  x = (x ^ (x << 5)) >>> 0;
  if ((x & 1) !== 0) {
    acc = (acc + ((x ^ 0x9e3779b9) >>> 0)) >>> 0;
  } else {
    acc = (acc ^ (x >>> 2)) >>> 0;
  }
}
const chk1 = acc >>> 0;
const t1 = performance.now();

// Stage 2
const m = 1_000_000;
const records = new Array(m);
let s = (seed ^ 0xa5a5a5a5) >>> 0;
for (let i = 0; i < m; i++) {
  s = (s ^ (s << 13)) >>> 0;
  s = (s ^ (s >>> 17)) >>> 0;
  s = (s ^ (s << 5)) >>> 0;
  records[i] = {
    id: i,
    category: s & 63,
    amount: s % 1_000_000,
  };
}
records.sort((a, b) => (a.amount !== b.amount ? a.amount - b.amount : a.id - b.id));
let chk2 = 0;
for (let i = 0; i < m; i++) {
  const r = records[i];
  chk2 += r.amount * ((i & 255) + 1) + r.category;
}
const t2 = performance.now();

// Stage 3: 64KBチャンク同期ストリーム読込
const map = new Map();
const fd = fs.openSync(logPath, "r");
const buf = Buffer.allocUnsafe(64 * 1024);
let leftover = "";
let bytesRead = 0;

function processLine(line) {
  if (line.endsWith("\r")) line = line.slice(0, -1);
  const p1 = line.indexOf("|");
  if (p1 < 0) return;
  const p2 = line.indexOf("|", p1 + 1);
  if (p2 < 0) return;
  const lvl = line.slice(p1 + 1, p2);
  if (lvl !== "ERROR" && lvl !== "WARN") return;
  const p3 = line.indexOf("|", p2 + 1);
  if (p3 < 0) return;
  const svc = line.slice(p2 + 1, p3);
  const p4 = line.indexOf("|", p3 + 1);
  if (p4 < 0) return;
  if (line.startsWith("ms=", p4 + 1)) {
    const msVal = Number(line.slice(p4 + 4));
    map.set(svc, (map.get(svc) || 0) + msVal);
  }
}

while ((bytesRead = fs.readSync(fd, buf, 0, buf.length, null)) > 0) {
  const chunk = leftover + buf.toString("utf8", 0, bytesRead);
  let start = 0;
  let nl;
  while ((nl = chunk.indexOf("\n", start)) !== -1) {
    processLine(chunk.slice(start, nl));
    start = nl + 1;
  }
  leftover = chunk.slice(start);
}
if (leftover.length > 0) processLine(leftover);
fs.closeSync(fd);

let totalMs = 0;
for (const v of map.values()) totalMs += v;
const svc0Ms = map.get("service_0") || 0;
const chk3 = totalMs * 1000 + (svc0Ms % 1000);
const t3 = performance.now();

console.log(
  `RESULT|JavaScript|stage1=${(t1 - t0).toFixed(2)}|stage2=${(t2 - t1).toFixed(2)}|stage3=${(t3 - t2).toFixed(2)}|total=${(t3 - t0).toFixed(2)}|chk=${chk1}-${chk2}-${chk3}`
);
📂 8位:TypeScript 版ソースコード全文(bench.ts / 実効93行)を開く
// 直接実行例: node bench.ts 123456789 access.log
// 型検査ビルド例: npx tsc bench.ts --target ES2022 --module NodeNext --strict
import fs from "node:fs";
import { performance } from "node:perf_hooks";

interface RecordItem {
  id: number;
  category: number;
  amount: number;
}

const seed: number = (process.argv[2] ? Number(process.argv[2]) : 123456789) >>> 0;
const logPath: string = process.argv[3] || "access.log";

const t0: number = performance.now();

// Stage 1
let x: number = seed;
let acc: number = 0;
for (let i = 0; i < 30_000_000; i++) {
  x = (x ^ (x << 13)) >>> 0;
  x = (x ^ (x >>> 17)) >>> 0;
  x = (x ^ (x << 5)) >>> 0;
  if ((x & 1) !== 0) {
    acc = (acc + ((x ^ 0x9e3779b9) >>> 0)) >>> 0;
  } else {
    acc = (acc ^ (x >>> 2)) >>> 0;
  }
}
const chk1: number = acc >>> 0;
const t1: number = performance.now();

// Stage 2
const m = 1_000_000;
const records: RecordItem[] = new Array<RecordItem>(m);
let s: number = (seed ^ 0xa5a5a5a5) >>> 0;
for (let i = 0; i < m; i++) {
  s = (s ^ (s << 13)) >>> 0;
  s = (s ^ (s >>> 17)) >>> 0;
  s = (s ^ (s << 5)) >>> 0;
  records[i] = {
    id: i,
    category: s & 63,
    amount: s % 1_000_000,
  };
}
records.sort((a: RecordItem, b: RecordItem): number =>
  a.amount !== b.amount ? a.amount - b.amount : a.id - b.id
);
let chk2 = 0;
for (let i = 0; i < m; i++) {
  const r: RecordItem = records[i];
  chk2 += r.amount * ((i & 255) + 1) + r.category;
}
const t2: number = performance.now();

// Stage 3
const map = new Map<string, number>();
const fd: number = fs.openSync(logPath, "r");
const buf: Buffer = Buffer.allocUnsafe(64 * 1024);
let leftover = "";
let bytesRead = 0;

function processLine(line: string): void {
  if (line.endsWith("\r")) line = line.slice(0, -1);
  const p1: number = line.indexOf("|");
  if (p1 < 0) return;
  const p2: number = line.indexOf("|", p1 + 1);
  if (p2 < 0) return;
  const lvl: string = line.slice(p1 + 1, p2);
  if (lvl !== "ERROR" && lvl !== "WARN") return;
  const p3: number = line.indexOf("|", p2 + 1);
  if (p3 < 0) return;
  const svc: string = line.slice(p2 + 1, p3);
  const p4: number = line.indexOf("|", p3 + 1);
  if (p4 < 0) return;
  if (line.startsWith("ms=", p4 + 1)) {
    const msVal: number = Number(line.slice(p4 + 4));
    map.set(svc, (map.get(svc) || 0) + msVal);
  }
}

while ((bytesRead = fs.readSync(fd, buf, 0, buf.length, null)) > 0) {
  const chunk: string = leftover + buf.toString("utf8", 0, bytesRead);
  let start = 0;
  let nl: number;
  while ((nl = chunk.indexOf("\n", start)) !== -1) {
    processLine(chunk.slice(start, nl));
    start = nl + 1;
  }
  leftover = chunk.slice(start);
}
if (leftover.length > 0) processLine(leftover);
fs.closeSync(fd);

let totalMs = 0;
for (const v of map.values()) totalMs += v;
const svc0Ms: number = map.get("service_0") || 0;
const chk3: number = totalMs * 1000 + (svc0Ms % 1000);
const t3: number = performance.now();

console.log(
  `RESULT|TypeScript|stage1=${(t1 - t0).toFixed(2)}|stage2=${(t2 - t1).toFixed(2)}|stage3=${(t3 - t2).toFixed(2)}|total=${(t3 - t0).toFixed(2)}|chk=${chk1}-${chk2}-${chk3}`
);
📂 9位:Ruby 版ソースコード全文(bench.rb / 実効70行)を開く
# 実行例: ruby bench.rb 123456789 access.log
Record = Struct.new(:id, :category, :amount)

seed = (ARGV[0] ? ARGV[0].to_i : 123_456_789) & 0xFFFFFFFF
log_path = ARGV[1] || "access.log"

t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)

# Stage 1
x = seed
acc = 0
mask = 0xFFFFFFFF
i = 0
while i < 30_000_000
  x = (x ^ (x << 13)) & mask
  x = (x ^ (x >> 17)) & mask
  x = (x ^ (x << 5)) & mask
  if (x & 1) != 0
    acc = (acc + (x ^ 0x9E3779B9)) & mask
  else
    acc = (acc ^ (x >> 2)) & mask
  end
  i += 1
end
chk1 = acc
t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)

# Stage 2
m = 1_000_000
records = Array.new(m)
s = (seed ^ 0xA5A5A5A5) & mask
j = 0
while j < m
  s = (s ^ (s << 13)) & mask
  s = (s ^ (s >> 17)) & mask
  s = (s ^ (s << 5)) & mask
  records[j] = Record.new(j, s & 63, s % 1_000_000)
  j += 1
end
records.sort_by! { |r| [r.amount, r.id] }
chk2 = 0
records.each_with_index do |r, idx|
  chk2 += r.amount * ((idx & 255) + 1) + r.category
end
t2 = Process.clock_gettime(Process::CLOCK_MONOTONIC)

# Stage 3
counts = Hash.new(0)
File.open(log_path, "r") do |f|
  f.each_line do |line|
    line.chomp!
    parts = line.split("|")
    if parts.length >= 5
      lvl = parts[1]
      if lvl == "ERROR" || lvl == "WARN"
        svc = parts[2]
        ms_field = parts[4]
        if ms_field.start_with?("ms=")
          counts[svc] += ms_field[3..-1].to_i
        end
      end
    end
  end
end
total_ms = 0
counts.each_value { |v| total_ms += v }
svc0_ms = counts["service_0"]
chk3 = total_ms * 1000 + (svc0_ms % 1000)
t3 = Process.clock_gettime(Process::CLOCK_MONOTONIC)

s1 = (t1 - t0) * 1000.0
s2 = (t2 - t1) * 1000.0
s3 = (t3 - t2) * 1000.0
tot = (t3 - t0) * 1000.0

puts format("RESULT|Ruby|stage1=%.2f|stage2=%.2f|stage3=%.2f|total=%.2f|chk=%d-%d-%d",
            s1, s2, s3, tot, chk1, chk2, chk3)
📂 10位:Python 版ソースコード全文(bench.py / 実効67行)&共通ログ生成コードを開く
# 実行例: python3 bench.py 123456789 access.log
import sys
import time


class Record:
    __slots__ = ("id", "category", "amount")

    def __init__(self, id_: int, category: int, amount: int):
        self.id = id_
        self.category = category
        self.amount = amount


def main() -> None:
    seed = int(sys.argv[1]) & 0xFFFFFFFF if len(sys.argv) > 1 else 123456789
    log_path = sys.argv[2] if len(sys.argv) > 2 else "access.log"

    t0 = time.perf_counter()

    # Stage 1
    x = seed
    acc = 0
    mask = 0xFFFFFFFF
    for _ in range(30_000_000):
        x = (x ^ (x << 13)) & mask
        x = (x ^ (x >> 17)) & mask
        x = (x ^ (x << 5)) & mask
        if x & 1:
            acc = (acc + (x ^ 0x9E3779B9)) & mask
        else:
            acc = (acc ^ (x >> 2)) & mask
    chk1 = acc
    t1 = time.perf_counter()

    # Stage 2
    m = 1_000_000
    records = [None] * m
    s = (seed ^ 0xA5A5A5A5) & mask
    for i in range(m):
        s = (s ^ (s << 13)) & mask
        s = (s ^ (s >> 17)) & mask
        s = (s ^ (s << 5)) & mask
        records[i] = Record(i, s & 63, s % 1_000_000)

    records.sort(key=lambda r: (r.amount, r.id))
    chk2 = 0
    for i, r in enumerate(records):
        chk2 += r.amount * ((i & 255) + 1) + r.category
    t2 = time.perf_counter()

    # Stage 3
    counts: dict[str, int] = {}
    with open(log_path, "r", encoding="utf-8", buffering=65536) as f:
        for line in f:
            parts = line.rstrip("\r\n").split("|")
            if len(parts) >= 5:
                lvl = parts[1]
                if lvl == "ERROR" or lvl == "WARN":
                    svc = parts[2]
                    ms_field = parts[4]
                    if ms_field.startswith("ms="):
                        val = int(ms_field[3:])
                        counts[svc] = counts.get(svc, 0) + val

    total_ms = sum(counts.values())
    svc0_ms = counts.get("service_0", 0)
    chk3 = total_ms * 1000 + (svc0_ms % 1000)
    t3 = time.perf_counter()

    s1 = (t1 - t0) * 1000.0
    s2 = (t2 - t1) * 1000.0
    s3 = (t3 - t2) * 1000.0
    tot = (t3 - t0) * 1000.0

    print(
        f"RESULT|Python|stage1={s1:.2f}|stage2={s2:.2f}|stage3={s3:.2f}|total={tot:.2f}|chk={chk1}-{chk2}-{chk3}"
    )


if __name__ == "__main__":
    main()
// 同一の20万行テストログ(access.log / 13.14MB)を生成するワンライナー用スクリプト (gen_log.mjs)
import fs from "node:fs";
const levels = ["INFO", "INFO", "DEBUG", "WARN", "INFO", "ERROR", "INFO", "WARN"];
let s = 987654321 >>> 0;
const lines = new Array(200_000);
for (let i = 0; i < 200_000; i++) {
  s = (s ^ (s << 13)) >>> 0;
  s = (s ^ (s >>> 17)) >>> 0;
  s = (s ^ (s << 5)) >>> 0;
  lines[i] = `2026-11-03T12:00:00Z|${levels[s & 7]}|service_${(s >>> 3) & 63}|path=/api/v1/items/${(s >>> 9) % 100000}|ms=${((s >>> 16) % 950) + 5}\n`;
}
fs.writeFileSync("access.log", lines.join(""), "utf8");

個人開発でAIとRustを快適に回す母艦環境と結論

今回の実験で改めて実感したのは、「人間はやりたいこと(仕様と入出力の確認)だけに集中し、重たい処理の実装は AI に Rust で書かせ、手元の大容量メモリ環境でビルドする」 という組み合わせの圧倒的な気持ちよさです。

Rust のビルド中に出る一時ファイルや作業キャッシュを 64GB メモリ母艦の RAM ディスク に逃がしておけば、内蔵 SSD の寿命(書き込み摩耗)を 1 バイトも削ることなく、0.5 秒で 369 KB の超高速ツールが手に入ります。

ここからは耐用年数にもとづく 推論(試算) ですが、たとえば中古・整備済みの 27 インチ 5K iMac(2020 年モデル)を 64GB メモリへ増設した環境や、64GB ユニファイドメモリ搭載の Mac mini を 5 年間(1,825 日) 毎日使うとすれば、1 日あたりの母艦コストは 数十円〜百数十円 に収まります。誰かに数万円〜数十万円の外注費を払ったり、遅くてメモリを食うツールに毎日イライラしたりする時間を考えれば、自分の時間と快適さを生み出す投資(タイム・イズ・マネー)としてこれほど頼もしい相棒はありません。

【Intel Mac増設用】Crucial iMac 27インチ(2019/2020)・ノートPC用 メモリ DDR4-2666 SO-DIMM (16GB×2 / 32GB×2)

【Intel Mac増設用】Crucial iMac 27インチ(2019/2020)・ノートPC用 メモリ DDR4-2666 SO-DIMM (16GB×2 / 32GB×2)

【中古・整備済品】Apple iMac 27インチ Retina 5K (2020年モデル / Intel最終世代・メモリ自己増設対応)

【中古・整備済品】Apple iMac 27インチ Retina 5K (2020年モデル / Intel最終世代・メモリ自己増設対応)

Apple Mac mini(M4 Pro 等 Appleシリコン Pro系チップ・64GBユニファイドメモリ構成)

Apple Mac mini(M4 Pro 等 Appleシリコン Pro系チップ・64GBユニファイドメモリ構成)

🔧 ✅ 本記事の結論:AI時代の言語選びで損をしない使い分け
  • 裏方のデータ処理・集計・検査・常駐ツールは『Rust』一択 : 外部パッケージに頼らず標準機能中心で AI に書かせれば、コンパイルも 0.53 秒 で終わり、Python の 155 倍速・Java の 10 分の 1 のメモリで動く安全な単一ファイルが手に入ります。

  • 画面まわりは適材適所で組み合わせる : Web サイト構築には Astro(TypeScript)、Mac 専用の画面付き小道具には SwiftUI や Tauri(画面+Rust) を組み合わせるのが最も合理的です。

  • 避けるべき選択 : ただ「人間が書きやすいから」「昔から使っているから」という理由だけで、重い処理まで Python や Java、Node.js で妥協し続けること。

もし手元に「毎回時間がかかっている Python や Node.js のスクリプト」や「これから AI に作らせたい自分専用ツール」がある方は、以下のプロンプトをコピーして試してみてください。なお、メモリ安全設計や境界条件の詰めを読み飛ばさないよう、軽量モデルではなく 必ず『Gemini 4 Argon』(または同等以上の最上位推論モデル)を選択した上で 相談することをおすすめします。

🔧 🤖 コピペ用:最上位AI(Gemini 4 Argon等)に『外部依存ゼロの高速Rustツール』を作らせるプロンプト
以下の記事の実測検証(外部パッケージに頼らない標準ライブラリ中心のRust実装による高速化・省メモリ化・単一バイナリ化)を参考に、私の手元の不便を解消する高速ツールをRustで作成したいです。
https://lab.chihouzakki.com/blog/review/rust-vs-10-languages-ai-benchmark-compile-memory/

いきなりコードを書き始める前に、まず以下の手順で進めてください:
1. 私がやりたい処理の「入力データ」「出力形式」「例外ケース(空行・文字コード・桁あふれ等)」をヒアリングして仕様を確定させる。
2. ビルド時間とファイル容量を最小に保つため、可能な限り外部クレートに頼らずRust標準ライブラリ(std)のみで設計できるか検討し、構成案を提示する。
3. 私の合意後にソースコードと最適化ビルド手順(rustc -C opt-level=3 または cargo build --release)、および結果が1ビットも狂っていないか確かめる検証テストを提示する。

「人間には難しすぎた最強の道具」が、AI という翻訳機兼職人を経ることで「誰でもタダで使える一番身近で快適な道具」に変わりました。ぜひ身の回りのちょっとした不便解消から、AI × Rust の爆速体験を味わってみてください。

10言語の計測結果を鋭い目で見つめるノルウェージャンフォレストキャット

「ワタシのご飯の準備も、Python の 16.8 秒じゃなくて Rust の 0.1 秒でお願いするでちぃ」