その1バイトの節約には意味があるのか?
8ビットな時代はコンピュータの持っているリソースが極めて少なく、何らかの処理をするにも、その1バイト1バイトが貴重なものでした。アセンブラでコードを書くのであれば、1バイトでも短いコードで書くためにいろいろな工夫をしたものです。それには切実な理由もあって条件分岐が前後128バイトにしか飛べないとか、焼き込むROMの容量がギリギリなどがありました。当然データに関しても無駄な領域を作らないようにアレコレ考えたものです。
16ビット時代になるとメモリ事情はだいぶ改善されて、そこまで頑張らなくなりました。もちろんもうアセンブラを使うことも滅多になくなりましたし、どちらかというとC言語で最適化が充分に働くように配慮すれば良いのかなという程度になりました。そしてデータに関してはメモリのアライメントという問題が大切になりました。16ビットCPUは原則として16ビット、すなわち2バイト単位でメモリを読み書きします(68000に至っては奇数アドレスにアクセスできないし、それ以外でもバイトアクセスにはアクセス時間のペナルティがある)。これが理由で配列や構造体などには(コンパイルオプションで制御できますが)パディングと呼ばれる隙間が挿入される(ことがある)ようになりました。
ここから最適化は常にメモリのサイズを取るかパフォーマンスを取るかの選択を迫られるようになります。少しくらいメモリの使用量が増えても気にしなくて良いとはいえ、大きな配列ではやはり影響は大きいですし、繰り返し処理に関してはコードがキャッシュに乗りきるのかという心配もあります(出来ればループ内でメインメモリにアクセスしたくない)。
これが32ビット、そして64ビット時代になるとアライメント問題は顕著になります。もちろん一連のメモリはメインメモリにあったとしても一括してキャッシュには読み取られるのですが、そこへのアクセスは基本的にCPUのビット幅を単位でやりとりされます。ですからデータは(64ビットCPUであれば)64ビット、すなわち8バイト毎にアクセスするのが効率が良いわけです。最近はキャッシュサイズも大きく複雑になり、たとえコードやデータが1次キャッシュに乗り切らなかったとしても2次以降のキャッシュには入りますし、どちらかというと仮想メモリのページサイズに収まるかどうかのほうが気になるようになります。
そしてメモリアクセスのコストの方が高くCPUの能力が余裕のある状況になったので、アルゴリズムを工夫して8バイト単位で一気に処理をしたり、メモリ上のデータは圧縮した状態にしてレジスタまたはキャッシュ上で展開したほうがパフォーマンスが高くなることすら出てきました(ひとつのアドレスページにより多く詰め込んだ方が良い)。
そういえば昔も中身が殆どゼロでいくつかの要素だけに値があるような行列演算をゼロの要素を除いた表現に変換してメモリ使用量を減らし演算回数も少なくする工夫を良くしていました。また文字列処理に関してもバイト単位で行うのではなく、8バイト単位で文字列をコピーしたり検索したりする工夫が標準ライブラリのコードに見ることができます。
スタック上に領域を確保するローカル変数にしても基本的にメモリアライメントの単位で割り当てられますし、動的メモリを確保する malloc(や、それを使っている new 演算子) も比較的大きな単位で確保してからメモリアライメントの単位で要求されたメモリを割り当てています。ですから実際のメモリを眺めると結構隙間だらけの使い方をしています。
古い人間は未だにメモリをギッチリ使わないとなんだか勿体ない思いが消えないのですが、無理をしても却ってパフォーマンスが悪くなるかもしれませんし、キャッシュや仮想メモリもあるので、あまり頑張っても効果は限定的です。どちらかというとデータを圧縮表現したほうがサイズを減らせますし、キャッシュが効きやすいように連続してアクセスするアドレス領域を集めるような配慮のほうが効果が高いです。実装のレイヤであればコンパイラや、それこそAIの力技のほうが人間がゴリゴリ書くよりよほど安全で正確です。でも、そもそものアルゴリズムは人間のほうが広い視野を持てるので、大きな視点で考えるように工夫を考えたいものです。
もちろん組み込みの世界では未だにメモリの制約が厳しいこともありますし、正確な使用量や実行速度が計測できる必要があったりもしますけど、あまり人間がゴリゴリ書くのは限られた状況に留めたほうが良くなってきていると思います。そしてソースをコンパイルしてインストールするよりも、ソースコードをその時点でのリソース状況に応じてJITのようにその場で必要な部分のみコンパイルして実行したほうがトータルの意味で効率的になるでしょうし、今後はプロンプトをAIが解釈して実行させるほうが、それまでに蓄積した知識をフルに使えるので最終的にはよりよい結果が得られるようになるかもしれません。もっとも最適化と同じで、このコードを実行しても意味が無いと判断されてしまうという笑えない話になるかもしれませんが。
ヘッダ画像は、この記事の内容をGeminiを使ってアレコレ加工してから説明する画像を生成してもらいました。
#メモリ最適化 #節約と効率 #メモリアライメント #アクセス効率 #メモリ圧縮 #アルゴリズム #メモリキャッシュ #ページング #パディング
いいなと思ったら応援しよう!
頂いたチップは記事を書くための資料を揃えるために使わせていただきます!