EXE を作るプロジェクトのデフォルトが Any CPU から x86 に変わった理由 - または Any CPU の本当の意味

Visual Studio 2010 では EXE を作るプロジェクトのデフォルトが Any CPU から x86 に変わります。また、DLL を作るプロジェクトは Any CPU のままです。これらの理由を説明している記事を見つけました。

AnyCPU Exes are usually more trouble than they're worth - Rick Byers

良い記事なのでかいつまんで勝手に訳してみます。だいぶはしょっているいいかげんな訳なので、できれば原文も読んでください。それと、訳についてアドバイスがもらえるとうれしいです。

勝手訳

AnyCPU Exes are usually more trouble than they're worth

AnyCPU EXE は通常、価値よりもトラブルのほうが多い

私は過去数ヶ月にわたってここ(と一部の顧客)の人々と「AnyCPU」(アーキテクチャ中立)のマネージ EXE のコスト/利点のトレードオフについて興味深い議論をしました。それはほとんどあなたが望むものではなく、そして Visual Studio のデフォルトであってはならないという合意に達したと思います。この話題が一部の人々の興味(そして、ショックさえ)を引くかもしれないので、この根拠を共有しようと思いました。

Background - .NET and 64-bit

背景 - .NET と64ビット

Win64(64ビットバージョンの Windows)によって PE ファイル(EXE と DLL)は32ビット「または」64ビットとマークできるようになりました。32ビット EXE は Win64 上では、プロセスに32ビットオペレーティングシステムの幻想を見せる「WOW(Windows32 on Windows64)」の中で走ります。通常、32ビット DLL は32ビットプロセスだけにロードでき、64ビット DLL は64ビットプロセスだけにロードできます。CLR2.0 で64ビットサポートを加えたとき、マネージバイナリは難しい CPU 依存がなかったので32ビットと64ビットのどちらでも使えるようにしました。私たちは人々に32ビットと64ビットの両方のプロセスから再利用できる .NET ライブラリを書くことができて欲しかったので、Windows の OS ローダーサポートを拡張してアーキテクチャ中立(AnyCPU)な PE ファイルを使用可能にしました。
マネージのアーキテクチャ中立 DLL は32ビットと64ビットのどちらのプロセスにもロードでき、正常に動きます。AnyCPU EXE は64ビット OS では(ldr64 が何か言わなければ)64ビットプロセスとして動き、32ビット OS では32ビットプロセスとして動きます。Visual Studio 2008 では AnyCPU が C#VB プロジェクトのデフォルトプラットフォームです。これはデフォルトであなたがコンパイルするアプリケーションが32ビット OS では32ビットプロセスとして、64ビット OS では64ビットプロセスとして動くことを意味します。これはすばらしいことで、確かにたいていうまくいきます。しかし、ちょっと不利な面がいくつかあります。

The costs of architecture-neutral EXEs

アーキテクチャ中立 EXE のコスト

AnyCPU が EXE のデフォルトであってはならないと考える理由がいくつかあります。誤解しないで欲しいのですが64ビットハードと OS は間違いなくよいものです。しかし、それが大部分のプロセスが64ビットでなければならないことを必ずしも意味するわけではありません。私が Visual Studio の EXE プロジェクトのデフォルトを x86 にすることを正当化するために、議論で使ったリストはこれです。


1. 2つの非常に異なるモードで動作することは、製品の複雑さとテストのコストを上げます

しばしば人々はアーキテクチャ中立アセンブリのネイティブインタロップの意味に気づいていません。それは、あなたが依存するネイティブ DLL の32ビットと64ビットバージョンが同じように利用できることを確実にする必要があることを意味します。そして適切なほうが自動的に選ばれます。OS の API を呼ぶのは WOW のおかげでとても簡単です。しかし、マネージアプリにネイティブ DLL を一緒に配布している人々は64ビットシステムで32ビット DLL が問題を起こすことにはじめは驚きます。また、マネージでは珍しいですがポインターサイズのバグ(IntPtr と Int32 のサイズを同じと仮定してしまったり、マーシャリングの宣言を誤るミス)もいまだにあります。
また、コードを二回テストしなければいけないという問題があります!すべてのプラットフォームでテストし、サポートするには大きなコストを払うことになります。


2. 32ビットはいずれにしろより速い傾向があります

アプリケーションが32ビットか64ビットモードで走るとき、32ビットモードのほうが少し速い傾向があります。大きなポインターは多くのメモリとキャッシュを消費します。そして利用できる CPU キャッシュのバイト数は32ビットと64ビットプロセスで同じです。もちろん WOW レイヤーは若干のオーバーヘッドを加えますが、私が見た大部分の現実のシナリオではネイティブ64ビットプロセスより WOW のほうが速いことを示しました。


3. いくつかの機能が64ビットでは利用できません

32ビットと64ビット間で完全に同じ機能にしたいのですが、現実はまだそうではありません。CLR v2 は混在モードデバッグx86 だけでサポートしました。そして、CLR v4 で x64 サポートを加えましたが、エディット&コンティニューや IntelliTrace はまだ x64 でサポートしていません。CLR チームで新しい機能を加えるときは、いつも x64 を第一級市民と考えています。しかし、現実は我々が複雑なコードベース(例えば完全に別々の32ビットと64ビット JIT コンパイラ)を持っており、トレードオフをしなければいけないということです。

So how is Visual Studio 2010 and .NET 4.0 changing?

それで Visual Studio 2010 と .NET 4.0 はどう変わりますか?

CLRコンパイラは何も変えていません。両方のモードをサポートし続けます。しかし、これらの問題を議論した後、VS プロジェクトシステムチームは、VS2010 で EXE プロジェクトのデフォルトを x86 にすることに同意しました。AnyCPU は DLL(どんなプロセスにロードされるか必ずしもわからない)ではまだすばらしい価値があり、AnyCPU をやめる十分な正当性はありません。そのため、DLL プロジェクトは AnyCPU のままです。
[訳注: ベータ1では誤って DLL も x86 になっていたらしい]
[訳注: DLL は32ビットと64ビットの両方のプロセスで使えるので、両方でテストが必要です]

この問題を議論したとき、ほとんどの人は x86 をデフォルトにすることは少なくとも数年間は最高の選択と同意しました。これは64ビット OS とフレームワークのサポートの減少を意味するものではありません[訳注:全体の半分くらいがこの点についてくどくど書いてありますが、ばっさり省きました。よっぽど言われたんだと思います]。

おまけ

ildasm で PE ヘッダを覗いて AnyCPU/x86/x64 を比べてみました。ia64?あー、聞こえない。

COFF Header の Machine Type は AnyCPU/x86I386(0x14c)。x64 は AMD64(0x8664)。
PE Optional Header のマジックナンバーは AnyCPU/x86 が 0x10b = 32bit(PE32)、x64 は 0x20b = 64bit(PE32+)。

CLR Header
AnyCPU .corflags 0x00000001 // ILONLY
x86 .corflags 0x00000003 // ILONLY 32BITREQUIRED
x64 .corflags 0x00000001 // ILONLY

AnyCPU の PE ファイルは見かけは32ビットの体裁をとっているようです。

従来の並列化と最近/これからの並列化の違い

一昔前の CPU の進化とはシングルスレッド性能を伸ばすことでした。
ソフト屋はこの進化にただ乗りし、ソフトをいったん作ればあとは何もしなくても、新しいハードでは速くなる、時間が経ってハードを買い換えてくれれば速くなるというおいしい状況でした。

Windows XP は発売当時、見た目だけハデにした重い OS と言われたのに、今では軽い OS として評価が高いのはハードの進化のおかげです。ハード進化の一翼が CPU のシングルスレッド性能の伸びでした。MS が Vista 発売にあわせて XP を軽くなるよう改良したわけではありません。

これをソフト屋がおいしい思いをしている、ただ飯を食ってるってことで、フリーランチと言います。


ところが今では CPU は進化の方向を変え、シングルスレッド性能の伸びはゆるやかになり、その代わりコア数が増えていく方向になりました。
これまでのようなソフトを書いていたのでは、ほっといても速くなりません。フリーランチに預かるには、コア数にあわせて速くなるようにプログラムを書く必要があります。


つまり、処理の並列化が必要で、それもこれまでのような一定数のスレッドを使うのではなく、コア数に応じて性能が伸びる仕組みが必要です。そうしなければ、ソフトは新しいハードでも速くならず、競争力がないソフトになってしまいます。数年後に軽いソフトとして評価が上がるということもないわけです。
可能な限りの処理を並列化してしまおうという方向です。
さらに最近は、並列にできない部分が処理のネックという意見も出てきました。そんなこと言われても困ってしまいますが…。


で、ソフト屋が並列に乗っかるにはどうすればいいかという模索が始まり、それが関数型言語だったり、TPL/PLINQ のようなライブラリだったりするというわけです。


