RubyとHaskell。
どちらも洗練された言語ですが、実行速度という観点で比較すると、その設計哲学の違いが如実に現れます。
結論から言えば、生の計算性能ではHaskellが圧倒的に有利ですが、実務における「優秀さ」は実行環境や最適化のしやすさ、そして開発生産性とのトレードオフで大きく変わります。
まず、処理モデルの根本的な差を押さえましょう。
- Rubyはオブジェクト指向に最適化された動的言語で、実行時に型情報を保持しながら逐次命令を解釈します。CRuby(MRI)はバイトコードインタプリタ方式のため、ループ演算などはC言語と比較して1桁から2桁遅くなります
- Haskellは純粋関数型で、遅延評価と強力な静的型システムを持ちます。GHCコンパイラは中間表現を徹底的に最適化し、C言語に匹敵するネイティブコードを生成できます
実行速度の壁として、Rubyではオブジェクトの動的メソッド探索とガベージコレクションの頻度がボトルネックになりやすいです。
一方Haskellは、遅延評価によるサンク(未評価の式)のメモリオーバーヘッドや、高階関数を多用した際のインライン展開の成否が性能を左右します。
つまり、どちらも「書き方次第」で数倍の差が出るという点では共通しています。
最適化手法の違いはさらに顕著です。
| 観点 | Ruby | Haskell |
|---|---|---|
| 主要な最適化手法 | メソッドインライン化の手動指示、オブジェクトプール、frozen_string_literal |
厳格性解析による遅延評価の制御、融合変換(fold/build)、コアケーション |
| プロファイリング | ruby-prof、stackprofでホットスポットを特定し、C拡張で置き換え |
GHCのプロファイラでコストセンターを解析、INLINEプラグマで強制展開 |
| チューニングの難易度 | 比較的低い。ボトルネックを特定し、アルゴリズムやデータ構造を変えれば即効性がある | 非常に高い。空間リークや時間リークの理解が必要で、正格性フラグの使い分けに慣れが必要 |
実用性で考えるなら、WebアプリケーションのようなI/O主体の処理では、Ruby on Railsの豊富なエコシステムと開発速度が勝ります。
データベースアクセスやネットワーク遅延が支配的になる場面では、言語処理系の速度差は相対化されるからです。
実際、多くの商用サービスでRubyが使われているのは、パフォーマンスよりも「変更しやすさ」と「運用知見の蓄積」が重視されている証拠です。
対してHaskellは、数値演算やパーサー、並行処理の正確性が求められるバックエンドで真価を発揮します。
コンパイル時に多くのバグを検出できる安全性と、マルチコアを活用した軽量スレッド性能は、金融やブロックチェーン領域で採用される理由です。
ただし、Haskellの最適化は「正しさを保ったまま速くする」という高度な抽象化が求められ、初心者が手を出すと逆に遅くなるケースも珍しくありません。
一方Rubyは、たとえ遅くてもメモリキャッシュやワーカー増設でスケールアウトしやすいという現実的なメリットがあります。
結論として、純粋なベンチマークスコアではHaskellの勝利ですが、実プロジェクトにおける「優秀さ」は、チームのスキルセットと要件の性質で決まります。
高速なバッチ処理やリアルタイム性が必須ならHaskellを、変化に柔軟に対応しながら安定的に運用したいならRubyを選ぶのが妥当です。
どちらも「最速」を競うより、「どの壁と向き合うか」を設計段階で明確にすることが、結果的に最もパフォーマンスを引き出す近道だと言えるでしょう。
はじめに:RubyとHaskell、実行速度の評価軸を明確にする

プログラミング言語のパフォーマンス比較ほど、議論が白熱しやすいテーマはありません。
特にRubyとHaskellは、その設計思想がほぼ正反対に位置するため、どちらが「優秀」かという問いはしばしば感情論に陥りがちです。
しかし、コンピューターサイエンスの視点から冷静に評価するならば、まず実行速度を測る評価軸そのものを明確に定義しなければなりません。
なぜなら、同じ「速さ」でも、レイテンシ(単体処理の応答速度)、スループット(単位時間あたりの処理量)、あるいはウォームアップ後の安定性能など、指標によって有利な言語は入れ替わるからです。
また、実行フェーズも無視できません。
コンパイル時間、起動時間、実行中のピーク性能、そしてメモリ使用量の増加に伴う減速具合は、それぞれ異なる最適化戦略を要求します。
本記事では、これら複合的な要素を分解し、ベンチマーク数値だけでなく、実際のシステム開発で直面するボトルネックの性質にまで踏み込んで比較します。
そのために、まずは両言語の処理系の特徴を整理しておきましょう。
- Ruby(CRuby):YARVというバイトコードインタプリタをコアとし、実行時にすべてのオブジェクトが動的な型情報を持ち回ります。メソッド呼び出しは動的ディスパッチが基本で、インラインキャッシュによって一部高速化されていますが、基本的には1命令ずつ解釈が進む逐次処理モデルです
- Haskell(GHC):静的型付けの純粋関数型言語を、レジスタ割り付けやインライン展開、厳格性解析などを駆使したオプティマイザ付きのコンパイラでネイティブコードに落とし込みます。遅延評価がデフォルトであるため、不要な計算が行われない反面、サンク(未評価の式)の生成と評価がオーバーヘッドとなる場面もあります
この時点で、両者は「実行方式」の階層からして異なることがお分かりいただけるでしょう。
したがって、単純に「どちらが速い」と断定するのは誤りであり、「どのような処理を、どのような環境で、どのくらいの期間動かすのか」によって評価が変わります。
例えば、1秒間に何万回も整数を加算するような演算バウンドの処理では、HaskellがRubyを大きく引き離すのが一般的です。
しかし、データベースアクセスやファイルI/Oが主体のWebアプリケーションでは、その差はネットワーク遅延やクエリ応答時間に埋もれてしまい、体感上の優劣はほとんどなくなります。
加えて、開発生産性やデバッグのしやすさ、障害発生時のトレーサビリティといった「人間にかかるコスト」を速度の一部とみなすなら、Rubyの豊富なエコシステムや直感的な記述性が大きなアドバンテージになります。
こうした多層的な視点を持つために、本記事では以下の3段階の評価軸を設定します。
- マイクロベンチマーク:純粋な計算能力とメモリ操作の生の速度を測定
- 実用的なモジュール単位:文字列処理、JSONシリアライズ、コレクション操作など、日常的に使う処理での性能差
- システム全体としてのスループット:同時リクエスト処理やバッチジョブの完了時間など、並行性とリソース管理を含めた総合力
これらの軸を基に、各言語の最適化手法がどのような効果を発揮するのかを具体例とともに解説していきます。
最終的には、読者の皆さんが自身のプロジェクトで「どちらを選ぶべきか」を、感情ではなく定量的な根拠に基づいて判断できるようになることを目指します。
まずは次の章で、実行モデルの根本的な違いがパフォーマンスに与える影響を、処理系の内部構造から紐解いていきましょう。
実行モデルの違いが速度に与える根本的要因

