2013-04-02

現在のアセンブリの利用例

LEG/Engineering/OPTIM/Assembly - Linaro Wiki

Linux用のソフトウェアをARMアーキテクチャでサポートする目的で設立された非営利団体、Liaro.orgが、ソフトウェアパッケージのARM移植の調査のために、UbuntuとFedoraのレポジトリの全パッケージに検索をかけて、アセンブリの利用例を抽出した結果がまとめられている。

アセンブリの利用は、アセンブリのソースコードによくある拡張子と、その他のソースコードのインラインアセンブリを探すことで抽出された。さらに、抽出されたアセンブリ利用例に対して手動でその利用目的を調べてまとめている。

それによれば、アセンブリが使われているパッケージは1435件あったそうだ。

Ubuntuのパッケージだけで考えると、2万以上のソースパッケージの中の1200超のパッケージでアセンブリが使われており、利用率は約6%となる。

また、1435件のアセンブリを利用するパッケージについて、その利用目的を調べたところ、以下のように分類できた。もちろん、一つのパッケージで多目的に利用しているものもあるので、合計は100%以上になる。

  1. アトミック(10%) - メモリバリア、ロック、アトミックインクリメントやデクリメントなどの目的でアセンブリを使用。
  2. 埋め込みライブラリ由来(18.1%) - パッケージ自体に取り込んだライブラリ由来のアセンブリ(e.g. libjpegやgettext)
  3. 誤検出(11.1%) - スキャンでアセンブリだと誤って検出されたもの(.sで終わるファイル名)や、何らかの理由によりコメントアウトされたりなどして使われていないコード
  4. 低級(lowlevel)(38.1%) - 様々な低級(lowlevel)な目的のためのアセンブリコード、例えばレジスターやスタックやハードウェアポートなど、直接ハードウェアを操作するもの
  5. 他のOS用(9.3%) -  ホストであるFedoraやUbuntu以外の他のプラットフォームやOSをサポートするためのアセンブリで、移植作業には関係ないもの
  6. パフォーマンス(30.4%) - 何らかの方法でパフォーマンスの向上をはかる(実際に達成できているかどうかはともかく)とするアセンブリコード(e.g. マルチメディアのためのSIMDやループ内のコードのアセンブリによる置き換えなど)
  7. シンボルやセクション等(2.9%) - シンボルアクセスやELFセクションを直接いじるための"asm"の利用。

一部のパッケージは、ARMへの移植作業としては無視できるという。たとえば、アーキテクチャ独自のもの(ブートローダー)とか、一部のハードウェアを操作するもの(ディスプレイやグラフィックハードウェアの設定を行うもの)だ。

また、アセンブリの典型的な使われ方に着目したまとめもある。

バイトスワップやビット操作

多くのアセンブリが、単純なバイトスワップやビット操作を高速化するためにアセンブリを利用している。これは、今のコンパイラーならばたやすく最適化できるものである。このような目的にアセンブリが使われているのは、プログラマーの不理解や、当時の貧弱なコンパイラーへの対処のためである。もはやこの手のアセンブリ利用は必要なく、また時には、優秀なコンパイラーより遅くなることすらありえる。

ハードウェアの機能検出

例えばCPUIDなど、ハードウェアやそのサポートされている機能の検出目的でアセンブリが使われている。多くのソフトウェアパッケージが、独自にアセンブリを書いてハードウェアの検出を行なっている。これは恥ずべき現状である。このような処理には共通のライブラリが存在するべきだし、実際Linuxカーネルに検出機能があり、glibcを経由して提供されてもいるが、どういったわけか(移植性のためか?)、多くのソフトウェアパッケージは、共通のライブラリを使わずに自前実装している。

タイマーアクセス

RDTSCやHPETのような高精度のハードウェアタイマーにアクセスするためにアセンブリを使っている。主な利用はベンチマークだ。これもやはり自前でアセンブリを書くより、共通のライブラリにまかせるべき処理ではないかと思う。実際、POSIXにもハードウェアタイマーをラッパしたライブラリが提供されているのだが。

本の虫: 100ナノ秒ぐらいの分解能をもつクロック実装

SIMD

マルチメディア処理はパフォーマンスのためにSIMD演算を多用する。元記事では、compiler intrinsicを使うべきだとしているが、やはりcompiler intrinsicでカバーできるのは一部だけであり、動画屋は手動でカリカリに最適化したアセンブリを好むようだ。まあ、これはSIMDアーキテクチャとコンパイラー技術の改良が次第に状況を変えていくのではないかと思う。

atomic