いくつか聞いた話をあわせるとこんなところでしょうか。
たまにはこんなことを書いておくのもいいかなと思って書いてみました。

.NET4.0 の並列処理を試してみた その3 : サーバー GC

id:siokoshou:20090717、id:siokoshou:20090721、id:siokoshou:20090722 に続く TPL の記事。ベータ2の改良点がパラレルチームの blog に投稿されています。

でも、今回はそんなのがどうでもよくなるほど驚いたネタ。
ベータ2で7月に試したコードを実行してみると特にパフォーマンスに変化はありませんでした。しかし、掲示板でコアが多いほうが遅いんだけどなんで?って質問があって、その答えにこんなものが。

In addition to Andy's good questions, have you tried enabling the server GC? If not, you should try that as well, by creating a .config file for your application containing:






Parallel.For Slows Down with more cores

サーバーGCを試してみてとのこと。
さっそく試してみると、パフォーマンスがぐぐっとアップしました。前回はタスクを大量につくらないように変更してパフォーマンスを上げましたが、そのような小細工をしていないコードがそれにせまるほどの約2倍も高速化しました。一番速いのはやはりタスクをほとんど作らないパターンで、これもごくごくわずかですが速くなりました。

プロセッサが 2 つ以上のコンピュータでは、サーバーのガベージ コレクションが最速のオプションとなります。

<gcServer> 要素

え?そうだったの!?ちょっといろいろ試してみる…。

結論 : コアが多い場合はサーバー GC を試してみるといいことあるかも。

VS2010テキストエディターの矩形操作

http://blogs.msdn.com/visualstudio/archive/2009/10/26/box-selection-and-multi-line-editing.aspx

ビデオがわかりやすい。

  • Alt + マウス操作で矩形選択
  • Alt + Shift + カーソルで矩形選択 (ビデオでこれしゃべってる?とにかく試したらこれでいけた)
  • 矩形を選んでまとめて編集できる!

プロポーショナルフォントでもできた。無駄にすごいw

WPF のカルチャー

WPF で以下の XAML を書いて実行すると、日付と時刻が「10/29/2009 7:56:33 PM」と en-US カルチャーで表示されてしまいます。

<Window x:Class="LangTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    Title="LangTest"
    SizeToContent="WidthAndHeight">
    <TextBlock Margin="30"
        Text="{Binding Source={x:Static sys:DateTime.Now}}"/>
</Window>

これを「2009/10/29 19:56:33」のように ja-JP カルチャーで表示するには、App クラスの OnStartup をオーバーライドしてカルチャーを指定すれば OK です。

protected override void OnStartup( StartupEventArgs e )
{
    FrameworkElement.LanguageProperty.OverrideMetadata(
      typeof( FrameworkElement ),
        new FrameworkPropertyMetadata(
          XmlLanguage.GetLanguage( CultureInfo.CurrentCulture.IetfLanguageTag ) ) );

    base.OnStartup( e );
}

ほかにも XAMLxml:lang="ja-JP" を付ける、Language="ja-JP" を付けるなどの方法もあります。

そして、これはバグではなくデザインだそうです。以下の connect にあります。
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=442569
回答はとても興味深いもので、従来の .NET の国際化まわりのチェックをしなかったのはよくなかったと考えており、きちんと明示させる方向を選んだそうです。FxCop はこのあたりをうるさく指摘しますよね。WPF のこの仕組みにより、デザイナー(美術さんじゃなくアプリデザイナーのことだと思います)がきちんとカルチャーについて考えるようにしむけたいようです。

まあ、でも、ネットを見るとこの問題は FAQ で、上のコードを書けという答えばかりで、カルチャーを意識させるのは失敗しているようです…(^^;
id:siokoshou:20090310 に書いた Binding チートシートの最後にもこのコードだけが載ってます。

WPF と BackgroundWorker

WPF で BackgroundWorker は使ってもいいのかな?と調べてみました。結果は OK です。
BackgroundWorker 内部で使っている SynchronizationContext は WPF 用の新しい派生クラスが使われています。

  • WinForm … System.Windows.Forms.WindowsFormsSynchronizationContext
  • WPF … System.Windows.Threading.DispatcherSynchronizationContext

そして、とても良い記事がありました。後半のちゃぶ台返しに注目。
http://msdn.microsoft.com/ja-jp/magazine/cc163328.aspx

WPF は System.ComponentModel を無視しようとしているように見えるので気になったのでした。