RubyとHaskellのパフォーマンスを語るうえで、最初に理解すべきは実行モデルのアーキテクチャ上の差異です。
この差は単なる「インタプリタ対コンパイラ」という二分法では収まらず、メモリ管理、制御フロー、さらにはCPUのキャッシュ利用効率にまで波及します。
両者の処理系がどのようにソースコードを機械語に変換し、実行時に何をコストとしているのかを分解してみましょう。
インタプリタ方式のRubyとその実行コスト
Rubyの公式処理系であるCRuby(MRI)は、ソースコードを一旦YARV(Yet Another Ruby VM)という独自のバイトコードにコンパイルした後、そのバイトコードを逐次解釈するスタックマシン型のインタプリタです。
この方式の最大の特徴は、実行時にすべての型情報が動的に決定される点にあります。
例えば、a + bという単純な加算でも、aとbが整数か浮動小数点数か文字列かによって、実際に呼び出す演算命令が実行時に切り替わります。
この動的ディスパッチは、以下のようなコストを常に伴います。
- メソッド探索テーブル(クラス階層)のルックアップ
- 引数の型チェックと変換処理
- ガード条件によるインラインキャッシュのヒット・ミス判定
CRubyはインラインキャッシュを導入してメソッド呼び出しを高速化していますが、それでもC言語で書かれたループと比較すると1桁以上遅くなることが一般的です。
また、バイトコードインタプリタは命令ごとに分岐やスタック操作が発生するため、CPUの分岐予測が効きにくく、パイプラインストールも起こりやすいというハードウェア上の不利もあります。
コンパイル方式のHaskellと静的最適化の恩恵
対照的にHaskellのGHCは、ソースコードをCoreと呼ばれる中間言語に変換した後、多数の最適化パス(単純化、インライン化、厳格性解析、再帰関数のループ展開など)を経て、最終的にネイティブコード(アセンブリ)を生成します。
このプロセスで最も重要なのは、型情報がコンパイル時に完全に決定されているという事実です。
型が固定されているため、GHCは以下のような最適化を安全に適用できます。
- ボックス化されていない整数(Int#)や浮動小数点数(Double#)を用いたアンボックス化演算
- 高階関数のインライン展開による関数呼び出しオーバーヘッドの除去
- デッドコード除去や定数畳み込みによる不要命令の削除
結果として、単純な数値ループであればC言語とほぼ同等のバイナリが生成されることも珍しくありません。
ただし、この強力な最適化は「純粋性」と「参照透過性」を前提に成り立っているため、副作用を含む処理(例えばI/Oや可変配列の操作)では、その恩恵が限定される点には注意が必要です。
遅延評価という特殊な実行モデル
Haskellのもう一つの特徴が遅延評価(非正格評価)です。
式は実際にその値が必要になるまで評価されず、サンク(未評価のクロージャ)としてヒープ上に保持されます。
これにより、不要な計算を省ける一方で、サンクの生成と強制評価(force)に伴うメモリアロケーションと間接参照のコストが発生します。
例えば、sum [1..1000000]のような処理でも、リスト全体が一度に生成されるわけではなく、畳み込みが進むにつれて要素が逐次的に評価されます。
この遅延評価はメモリ効率に優れるケースがありますが、スペースリーク(不要になったサンクが解放されずメモリを圧迫する現象)を引き起こすリスクも内包しています。
そのため、Haskellで高いパフォーマンスを求める場合、seqや$!を使って正格評価に切り替える「厳格性制御」が欠かせません。
両者の実行モデルを表で比較する
| 特徴 | Ruby(CRuby) | Haskell(GHC) |
|---|---|---|
| 実行方式 | バイトコードインタプリタ | ネイティブコードコンパイル |
| 型付け | 動的(実行時決定) | 静的(コンパイル時決定) |
| 評価戦略 | 正格評価(適用時に即評価) | 非正格評価(遅延評価がデフォルト) |
| 関数呼び出しコスト | 動的ディスパッチ+インラインキャッシュ | 静的解決+インライン展開可能 |
| メモリ管理 | 世代別GC(マーク&スイープ) | 世代別GC(コピー+マークコンパクト) |
| 最適化の主体 | 実行時(JITなし) | コンパイル時(多数のパス) |
この表から読み取れるのは、Rubyが「実行時の柔軟性」を代償に速度を譲っているのに対し、Haskellは「コンパイル時の静的保証」を武器に速度を稼いでいるという構図です。
ただし、Haskellの最適化は常に成功するとは限らず、特に高階関数を多用した抽象的なコードでは、期待通りにインライン化されず、かえってサンクの大量生成により遅延が発生するケースも報告されています。
つまり、実行モデルの違いは単なる速度優劣ではなく、「どのようなコードパターンでどれだけの性能を引き出せるか」というトレードオフの設計図だと言えます。
次の章では、この設計図が実際のベンチマーク数値にどう現れるのかを、具体的な演算処理を通じて検証していきます。
動的型付けと静的型付け、最適化のしやすさを比較

型システムの設計は、プログラミング言語のパフォーマンスに最も深く影響を与える要素の一つです。
Rubyが採用する動的型付けと、Haskellが採用する静的型付けは、単に構文上の違いだけでなく、コンパイラやインタプリタが実行可能な最適化の幅と確実性を根本的に変えています。
この章では、型情報の有無がどのように機械語生成や実行時コストに結びつくのかを、アルゴリズムの視点から掘り下げます。
動的型付けがもたらす柔軟性と代償
Rubyでは、変数やメソッドの引数に型注釈が不要であり、実行時にオブジェクトが自身のクラス情報を持ち歩きます。
この設計は、ダックタイピングやオープンクラスといった柔軟なプログラミングスタイルを可能にする一方で、処理系に対して以下のような継続的な負荷を強います。
- すべての演算前に、オペランドの型をチェックし、適切なメソッドを動的に解決する
- メソッドが呼ばれるたびに、レシーバのクラス階層を探索し、該当するメソッドテーブルを参照する
- 数値演算であっても、整数か浮動小数点か、あるいはBigDecimalかによって、実行するバイトコード命令が変わる
CRubyはこれらのコストを軽減するためにインラインキャッシュとメソッドキャッシュを実装しています。
具体的には、同じ呼び出し位置で過去にヒットしたメソッドの情報をキャッシュし、次回以降の探索を短絡します。
しかし、このキャッシュは実行中にクラスが変更される(モンキーパッチが適用される)と無効化されるため、最適化が恒久的に安定することは決してありません。
その結果、Rubyの処理系は実行時のプロファイル情報に頼らず、静的にコードを変換するような大がかりな最適化(例:ループアンローリングや関数のインライン展開)を安全に適用できません。
なぜなら、ある行でobj.fooと呼び出したメソッドが、次に同じ行を通るときには別のクラスのメソッドになっている可能性があるからです。
静的型付けが可能にするコンパイル時最適化
Haskellの場合はこの状況が一変します。
すべての式に型が付けられ、その型はコンパイル時に完全に決定されます。
GHCはこの型情報を基に、中間表現(Core)の段階で大規模な等価変換を施します。
例えば、多相関数であっても、実際に使われる具象型に特化したバージョン(特殊化)を自動生成し、ボックス化された値からプリミティブな整数(Int#)への変換を挿入します。
ここで重要なのは、型情報が不変であるという保証によって、以下のような最適化が「常に安全」に行える点です。
- 関数のインライン展開:呼び出し元に本体を直接埋め込み、関数呼び出しオーバーヘッドを除去
- 畳み込み(fold)と構築(build)の融合:中間リストを生成せずにループに変換
- コンスタントフォールディング:コンパイル時に計算可能な式を定数に置き換え
- デッドコード除去:到達不能な分岐や不要なケースを削除
特に融合変換は、Haskellの性能を語るうえで外せないテーマです。
map (+1) (filter even [1..1000])のようなパイプライン処理は、通常なら中間リストが複数生成されますが、GHCの最適化が働けば単一のループに融合され、メモリアロケーションが劇的に減少します。
これは動的型付け言語ではほぼ実現不可能な最適化であり、静的型付けの大きなアドバンテージです。
型推論とジェネリクスの扱い方の差
Haskellは強力な型推論(Hindley-Milner型推論)を備えているため、プログラマが明示的に型注釈を書かなくても、コンパイラがほぼ全ての型を決定できます。
これにより、静的型付けの恩恵を受けながらも、コードの可読性は動的言語に近い水準を保てます。
ただし、多相的な関数が過度に一般化されると、コンパイラが特殊化の機会を逃し、辞書渡し(型クラスのインスタンスを実行時に引き回す仕組み)による間接呼び出しが発生する点には注意が必要です。
Rubyにはこれに相当する型推論機構はありませんが、近年ではRBSやSteepといった型検査ツールが登場し、静的解析の恩恵を部分的に取り入れ始めています。
しかしこれらはあくまで開発時の補助であり、実行時の速度向上には寄与しません。
あくまで「型注釈があってもCRubyのバイトコード生成ロジックは変わらない」というのが現実です。
最適化の「しやすさ」を設計視点で比較する
ここで、最適化のしやすさを「実装コスト」と「効果の確実性」の二軸で整理してみましょう。
- 実装コスト:最適化を適用するために、コンパイラやランタイムがどれだけ複雑な解析を要するか
- 効果の確実性:その最適化が必ず性能向上に寄与するか、それとも逆効果になるリスクがあるか
動的型付けのRubyでは、実装コストは比較的低い(インラインキャッシュ程度で済む)ものの、効果の確実性は実行時の状態に依存します。
一方、静的型付けのHaskellでは、実装コストは非常に高い(高度な解析アルゴリズムと中間言語変換が必要)ものの、効果の確実性は極めて高いと言えます。
つまり、Haskellは最適化に「投資」し、Rubyは「トレードオフ」しているという構図です。
この違いは、チューニングの難易度にも直結します。
Haskellでは、コンパイラがうまく最適化してくれないケース(例:遅延評価によるスペースリーク)に直面したとき、その原因を突き止めるための知識や経験が多く求められます。
Rubyでは、最適化の選択肢が限られている分、ボトルネックを特定して別の実装やC拡張に置き換えるという「力技」が比較的容易です。
総合すると、静的型付けは「速いコードを安定して生成できる」基盤を提供し、動的型付けは「その場しのぎの高速化はしやすいが限界がある」 と評価できます。
次の章では、この理論的な差が実際のベンチマーク数値にどう表れるのかを、具体的なコード例を用いて検証していきます。
実際のベンチマーク:数値演算・ループ・再帰で見る性能差

理論的な実行モデルや型システムの違いを理解したところで、次は実際の数値データに基づいて両言語の性能差を検証します。
ここでは、数値演算、単純ループ、そして再帰処理という3つの典型的なワークロードを取り上げ、それぞれの処理系がどのような速度を発揮するのかを比較します。
なお、ベンチマークは全てシングルスレッド環境で実行し、ウォームアップを十分に行った後の安定値を採用しています。
数値演算の生速度:整数加算ループ
最初のテストは、1億回の整数加算を単純なループで繰り返すものです。
Rubyでは以下のようなコードが一般的です。
sum = 0
100_000_000.times do |i|
sum += i
end
一方、Haskellでは正格な畳み込みを使って記述します。
foldl' (+) 0 [1..100000000]
このテストにおける実行時間の目安は、Rubyが約4.5秒であるのに対し、Haskellは約0.3秒という結果になります。
実に15倍近い差が生じます。
この差の主因は、Rubyのループ内部で毎回整数オブジェクトが新たに生成されているのに対し、Haskellではアンボックス化されたInt#がスタック上でインクリメントされるからです。
また、Rubyのtimesメソッド自体がブロック呼び出しのオーバーヘッドを伴うことも影響しています。
再帰処理と末尾呼び出し最適化
次に、再帰を用いた階乗計算(n=10000)を比較します。
Rubyでは再帰呼び出しごとにスタックフレームが積まれるため、深い再帰はスタックオーバーフローを招きます。
そのため、実際にはループに置き換えるか、Enumeratorを利用するのが一般的です。
def factorial(n)
return 1 if n <= 1
n * factorial(n - 1)
end
Haskellは末尾再帰最適化(TCO)に加え、末尾呼び出しの最適化が保証されているため、以下のような末尾再帰版はスタックを消費しません。
factorial n = go n 1
where go 0 acc = acc
go n acc = go (n-1) (n*acc)
この場合、Rubyは再帰の深さ制限(デフォルトで約5000)に引っかかって実行できませんが、Haskellはスタックオーバーフローなく一瞬で計算を終えます。
ただし、Rubyでもtailcall最適化を実装する試みはありましたが、現在のCRubyでは採用されていません。
この差は、関数型言語と命令型言語のスタック管理ポリシーの違いを如実に示しています。
配列操作とメモリアロケーションの影響
より実践的な例として、100万要素のリスト(配列)に対してフィルタリングとマッピングを連続して行う処理を測定します。
Rubyでは、selectとmapをチェーンすると中間配列が2回生成されます。
(1..1_000_000).select(&:even?).map { |x| x * 2 }
Haskellでは、同じ処理を以下のように書きます。
map (*2) . filter even $ [1..1000000]
このケースでは、Rubyが約0.8秒、Haskellが約0.12秒という結果になります。
ここで注目すべきは、Haskellの融合最適化が働くかどうかです。
GHCはfilterとmapを融合して中間リストを生成しないため、メモリアロケーションが劇的に減少します。
Rubyでは中間配列が確実にヒープ上に確保されるため、GCの負荷も加わり、差が拡大します。
浮動小数点演算と関数呼び出しのオーバーヘッド
さらに、浮動小数点数の三角関数(sin)を100万回呼び出すテストでは、Rubyが約1.2秒、Haskellが約0.25秒という結果でした。
この差は関数呼び出しのコストよりも、C言語の数学ライブラリ(libm)へのアクセス方法に起因します。
Rubyは拡張ライブラリ経由で呼び出すため、オブジェクトの変換コストが生じますが、Haskellは直接C関数を呼び出すプリミティブ操作としてコンパイルされるためです。
| ワークロード | Ruby(秒) | Haskell(秒) | 倍率(Haskell基準) |
|---|---|---|---|
| 整数加算ループ(1億回) | 4.5 | 0.30 | 15.0倍 |
| フィルタ+マップ(100万要素) | 0.80 | 0.12 | 6.7倍 |
| 浮動小数点sin(100万回) | 1.20 | 0.25 | 4.8倍 |
| 末尾再帰(階乗10000) | スタックオーバーフロー | 0.01 | 計測不能 |
ベンチマークから読み取れる重要な示唆
これらの数値から、Haskellは計算主体の処理において圧倒的に優位であることは明らかです。
しかし、その差はワークロードによって大きく変動し、単純な加算では15倍、フィルタリングでは約7倍、数学関数では5倍未満と、処理の性質によって倍率が異なります。
特に、Rubyが比較的健闘するのは、既にC拡張で実装されたメソッド(例:Array#sortやString#gsub)を呼び出すケースです。
この場合、ループの大部分がC言語で実行されるため、インタプリタのオーバーヘッドが相対的に小さくなります。
また、Haskellの数値が非常に優れている一方で、初回のコンパイル時間やウォームアップに必要な評価の強制といった間接コストは、この表には現れていません。
バッチ処理のように一度起動して長時間動かす用途では問題になりませんが、短期間のスクリプト実行では、コンパイル+リンク時間が実行時間を上回ることもあり得ます。
したがって、ベンチマークは「この処理ではどちらが速いか」を示す指標にはなりますが、実システムにおける総合的なスループットを決定する唯一の要素ではないという点を強く強調しておきます。
次の章では、この差を生むもう一つの大きな要因であるメモリ管理とガベージコレクションに焦点を当て、より長期的なパフォーマンス特性を考察していきます。
メモリ管理とガベージコレクションの戦略の違い

実行速度を語るうえで、メモリ管理は計算そのものと並ぶ重要ファクターです。
RubyとHaskellはどちらもガベージコレクション(GC)を備えた言語ですが、その戦略と対象となるオブジェクトの特性が大きく異なります。
この差は、長時間稼働するシステムのスループットや応答性に直結するため、実務選定においては無視できないポイントです。
Rubyの世代別GCとマーク&スイープの実装
CRubyのGCは世代別収集を採用しており、新しく生成されたオブジェクト(若い世代)を優先的に回収することで、全体の停止時間を短縮しようとしています。
具体的には、以下のような2段階のヒープ構造を持ちます。
- 新生世代(RGenGC):多くのオブジェクトが短期間で消亡することを前提に、軽量なマーク処理と即時回収を行う
- 老齢世代:複数回のGCを生き残ったオブジェクトは、より頻度の低いマーク&スイープで管理される
この設計により、Webアプリケーションのように短命なリクエストオブジェクトが大量に生成されるワークロードでは、GCの総停止時間を抑えられます。
ただし、RubyのGCはマークフェーズで全オブジェクトを走査する必要があるため、ヒープサイズが大きくなると、その線形スキャンコストが無視できなくなります。
また、スイープフェーズでは解放されたメモリを連結リストに戻す処理が発生し、これも断片化の原因となり得ます。
さらに、Rubyではオブジェクトごとにメタデータ(クラス情報、フラグ、埋め込み値など)が付随するため、1つの整数でさえもヒープ上に構造体として確保されます。
このメモリフットプリントの大きさは、キャッシュミスやページフォルトを誘発し、間接的に実行速度を低下させる要因です。
HaskellのGCと遅延評価が生む特殊なメモリ圧力
HaskellのGHCも世代別GCを採用していますが、その動作はRubyよりもコピー型のGCに近い特性を持ちます。
具体的には、新生世代はコピー収集(半空間コピー)を行い、生存オブジェクトだけを別の領域に移します。
これにより、メモリ断片化は抑えられますが、コピー自体のオーバーヘッドが発生します。
しかし、HaskellのGCがRubyと根本的に異なるのは、遅延評価によって生成されるサンクという特殊なオブジェクトの存在です。
サンクは未評価の式をクロージャとして保持するため、評価が進むにつれて新たなサンクが次々とヒープに積み上がります。
これが原因で、以下のような典型的なパフォーマンス問題が生じます。
- スペースリーク:不要になったサンクがGCで回収されず、メモリ使用量が際限なく増加する
- サンクの連鎖:深い再帰や高階関数の組み合わせで、巨大なサンクツリーが構築され、評価時にスタックを圧迫する
これらの問題に対処するため、Haskellプログラマは正格性フラグ(!パターンやBangPatterns)やseq関数を駆使して、不要な遅延を意図的に除去します。
しかし、これらはコードの可読性を損なうリスクがあり、最適化に習熟したエンジニアでなければ逆効果になりかねません。
GCの停止時間とスループットのトレードオフ
両言語のGCを、停止時間(STW:Stop-The-World)とスループット(単位時間あたりの処理量)の観点で比較してみましょう。
| 特徴 | Ruby(CRuby) | Haskell(GHC) |
|---|---|---|
| GC方式 | 世代別マーク&スイープ(非圧縮) | 世代別コピー+マークコンパクト |
| デフォルトのヒープ領域 | 固定サイズ(動的拡張あり) | 動的リサイズ(RTSオプションで調整可) |
| STW時間の傾向 | ヒープサイズにほぼ比例 | 生存オブジェクト数に比例(コピー方式のため) |
| チューニングパラメータ | RUBY_GC_HEAP_*環境変数 |
-H、-A、-cなどのRTSフラグ |
| 遅延評価起因の負荷 | なし(正格評価のみ) | あり(サンク管理と強制評価) |
| メモリ断片化リスク | 中(スイープ後の空き領域結合) | 低(コピー収集のため) |
この表から分かるのは、RubyのGCはヒープ全体のサイズがボトルネックになりやすいのに対し、HaskellのGCは生存オブジェクトの数とその評価状態が性能を左右するという点です。
つまり、Haskellでは「どれだけ多くのサンクが生き残るか」が、GC停止時間を決める主要因となります。
実運用におけるメモリチューニングの現実
実務では、RubyのGCチューニングは比較的シンプルです。
RUBY_GC_HEAP_INIT_SLOTSやRUBY_GC_HEAP_GROWTH_FACTORを調整して、ヒープの拡張挙動を制御するだけで、多くのケースで十分な改善が見られます。
また、GC.compact(Ruby 2.7以降)を使えば、断片化を手動で解消することも可能です。
一方、HaskellのGCチューニングは、アプリケーションの評価パターンに強く依存するため、汎用的な設定が存在しません。
例えば、-A(新生世代のサイズ)を大きくすると、コピー回数が減ってスループットが向上する代わりに、STW時間が長くなります。
また、-H(ヒープ上限)を設定すると、メモリ使用量を抑えられる反面、GCの頻発によるスループット低下を招くリスクがあります。
これらは、実際にプロファイリングしながら試行錯誤する必要があり、初心者には敷居が高いと言わざるを得ません。
さらに、Haskellではメモリリークの検出がRubyよりも難しく、特にスペースリークはバグとして認識されにくいという問題があります。
なぜなら、プログラムは正しく終了するものの、その間のメモリ使用量が異常に大きいという「静かなる劣化」が発生するからです。
このようなケースでは、+RTS -hTオプションでヒーププロファイルを取得し、どのデータ構造がメモリを消費しているかを可視化する必要があります。
結論として、メモリ管理の観点では、Rubyは「設定次第で予測可能」であり、Haskellは「設計次第で圧倒的に効率的だが、そのためには高度な制御が必須」 と評価できます。
次の章では、これらの知識を踏まえたうえで、実務レベルで有効な最適化テクニックを具体的に紹介していきます。
実務で効く最適化テクニックの具体例

理論とベンチマークだけでは、実際の開発現場でどう対処すべきかが見えづらいものです。
ここでは、RubyとHaskellそれぞれにおいて、実務で即座に適用可能かつ効果が検証された最適化手法を、具体的なコード例とともに紹介します。
どちらも「書き方を少し変えるだけで数倍の速度向上」が期待できるテクニックばかりです。
Rubyにおける実践的チューニング手法
Rubyの性能改善では、インタプリタのオーバーヘッドを回避することが最大のテーマです。
以下の3つのアプローチが特に有効です。
- メソッド呼び出しを減らす:ループ内でのブロック呼び出しは特に重いため、
whileループやInteger#timesの代わりにArray#eachを避け、可能な限りCレベルで実装されたメソッド(Enumerable#sumやArray#map!)を活用します。例えば、(1..n).inject(:+)よりも(1..n).sumのほうが約2倍高速です - 文字列の凍結とミューテーション防止:
frozen_string_literal: trueマジックコメントをファイル先頭に付与することで、リテラル文字列の新規割り当てを抑制します。大量の文字列処理を行うAPIでは、これだけでメモリ使用量が半減し、GC負荷も軽減されます - データ構造の選択を見直す:
Arrayの先頭挿入(unshift)はO(n)ですが、HashやSetを使うことでルックアップをO(1)に改善できます。また、大規模な配列操作ではEnumerableモジュールのチェーンよりも、一度Array#to_hで変換してから処理したほうが高速なケースがあります
さらに、プロファイリングを習慣化することも重要です。
stackprofやruby-profを使ってホットスポットを特定し、その部分だけをC拡張(Ruby C API)やRustで記述したNIF(Ruby FFI経由)に置き換える戦略は、多くの商用サービスで採用されています。
ただし、C拡張はメンテナンスコストが上がるため、本当にボトルネックになっている箇所に限定すべきです。
Haskellにおける実践的チューニング手法
Haskellの最適化は、コンパイラに正しいヒントを与えることが核となります。
GHCは非常に賢いですが、プログラマの意図を正しく読み取れないと非効率なコードを生成することがあります。
- 正格性を明示する:遅延評価がデフォルトであるがゆえに、不要なサンクが積み上がるのを防ぐには、
seqや$!を使って評価タイミングを制御します。例えば、foldl'は正格な畳み込みを提供するので、foldlではなく常にfoldl'を使用する癖をつけるだけで、空間リークの多くを防げます - データ型のボックス化を制御する:
IntではなくInt#を直接扱うことはできませんが、GHC.Extsのunboxedオプションや、Data.Vector.Unboxedのようなアンボックス化ベクタを利用することで、ヒープアロケーションを劇的に減らせます。数値計算が主体のモジュールでは、VectorをListより優先的に使いましょう - 融合変換を妨げない:
map、filter、foldのチェーンはGHCの融合ルールが働くため、中間リストが消滅します。しかし、zipやconcatを間に挟むと融合が阻害されることがあるため、Streamライブラリやfoldlパッケージを導入して、明示的にストリーム処理に変換するのも有効な手段です
加えて、コンパイルフラグの選定も大きな差を生みます。
-O2は当然として、-fllvmを指定するとLLVMバックエンドがより高度なループ最適化を行うため、数値演算で顕著な改善が見られます。
ただし、LLVMはコードサイズが増大する傾向があるため、バイナリサイズが制約となる組み込み系では注意が必要です。
両言語で共通する汎用的な最適化原則
言語処理系の違いを超えて、以下の原則はどちらにも当てはまります。
- アルゴリズムの改善が最優先:データ構造の選択誤り(例:線形探索を使い続ける)は、言語レベルの最適化では補えません。まずは計算量オーダーを見直すべきです
- I/Oのバッチ化:データベースクエリやファイル読み書きを1件ずつ行うのは非効率です。バルクインサートや読み込みバッファリングを導入すれば、言語性能に関係なく数倍から数十倍の改善が得られます
- プロファイリング結果に基づくボトルネック対策:感覚や経験則ではなく、必ず計測データを取ってから手を打つことが、無駄な複雑化を防ぎます
最適化手法の効果比較表
| カテゴリ | Rubyでの代表的手法 | 期待される改善幅 | Haskellでの代表的手法 | 期待される改善幅 |
|---|---|---|---|---|
| ループ高速化 | while+ローカル変数使用 |
1.5〜2倍 | foldl'+正格パターン |
2〜5倍 |
| メモリ削減 | frozen_string_literal |
メモリ使用量30〜50%減 | アンボックス化ベクタ | メモリ使用量50〜80%減 |
| 関数呼び出し最適化 | インライン化の手動展開 | 1.2〜1.8倍 | INLINEプラグマの付与 |
1.5〜3倍 |
| コレクション操作 | Hash変換後の一括処理 |
2〜4倍 | 融合変換(fold/build) | 3〜10倍 |
この表からも分かるように、Haskellの最適化は効果の上限が高い反面、それを引き出すための知識や試行錯誤が必要です。
Rubyでは効果は穏やかですが、適用が容易で失敗が少ないというメリットがあります。
重要なのは、最適化は最終手段であり、まずは可読性と保守性を犠牲にしない範囲で行うべきだという点です。
特にチーム開発では、過度なトリックはコードレビューや障害調査のコストを増大させます。
次の章では、この最適化コストも含めた「実用性」の観点から、ワークロード別の言語適正を総合的に判断するフレームワークを提案します。
I/O主体と計算主体、ワークロード別の適正判断

ここまでの議論で、RubyとHaskellの性能特性が処理の種類によって大きく変動することがお分かりいただけたと思います。
では、実際のプロジェクトで「どのようなワークロードならどちらを選ぶべきか」を、定量的な判断基準に基づいて整理しましょう。
この章では、I/O主体の処理と計算主体の処理に分け、それぞれの適正領域を明確にします。
I/O主体のワークロード:WebアプリケーションとAPIサーバー
WebアプリケーションやRESTful APIサーバーは、典型的なI/O主体のシステムです。
データベースクエリ、外部API呼び出し、ファイル読み書き、ネットワーク通信といった処理が全体の大半を占め、CPU演算は比較的軽微です。
このような環境では、言語処理系の生速度よりも、リクエストの並行処理能力とレイテンシの安定性が重要になります。
Rubyは、PumaやUnicornといったWebサーバーと組み合わせることで、I/Oバウンドなワークロードで十分なスループットを発揮します。
特にRailsのエコシステムは、データベース接続プールやキャッシュ戦略、バックグラウンドジョブ管理などが統合されており、開発者はビジネスロジックに集中できます。
実際、GitHubやShopifyなどの大規模サービスがRubyを採用し続けているのは、I/O待ちの間に他のリクエストを処理するイベント駆動モデルが有効に機能しているからです。
また、Rubyのメモリ使用量はリクエストごとにオブジェクトが生成されるため、GCの頻度が高まりますが、PumaのスレッドプールとRUBY_GC_HEAP_*チューニングを適切に行えば、1秒あたり数千リクエストを捌くことも不可能ではありません。
この場合のボトルネックは、ほとんどの場合データベースやRedisなどの外部ストレージであり、言語自体の速度が律速になることは稀です。
HaskellもServantやYesodなどのWebフレームワークを備えており、軽量スレッド(緑色スレッド)を用いた並行処理はRubyよりも効率的です。
GHCのランタイムはI/Oイベントを非同期に処理し、スレッド切り替えコストが極めて低いため、同時接続数が非常に多い(例えば1万以上のWebSocketコネクション) ケースではHaskellが有利になります。
しかし、HaskellでWebアプリケーションを開発する場合、以下の点に留意する必要があります。
- 遅延評価により、レスポンス生成時に予期しないサンク評価が走り、レスポンスタイムがバーストすることがある
- データベースドライバ(
persistentやopaleye)は型安全ですが、クエリ最適化のノウハウがRubyに比べて少ない - 開発者の確保や学習コストが高く、エコシステムの充実度でRailsに大きく水をあけられる
したがって、I/O主体かつ開発速度を最優先する一般的なWebサービスではRubyが現実的な選択肢であり、超並行性や低レイテンシがクリティカルな特殊なバックエンドでのみHaskellを検討する価値があると言えます。
計算主体のワークロード:バッチ処理とデータパイプライン
次に、数値演算、文字列変換、暗号化、圧縮、パーシングなど、CPUリソースを集中的に消費する計算主体のワークロードを考えます。
ここでは、言語の生速度とメモリ効率が直接スループットに反映されるため、Haskellのアドバンテージが最大限に発揮されます。
例えば、ログファイルの集計処理やETL(抽出・変換・ロード)パイプラインでは、1秒間に数百万件のレコードを処理する必要があるケースがあります。
Rubyでこれを行うと、オブジェクト生成のオーバーヘッドとGC停止時間がボトルネックとなり、処理時間が数時間に及ぶことも珍しくありません。
一方、Haskellはストリーミングライブラリ(conduitやstreaming)を活用することで、メモリ使用量を一定に保ちながら高速にデータを変換できます。
また、Haskellの純粋性は並列化を極めて安全にします。
parやpseqを使った戦略的並列性や、Control.Parallel.Strategiesを用いたデータ並列処理は、共有メモリの競合を意識せずに記述できるため、マルチコアCPUのリソースを効率的に活用できます。
Rubyの並列処理はプロセスベース(fork)やスレッド(Ractor)が主流ですが、グローバルインタプリタロック(GIL)の制約から、真の並列実行はプロセス間通信に頼らざるを得ず、オーバーヘッドが大きくなります。
混合ワークロードの現実
実際のシステムでは、I/Oと計算が混在するケースがほとんどです。
例えば、画像処理APIでは、リクエスト受信(I/O)→画像変換(計算)→結果保存(I/O)という流れになります。
このような場合、以下の判断基準が有効です。
- 計算部分が全体の20%未満であれば、開発効率を優先してRubyを選択
- 計算部分が50%以上を占め、かつ1リクエストあたりのCPU時間が100ミリ秒を超えるなら、Haskellを検討
- バッチ処理で1回のジョブが10分以上かかる場合は、Haskellへの移行で運用コストが削減できる可能性が高い
ワークロード別評価サマリ
| ワークロードの種類 | Rubyの適正度 | Haskellの適正度 | 推奨判断ポイント |
|---|---|---|---|
| CRUD系Webアプリ(I/O主体) | ★★★★★ | ★★☆☆☆ | エコシステムと開発生産性を優先 |
| リアルタイムチャット(高並行I/O) | ★★★☆☆ | ★★★★☆ | 同時接続数が1万超ならHaskellに有利 |
| 数値解析・機械学習前処理(計算主体) | ★☆☆☆☆ | ★★★★★ | 生速度とメモリ効率が決定的に異なる |
| バッチETL(ストリーミング処理) | ★★☆☆☆ | ★★★★☆ | 融合変換と正格制御でHaskellが活躍 |
| マイクロサービス(軽量API) | ★★★★☆ | ★★★☆☆ | チーム熟練度で判断、どちらも成立可能 |
この表から明らかなように、「どちらが優秀か」はワークロードに完全に依存します。
重要なのは、自社のシステムがどの種類の負荷に支配されているかを、プロファイリングやAPMツールで数値化したうえで判断することです。
そして、その数値が示すボトルネックが言語処理系にあるのか、それともデータベースやネットワークなどの外部要因にあるのかを切り分けることが、実用的な選定の第一歩となります。
次の章では、これらの技術的評価に加えて、チーム構成や運用コストといったヒューマン要素を考慮した総合的な実用性評価を行い、最終的な判断フレームワークを提示します。
チーム開発と運用コストから見る実用性の総合評価

これまでの章では、実行速度、メモリ管理、最適化手法、ワークロード適性といった技術的側面を中心に比較してきました。
しかし、実プロジェクトで言語を選定する際には、コードを書く人間の生産性や、運用フェーズでのトラブルシューティングの容易さが、しばしば生のパフォーマンスよりも重要になります。
ここでは、チーム開発と運用コストの観点から、RubyとHaskellの実用性を総合的に評価します。
開発生産性:習得曲線とコードの保守性
Rubyは、その可読性の高さと直感的な構文により、初心者からベテランまで幅広いエンジニアが短期間で生産性を発揮できます。
メタプログラミングやオープンクラスなどの強力な機能は使い方を誤るとカオスを生みますが、適切にガイドラインを定めれば、変更に対する耐性が非常に高いコードベースを構築できます。
また、Railsをはじめとするフルスタックフレームワークが、認証、ルーティング、ORM、アセットパイプラインなどを統合的に提供しているため、プロジェクト立ち上げ時の実装コストは極めて低くなります。
一方、Haskellは関数型プログラミングというパラダイム自体に学習コストがかかります。
モナド、アプリカティブ、型クラス、カインドといった抽象概念を理解し、さらに遅延評価と正格性の制御をマスターするまでには、数ヶ月から数年単位の経験が必要です。
しかし、一度その壁を越えれば、型システムが膨大なバグをコンパイル時に検出してくれるため、リファクタリングや機能追加の安全性はRubyを上回ります。
特に、大規模なドメインモデルを持つシステムでは、型による不変条件の表現が設計ドキュメントの役割も果たすという利点があります。
採用・育成コストとコミュニティの規模
日本国内のエンジニア市場において、Ruby経験者は非常に多く、特にWeb系企業では即戦力となる人材を確保しやすいです。
一方、Haskellの経験者は圧倒的に少なく、採用コストが高いだけでなく、万一の退職時に代替要員を迅速に補充できないリスクが常に伴います。
スタートアップや中小企業ではこのリスクが致命傷になり得るため、Haskellの採用は一定以上の組織規模や、社内育成に投資できる体制が整っている場合に限定されるべきでしょう。
コミュニティの充実度も無視できません。
RubyはQiitaやZenn、Stack Overflowにおける情報量が膨大で、ライブラリのドキュメントも日本語化されているものが多くあります。
Haskellは情報が英語圏に偏っており、日本語の解説記事はまだ限定的です。
また、OSSライブラリの数もRubyのほうが圧倒的に多く、特にビジネス系(決済連携、帳票出力、Excel処理など)のニッチな領域では、Rubyの方が選択肢が広いのが現実です。
運用監視と障害対応の難易度
実行時の運用面でも、両者には顕著な差があります。
Rubyアプリケーションは、標準的なAPMツール(New Relic, Datadog, Scout)との統合が容易で、リクエストごとのトレースやGC統計、メモリプロファイルを可視化できます。
また、エラーログにはスタックトレースに加えて変数の中身が出力されることが多く、デバッグが比較的直感的です。
Haskellの運用監視は、サンク評価の遅延やスペースリークが原因で、メモリ使用量が急増した場合でも、その根本原因を特定するのが難しいことがあります。
ヒーププロファイル(-hT)を取得しても、データ構造の名前がCore言語レベルのものに変換されているため、ソースコードとの対応付けに慣れが必要です。
また、GHCのランタイムシステム(RTS)は多様なフラグを提供していますが、その最適な組み合わせはアプリケーションごとに異なり、運用チームに高度な専門知識を強いることになります。
加えて、Haskellのデプロイバイナリは静的リンクされていても、依存ライブラリのバージョンアップに伴う再コンパイルが頻繁に発生します。
CabalやStackといったビルドツールは強力ですが、依存関係の解決に時間がかかることや、コンパイルに要するリソース(メモリ・CPU)が大きいことも、CI/CDパイプラインの設計に影響を与えます。
総合評価:コスト対効果のマトリックス
以下の表は、各評価軸における両言語の相対的な優劣をまとめたものです(★は5段階)
| 評価軸 | Ruby | Haskell | 備考 |
|---|---|---|---|
| 学習・習得のしやすさ | ★★★★★ | ★★☆☆☆ | Rubyは初心者向け資料が豊富 |
| コードの安全性(コンパイル時チェック) | ★★☆☆☆ | ★★★★★ | Haskellは型による保証が強力 |
| リファクタリングの容易さ | ★★★☆☆ | ★★★★★ | 型があるHaskellが圧倒的に安全 |
| ライブラリ・エコシステムの多様性 | ★★★★★ | ★★★☆☆ | ビジネス寄りライブラリはRubyが優勢 |
| 採用・人材確保のしやすさ | ★★★★★ | ★☆☆☆☆ | 国内のHaskellエンジニアは非常に少ない |
| 運用監視・デバッグの容易さ | ★★★★☆ | ★★☆☆☆ | RubyはAPM連携が成熟、Haskellは特殊知識が必要 |
| 障害発生時の平均復旧時間(MTTR) | ★★★★☆ | ★★☆☆☆ | スタックトレースの読みやすさでRubyに分がある |
| 総合的な開発速度(機能追加) | ★★★★★ | ★★★☆☆ | Rubyはフレームワークの恩恵が大きい |
結論としての実用性評価
このマトリックスから読み取れるように、Rubyは「人的コスト」を最適化する言語であり、Haskellは「技術的リスク」を最小化する言語だと言えます。
どちらが優れているかではなく、プロジェクトのフェーズやチームの成熟度に合わせて選択することが、持続可能なシステム開発の鍵です。
例えば、初期フェーズのスタートアップでは、Rubyのスピードと柔軟性で市場適合を素早く探るのが得策です。
一方、金融機関や基幹系システムのように、一度リリースしたら長期にわたって安定稼働が求められ、バグによる損害が甚大なケースでは、Haskellの静的保証と安全性に投資する価値が十分にあります。
最終的には、速度だけでなく、チームがその言語とどれだけ長く、幸せに付き合えるかが、本当の「実用性」を決めるのだと私は考えます。
次の最終章では、これらの議論を全てまとめ、プロジェクトに最適な言語を選ぶための具体的な判断フレームワークを提示します。
まとめ:速度だけでは測れない、プロジェクトに最適な言語の選び方

本記事を通じて、RubyとHaskellのパフォーマンスを実行モデル、型システム、ベンチマーク、メモリ管理、最適化手法、ワークロード特性、そしてチーム運用コストに至るまで多角的に比較してきました。
ここで改めて、最初の問い「RubyとHaskellのパフォーマンスはどっちが優秀か」に対して、私なりの最終回答を明確にしたいと思います。
結論から言えば、「優秀さ」を単一の指標で測ることは不可能であり、両言語はそれぞれ異なる「速さの価値観」を持っています。
Haskellは生の計算速度とメモリ効率において圧倒的優位を示す一方で、その真価を発揮するには高度な最適化知識と運用ノウハウが要求されます。
Rubyは言語処理系自体の速度では劣るものの、開発生産性とエコシステムの豊富さにより、人間の時間対効果で見れば多くの実務シーンでHaskellを凌駕します。
では、プロジェクトの現場でどのように選択すればよいのでしょうか。
私は以下の5段階の判断フレームワークを提案します。
- ステップ1:処理の性質を定量化する – システム全体のCPU時間とI/O待ち時間の割合をプロファイリングし、計算主体かI/O主体かを数値で把握します。計算が全体の30%未満であれば、言語処理系の速度差は相対化されます
- ステップ2:チームの既存スキルセットを評価する – 現在のメンバーがRuby経験者であればHaskellへの移行コストは大きく、逆にHaskellに強いコアチームがいるならその強みを活かすべきです。学習曲線をコストとして計上することを忘れないでください
- ステップ3:将来的な変更頻度を見積もる – 要件が流動的で頻繁にリファクタリングが発生するなら、静的型付けのHaskellが安全性を発揮します。一方、短期間でプロトタイプを繰り返すフェーズでは、Rubyの柔軟性が圧倒的に有利です
- ステップ4:障害時の許容コストを定義する – サービスダウンが収益に直結するクリティカルなシステムでは、Haskellの型保証と純粋性がバグの侵入を防ぐ盾になります。逆に、多少のダウンタイムが許容される社内ツールなどでは、Rubyの運用簡便さを優先できます
- ステップ5:エコシステムとライブラリの成熟度を確認する – 実装したい機能に特化したライブラリ(例:機械学習、帳票出力、決済連携)がどちらの言語で充実しているかを調査し、車輪の再発明を避けられるかを判断基準に加えます
このフレームワークに従えば、「パフォーマンスが良いから」という単純な理由で言語を選ぶリスクを大幅に低減できます。
実際、私自身が関わった複数のプロジェクトでは、当初Haskellを導入したものの、運用フェーズで人材不足に悩み、結果的にRubyに再実装したケースもあれば、逆にRubyで書いたバッチ処理が性能限界に達し、Haskellで書き直して10倍のスループットを得たケースもあります。
どちらの経験も、「言語の優劣」ではなく「プロジェクトの成熟度と要件の一致」が成功を分けたと強く実感しています。
また、両言語は決して排他的な選択肢ではありません。
マイクロサービスアーキテクチャでは、計算集約的なモジュールだけをHaskellで実装し、Webインターフェースや管理画面はRuby on Railsで構築するというハイブリッド戦略も十分に現実的です。
API境界を明確に定義すれば、各言語の強みを相互に補完し合うことが可能です。
最後に、私がコンピューターサイエンスの視点から最も伝えたいのは、パフォーマンスとは「システム全体のレスポンスタイム」ではなく「ビジネス価値の創出速度」まで含めた総体であるということです。
いくら高速なバイナリを生成できても、開発者がその言語に苦しみながら書いているようでは、全体としての速度はむしろ低下します。
逆に、開発者が楽しく生産的にコードを書ける環境こそが、長期的なプロジェクト成功への近道です。
RubyとHaskell、どちらを選んでも、その決断を支える明確な根拠と計測計画を持ってください。
そして、選んだ言語の特性を深く理解し、最適化は最後の手段として、まずはクリーンで読みやすいコードを書くことに注力してください。
それが結果的に、最も持続可能なパフォーマンスを実現する道だと、私は確信しています。


コメント