多くのソフトウェアパッケージが、atomic操作のために自前でアセンブリを書いている。たとえ低級なatomic操作であっても、アセンブリを手書きするのではなく、compiler intrinsicやライブラリを使うべきである。

浮動小数点数の操作

驚くほど多くのパッケージが、浮動小数点数ユニットの演算精度や丸めモードなどを設定するために、x86アセンブリを直接手書きしている。これはx86以外のソフトウェアにはあまりみられない傾向だ。これこそライブラリで行うべき処理だ。C99にはFPUの挙動を設定するためのライブラリが追加されたが、どうやらC99にはなかなか移行できないようだ。

埋め込みライブラリ由来

ライブラリをライブラリとして参照するのではなく、ライブラリのソースコードを直接取り込むことによって、そのライブラリ由来のアセンブリコードまで取り込んだソフトウェアパッケージがかなりある。ライブラリのソースコードを取り込む理由は、

  1. パッケージをビルドしやすくする。
  2. ライブラリの特定のバージョンの依存したコード書いたり、ライブラリを改造して使う

といったところだ。一つ目の理由はそれほど重要ではない。大多数のユーザーは、ディストリビューターによってビルドされてパッケージ化されたものを使うからだ。ディストリビューターは、ライブラリの正しいビルド方法や依存関係の解決ができるだけのスキルを持っている。二つ目はそう簡単に解決できない。

ただし、ライブラリのコピーを内部で持つのは、様々な理由からおすすめできない。手動で本家のアップデートを追わなければならないのが一番の理由か。

もっとも頻繁に埋め込まれているライブラリもまとめている。

  • gnulib - これは誤検出ともいえる。本来ソースコードレベルでコピーして埋め込む設計のライブラリだから、設計通りと言える。その設計が正しいのかどうかはともかく。
  • gettext - これも基本的には誤検出だ。asmを使ってcygwin環境でシンボルのエクスポートを操作している
  • libgc - the Boehm garbage collection library
  • zlib - compression library
  • libjpeg - support for the JPEG graphics format

多くの例で、libgcはただ埋め込まれているだけではなく、目的に応じて改変されており、本家の最新版に追いついていくのを難しくしている。zlibはstableなABI/API互換を提供しているが、セキュリティホールの発見でたびたびアップデートされている。埋め込まれたzlibは正しくアップデートされにくい。

元記事では、最近の良い傾向として、多くの開発者がcompiler intrinsicなどの機能の存在を知っており、そのような機能を使おうとしていることを挙げている。これは移植を簡単にする。

悪い傾向としては、多くのパッケージで、本人が書いたのではなく、どこか他所からコピペしてきたらしきアセンブリのコードが散見されることだ。また、ライブラリの埋め込み利用も多数ある。これは移植を難しくする。

また、多くのアセンブリには、例えば以下のようなコメントが付属している。

「gcc 2.7のこのコードの最適化には誤りがある」

何十年も昔のコードならば許せもするが、適切な保守作業が行われていない証拠でもある。

10件のパッケージに、VAX用のアセンブリコードが発見された。いくつかは、どうやら当初VAXアセンブリで書かれて、その後、超最先端(new-fangled)のSunのワークステーションのためにCで移植されたらしい。1980年代のことだ。

多くのソフトウェアに、使われないまま眠っているアセンブリコードがある。書かれたものの正しく動かなかったのか、もはや必要がなくなったのか、いろいろな憶測が立つ。典型的な例は、あるIRCクライアントに含まれる、手で書かれてカリカリにチューニングされているが、全部コメントアウトされている、文字列処理用のアセンブリのサブルーチンだ。

その上で、ARMに移植するべきパッケージを優先度付けしている。

元記事はこの調査の上で、我々はアセンブリの悪用の例をドキュメント化し、まとめて、正しく教育する必要があるとしている。共通のライブラリがある場合はその存在を教育する。また、もっとコンパイラーを信頼するように教育しなければならない。近年のコンパイラーの技術の向上により、従来の多くのアセンブリ利用は、もはや不要になっているのだ。また、ソフトウェアの直接の開発者にも連絡して、不適切なアセンブリ利用をやめるよう説得するべきだ。

結論として、ほとんどのパッケージで、ARMのための移植作業は必要ない。Ubuntuのレポジトリで言えば、アセンブリを利用しているパッケージは、全体のたったの6%だからだ。アセンブリを利用している多くのパッケージも、そのまま動く。移植性の高い高級なコードでの実装とパフォーマンス目的のアセンブリ実装を切り替えるようになっていたりするからだ。また、ほとんどのアセンブリ利用は、それほど価値がない。本来アセンブリで書かなくてもいいような処理までアセンブリで書いていたりするからだ。

No comments: