2009の派生記事だ。この記事のフォーカスはUPXにあるので、wxRubyとExerbについて知りたい方はそちらの記事を参照のこと。以下、そちらの記事の知識があることを前提として書いている。World!レベルの.exeを単純にUPXで圧縮しても、2.4MiB程度にはなってしまう。複数の.exeを配布したい場合、このサイズはバカにならない。なんとかしてもう少し小さくできないだろうか。注:複数の.exeを作成しない場合でもこの節を最後まで読んで見て欲しい。ExerbのコアをDLLで利用するExerbでは、その機能コアをDLLで使用することができる。複数の.exeを作成する場合、コア部分は皆同じなので、このDLLを1つ同梱するようにすれば合計サイズを節約することが可能だ。コアにDLLを使用する場合、exerbコマンドで以下のようにする。app.exyでOKだ。Files\ruby-1.8\share\exerbにあるexerb50.dll(圧縮前で584KiB)が実行時に必要になる。wxRubyをタイトルに標榜するこの記事では、これぐらいでは焼け石に水だ。wxruby2.soが9852KiBとバカでかいので、むしろこっちをなんとかしなければならない。extension-libraryとコメントアウトし、後は同じようにexerbコマンドを実行すればよい。この場合には、実行時に上記exerb50.dllの他にwxruby2.soが必要になる。これによって上記10.3MiBあった実行ファイルは208KiBまでになった。UPXを使ってみよう。--lzma"で.exe本体が52KiBになった。exerb50.dllとwxruby2.soも圧縮可能で、それぞれ238KiBと1749KiBまで小さくなった。.exe、exerb50.dll、wxruby2.soの合計サイズは約2039KiBになる。...ちょっと待てよ。分離圧縮のススメ筆者は元記事の「おまけ1」で「--lzmaオプションを使用すると2461KiB」と書いた。アプリケーションはこの時と全く同じものだが、ExerbとwxRubyのコアをDLLで使用し、それぞれ別にUPXで圧縮しただけで約422KiBも小さくなっている。これはどういうことだろう。ここで、ふと思い出した。UPXで"--lzma"オプションを指定して実行ファイルを圧縮した場合、同じアルゴリズムを使用する(というよりこちらが本家である)7-zipで圧縮した場合よりもサイズが小さくなることがある。これは恐らく、UPXが実行ファイル(.exe)に特化し命令コードを整理する前処理を行っており、これが上手く効くケースでは7-zipよりも効率よく圧縮できることがその原因だ。wxruby2.soを実行ファイル(.exe)に含めた場合、wxruby2.soは「データ」として含まれることになる。この場合UPXはここを「命令コード」とはみなさないため、汎用的な圧縮しかなされないのだろう。wxruby2.soをUPXで圧縮した場合、その実体はDLLであり「命令コード」として圧縮されるため、UPXの前処理が効いてより小さくなる可能性がある。実際にwxruby2.soを7-zipで圧縮すると、2224KiBになり、UPXで圧縮した1749KiBとは約475KiBの差がある。細かな事情による誤差を考えれば、上記約422KiBの差はwxruby2.soにUPXの前処理が効いた結果と言ってよいだろう。蛇足だが7-zipにも実行ファイルを意識した前処理(BCJ)は存在する。7-zipは標準でこの処理を拡張子.soのファイルに適用しない。wxruby2.soをwxruby2_so.dllとリネームすると、BCJが効くようになり標準圧縮で1904KiB、超圧縮で1842KiBまで縮むが、上記の通りwxruby2.soにはUPXの前処理の方が効くようだ。結論wxRuby2を使用するRubyスクリプトをExerbで.exe化する場合には、作成する実行ファイル(.exe)が1つだけの場合でも、wxruby2.soは外に出し別途UPXで圧縮しよう。このほうがwxRuby2.soにUPX圧縮が良く効くため、トータルでサイズが小さくなる。実行ファイル(.exe)が複数の場合にはExerbのコアもDLL版を使用し、exerb50.dllを別途UPXで圧縮するとよい。実行ファイルが1つだけの場合にExerbのコアをDLL版で使用しないのは、UPXの展開コードが.exeや.dll/.soのファイルごとについてしまい、重複してしまい冗長であるためだ。DLLに分離した場合のDLL読み込みコードも省略されるので、元のデータ自体が小さいもので済む。実際、DLL版で完成した.exeとexerb50.dllをUPXにかけた合計サイズは約290KiBだが、スタティックリンク版のExerbコアを使用してUPXで圧縮した場合には251KiBとなり、約31KiB小さい。Exerbのコア自体はスタティック版でもUPXの前処理の対象になるはずだが、後述のサイズ一覧にある7-zipでの圧縮結果を見る限りあまり効果が現れない(相性がよくない)ようだ。注意点この方法を使用する場合に、1つ注意点がある。Exerbの制限かUPXの制限か不明だが、実行ファイル(.exe)のファイル名が長いとrequireに失敗し、Ruby上の例外を発生することがあるようだ。試した限りでは、"12345678901234.exe"だと失敗するが、"1234567890123.exe"では失敗しない。ファイル名のベース部分が13byteまでという制限なのか、ファイル名全体で17byteまでという制限なのかは不明だ。詳細を追求しておくと、上位フォルダの名前を変更し絶対パス長を長くしてもエラーとはならないため、ファイル名のみにかかる制限だと思われる。"123456789012猫.exe"では失敗するが、"12345678901猫.exe"では失敗しないことから、Unicode(UTF-16)での文字数ではなくShift_JIS(CP932)でのバイト数によるものと思われる。いわゆるShift_JIS(CP932)のダメ文字を実行ファイル名に使用すると、また別のエラーが起動時に出る。これらのことから、恐らくUTF-8でもなく、Shift_JIS(CP932)表現での問題点なのだと思われる。以下に、この記事で扱った実行ファイル・DLLのサイズの一覧を挙げておく。7-zipの標準圧縮レベルと、UPXでの"--lzma"に付加するレベルの対応は良く分からなかった。"-5"に相当するのかと思っていたのだが、試した限りでは"--lzma"指定時は"-3"と"-5"と"-7"は同じサイズになった。バイナリを比較すると2バイトほど違いはあるのだが、使用されるアルゴリズムは一緒なのだろう。"-1"と"-2"と"-3"は結果が異なるので、"--lzma"では3段階しか選択できないということだろうか。--lzma"でも同じだと思われる。1,886,011主な合計サイズの比較は以下の通り。表をよく見ればわかるが、実行ファイルを圧縮する場合に、UPXが常に7-zipより小さくなるわけではない。UPXの本来の利点は「そのまま実行できる状態で小さくなる」点にあることをお忘れなく。一応、7-zipの方が小さくなる理由として考えられるものは以下の通り。UPXでは自己展開コードが付く。UPXでは実行ファイルの体裁を保つため圧縮できない領域がある。UPXの前処理が効きにくい実行ファイルも存在し、相性がある。7-zipにも命令コード用の前処理(BCJ)は存在し、相性がある。LZMAの内部的オプションの使い方は、本家である7-zipの方が上手いはず。Perl5.010001。