本当のタイトルは「AviUtlなどがメモリを多く確保するために使っているLargeAddressAwareを試してみた」だった。
Windowsのフリー動画編集ソフトの定番「AviUtl」は多くの日本人ユーザーが利用しています。
ニコニコ動画やYouTubeなどに投稿される実況動画はほとんどがこのソフトを使っているのではないかと思います。
AviUtlの特徴は、無料で拡張機能を追加すれば多くの機能が利用できるという点にあります。
Adobe Premireなどはそれ単体で非常に高価ですし、拡張機能も有料でかなりのお値段がするものもあります。
このようにメリットの多いAviUtlですが、すでに更新が終了しています。
しかも32bitソフトウェアなのでメモリを最大2GBまでしか使用することができません。
最近の動画は高画質なものが増えてきており、2GBではメモリは全然足りません。
そこで、AviUtlにはメモリを2GB以上確保するための機能としてLargeAddressAwareを使用しています。
今回はこのLargeAddressAwareについて簡単に紹介します。
- そもそも32bitと64bitとは
- なぜ32bitはメモリが2GBまでなの?
- お前メモリ2GBしかないの?俺メモリ128GB積んでるけど?
- 2GBの壁を超えるLargeAddressAware
- 実際に試してみる
- AviUtlの場合は?
- あとがき
そもそも32bitと64bitとは
コンピューターはデータを有限の桁で表現します。
最初から有限である値(整数など)はそれだけの桁を用意すれば完璧に表現することができますが、無理数(円周率やネイピア数など)は永遠に数字が続いていくので表現し切ることはできません。
また、果てしなく長い有限の値も表現できません。なぜなら、それらの値を記憶しておくためのスペースが用意できないからです。
コンピューターが計算をするとき、計算を行うCPUはデータを自分の近くに用意します。メモリというたくさんデータがあるところからレジスタという近場に持ってくるわけです。
メモリはたくさんデータを記憶できる半面、CPUから半端なく遠い*1*2上にデータを取り出すのに時間がかかります。
コンピューターは超高速で計算し続けているので、人間にとっては全然遠くない距離でもとても遠くになってしまいます。そこで、CPUの内側にあるとても早くデータを取り出すことができるレジスタにデータを一旦記憶させておくのです。こうすることで高速に計算することができます。
さて、コンピューターの中には様々なデータのサイズに決まりがあります。
それは1回の処理の命令の長さであったり、1回の受信/送信で受け取る/送ることが可能な大きさであったりです。これらを個別にサイズを決めていくと、大小様々なサイズのデータを入れる場所を用意する必要があります。
それでは大変なので、「大体のデータのサイズはこれぐらいにしよう!」という大まかなルールができました。その暗黙のルールの一つが今回説明する32bitと64bitに関わります。
10年ぐらい前まで、この大まかなサイズは32bit(4バイト)でした。これはCPUを作る会社がようやく作ることのできたレジスタの大きさで、これによってそれ以前のコンピューターよりも一度に多くのデータをやり取りすることができるようになりました。*3最近では更に大型化し、64bit(8バイト)まで扱うことができるようになっています。
これって実はすごい進化なんですよ!*4ではどういうふうにメモリの制限に関わってくるのでしょうか。
なぜ32bitはメモリが2GBまでなの?
さて、前の項で32bit/64bitが大まかなデータのサイズの決まりということはお話しましたね。32bitのコンピューターがメモリを2GBしか扱えないのは、Windowsがメモリを使う方法が原因です。
まず、32bitのコンピューターでメモリを扱うとなると、32bitの長さのアドレス欄にアドレスを書いて、そのアドレスに対応するメモリの場所にアクセスするという方法をとります。これは普通の方法です。
ここで32bitのアドレス欄は2進数*5で表されているので、2の32乗個のアドレスを書き分けることができます。
2の32乗は4294967296。1バイトにつき1アドレス対応させるとしたら、大体4.2GBぐらい対応させることができます。ということでメモリは4.2GBまで使えることになりますね。
なのにソフトウェアは2GBしか使えません。どうして。
それはWindowsの仕様が原因です。
OSのOSによるOSのためのメモリ領域
Windowsに限らず、OSはプログラムです。なのでメモリが必要です。
しかし、OSに与えられるメモリの量が少ないとコンピュータ全体が不安定になります。
dic.nicovideo.jp
そこで、OSにはガッツリと固定でメモリを与えておくことでどっしり構えてもらおうという作戦に出ました。
その代わりユーザーのプログラムのメモリ量は2GBまでです。仕方ないね。
お前メモリ2GBしかないの?俺メモリ128GB積んでるけど?
最近のコンピュータはハードウェアの進歩のおかげでどんどん性能が上がりました。
おかげでユーザーはメモリをじゃぶじゃぶ追加できるぐらいになっています。
今ではほとんどのユーザーがメモリ4GB以上あるんじゃないでしょうか。
ちなみに章タイトルのメモリはメモリ・ストレージのことであってメモリのことではありません。
こんな事言うのはスマートフォンしか持っていない人やコンピュータをよくわかってない人ぐらいでしょう。
たまに本当にコンピュータに128GBメモリを積んでいる人がいますがその人は変態なので避けましょう。
ちなみに上で述べたように32bitのコンピュータで4GBを超える量のメモリは意味がありません。
2GBの壁を超えるLargeAddressAware
メモリを沢山使えるようにするにはまず64bitのコンピュータである必要があります。大体これはWindowsが64bit版であるだけでOKです。
ただし、32bit時代に作られたソフトウェアは、コンピュータが32bitで動いていると思いこんでいるのでメモリを2GB以上使おうとしません。
そこで、32bitのソフトウェアがメモリをもっと扱えるようにしようとMicrosoftは考えました。
それがLargeAddressAwareオプションです。
これを有効にすると、32bitのWindows上ではメモリが3GBまで、64bitのWindowsの上ではメモリが4GBまで使えるようになります。
先ほど説明したOSのメモリ取り分を減らしてやることでよりたくさんのメモリを使えるようにしたわけです。
このオプションはプログラムのある位置を少し改変するだけで有効にでき、また行儀のいいソフトウェアは内部の変更を行う必要がないので手軽で強力です。
実際に試してみる
じゃあ実際にやってみないとわからないということで、次のようなプログラムを用意してみました。
include <iostream> int main() { int size = 10; int count = 0; while (true) { void* p = malloc(size * 1024 * 1024); if (p == NULL) break; count++; } std::cout << "Memory size is " << count * size << "MB"; }
参考:64bit Windowsを前提とした32bitアプリケーション延命法 ~ LAAオプションで32bitアプリケーションのメモリ不足問題を解消 | OPTPiX Labs Blog
これをx86*6をターゲットにビルドしてみると、次のようなメッセージが出ます。
Memory size is 1980MB
32bitなのでちゃんと2GB以下しかメモリを取得できていません。
正確に2GBではないのは、DLLなど他のライブラリがメモリを持っていってるからでしょう。
ではここでバイナリエディタを使ってLargeAddressAwareの設定に当たる部分をいじってみます。
Windowsでよく使われるStirlingで今のプログラムを開いてみると、このような感じになっています。
ここで、アドレス位置0x3Cから0x3Fを見てみると、F0 00 00 00という値が入っています。
アドレス位置の見方は、左が上位アドレス、上が下位のアドレスで、左に00000030、上に0Cと書いてあればそこは0x3Cになります。
0xと先頭についているのは、その数が16進数で表されていることを示します。
さて、0x3Cから0x3Fまでに並んでいた4つの値を、右から読んでいきます。いわゆるリトルエンディアンです。
リトルエンディアンは、下位のビットを上から順番に書いていきます。なので読むときは逆に下から読む必要があるわけです。
F0 00 00 00ということは、逆から読めば00 00 00 F0になるので0x000000F0が探しているアドレスです。*7
このアドレスを0x16進めた先がLargeAddressAwareのオプションが書かれるアドレスです。*8
ここにはすでに値が入っています。今回は0x02と入っていますね。LargeAddressAwareを有効にするには、すでに入っている値に対して0x20を論理和すればOKです。
論理和は早い話が16進数での足し算のようなものです。桁上りしないので要注意!(詳しくは調べて)
https://wa3.i-3-i.info/word11663.htmlwa3.i-3-i.info
この場合だと、0x02 OR 0x20なので、0x22が新しい値になります。
Stirlingで該当箇所を0x02から0x22に書き換えて保存しましょう。
そして再度実行してみると次のようになりました。
Memory size is 4030MB
ちゃんとメモリが4GB近くまで使えています。
AviUtlの場合は?
ちなみに、AviUtlの場合はこれをAviUtl自身で行っています。
管理者権限でAviUtlを起動し、設定からLargeAddressAwareを有効にする
にチェックを入れて再起動すると自分で該当箇所を書き換えてくれます。賢いですね。
あとがき
この記事は途中で書く気が起きなくなってしまったのでしばらく放置していたんですが、そうしたらAviUtlの作者KENくんがもっといい感じにメモリを使えるように対応したバージョンを公開して「出遅れたぁ~!」な感じです。
ただ32bitアプリでも4GB以上のメモリを活用することは出来るので、その対応をしたテスト版を公開してみました。
— KENくん (@__kenkun) 2019年8月18日
映像キャッシュを共有メモリで確保するようにしただけなのですがビルド環境が変わっている等ありますのでひとまずテスト版としています。
現場からは以上です。