C言語は低水準言語ではない:あなたのコンピュータは高速なPDP-11ではない
C Is Not a Low-Level Language (2018)
MeltdownやSpectreの脆弱性の根本原因は、プロセッサ設計者がCプログラマの幻想を維持するために複雑な機能を追加してきたことにある。C言語の抽象機械はPDP-11のモデルを反映しているが、現代のプロセッサは投機的実行や複雑なキャッシュ階層を持ち、もはやCのモデルとはかけ離れている。Cコードを高速に実行するには、コンパイラの高度な最適化(ベクトル化やループ変換など)が必要であり、それはCのメモリレイアウト保証や未定義動作と衝突することもある。C言語はもはや低水準言語ではなく、プログラマに誤った安心感を与えていると論じる。
C言語の実装は、Cが基盤ハードウェアに容易に対応し、高速なコードを生成するという幻想を維持するために、ますます複雑にならざるを得なくなった。
HNでの議論
99- weitendorf
これは私見では非常に衒学的な指摘だと思います。C言語が低水準であるのは、機械語やアセンブリと容易に連携でき、こういったことを簡単にできるからです(以下はLLM支援の例):
int main() {
__m512i vecA = _mm512_setr_epi32(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15);
__m512i vecB = _mm512_setr_epi32(0,5,10,15,20,25,30,35,40,45,50,55,60,65,70,75);
unsigned short mask = 0;
__asm__ (
"vp2intersectd %[B], %[A], %%k2"
: "=@cck2" (mask)
: [A] "v" (vecA), [B] "v" (vecB)
: "k3"
);
printf("Intersection Mask: 0x%04X\n", mask);
return 0;
}
これは「低水準」プログラマが、高水準言語の利点を活かしつつ、特定のハードウェア命令(高度に最適化された検索アルゴリズムの実装で使われるAVX-512命令vp2intersectd)を明示的に制御するために、非常に頻繁に利用するものです。
もちろん、コンパイラの暗黙の動作に頼ってコードを最適化するなら、それはもはや「低水準」ではありません。しかし、機械レベルの命令に迅速かつ容易に落とし込んで明示的な実装の意味論を提供でき、しかもその言語がそれを比較的簡単に実現できるなら、それは私には確かに「低水準」に思えます。
- bee_rider
「低水準言語」という用語は、「VLSI(超大規模集積回路)」のようなもので、1970年代に定義されたため、学術的な定義が一般の人の期待とずれています。
これは問題ありません。専門用語であり、すぐに明白である必要はありません。
しかし、その理由でこの記事のタイトルは好きではありません。実際、より適切なタイトルは「現代のx86プロセッサはPDP-11ではない」のようなものでしょう。サブタイトルはほぼ完璧です。
追記:また、この件でC言語を責めるのは公平ではないと思います。問題は本当に低水準性にあるわけではありません。投機的実行の複雑さを実際にさらけ出す言語は、かなり高水準になり得るでしょう。ただ、線形のテキストエディタで読むのは難しくなるでしょう?依存関係グラフなどを描く方が良いでしょう。
- legobmw99
私はこの記事を何年も気に入っていますが、現代のスーパースカラCPU向けの真の低水準言語は実際には存在しないのではないかとよく考えさせられます。どなたかご存知ですか?
- melodyogonna
まあ、C言語は重要な場面で制御を与えてくれます。たとえそれがより現代的な機能に対して後付けのように感じられても。
私は実際、MojoがPDP-11を前提にしないように設計された唯一の現代言語だと思っています。C言語が非常に成功したため、後続の多くの言語は当然のようにC言語的なことを行ってきました。
Mojoでは、すべてが現代のコンピュータの複雑さを念頭に設計されており、どの段階でもプログラマは結果を完全に制御できます。何をインライン化するか、何をレジスタで渡すか、何をアンロールするかなどを決定します。この言語は優れた…いや、おそらく最高のポータブルSIMDサポートを備えています。すべての整数はSIMD上に構築され、スカラー整数は長さ1のSIMDにすぎません。また、Zigのものに似ていますが、より強力な(私見では、より多くの情報を提供できるため)コンパイル時プログラミングにより、何がコンパイルされるかを完全に制御できます。C言語やRustがインラインアセンブリを許可する一方で、Mojoはさらに進んでインラインMLIRやLLVMを提供するため、必要に応じてコンパイラに特定のLLVM組み込み関数にコンパイルするよう指示できます。また、この言語は単一のマシンで実行することを前提としていません。現代のすべてのコンピュータは本質的にヘテロジニアスであり、複数のプログラマブルユニットを含む可能性があるため、コンパイルパイプラインは、コードの一部をあるターゲット用に、他の部分を別のターゲット用にコンパイルできるように設計されています... 単一のコンパイル単位として。
- glouwbug
当時はそうでなかったかもしれませんが、今では基本的に独自の貧者のテンプレートシステムがあります:
#define array(T, N) struct array##T##N { T value[N]; }
void copy(array(int, 32)* x, array(int, 32)* y) {
*x = *y;
}
int main() {
array(int, 32) x;
array(int, 32) y = { 1, 2, 3, 4 };
copy(&x, &y);
}
(噂では)ラムダとdeferが追加される予定で、C言語はクラシックWoWの道を歩んでいます。