Deep Insider の Tutor コーナー
>>  Deep Insider は本サイトからスピンオフした姉妹サイトです。よろしく! 
Build Insiderオピニオン:岩永信之(6)

Build Insiderオピニオン:岩永信之(6)

次期C#とパフォーマンス向上(前編)―― 必要となった背景と改善内容

2016年5月10日

機能や構文ばかりが注目されるが、プログラミング言語ではパフォーマンスも重要だ。パフォーマンス向上に対する要求が高まってきた背景と、向上のための改善方法を説明。C# 7以降で追加が検討されている新機能にも言及する。

岩永 信之
  • このエントリーをはてなブックマークに追加

 近年、さまざまなプログラミング言語では、ソフトウェア開発の生産性を向上するべく便利な機能が実装されてきた。その一方で、書かれたプログラムのパフォーマンスへの配慮も重要である。「確かに書きやすいが、出来上がったものが遅くてつらい」ということになっては言語の魅力は半減する。

 C#では、これまでも、パフォーマンスに配慮した言語機能をいくつか提供してきている。例えば、値型(構造体)がその代表例だ。ジェネリック型消去ではない実装になっているのもパフォーマンス上の理由である。利用場面によってはイテレーター非同期メソッドなどもパフォーマンス向上に寄与するだろう。

 しかし、C#にもパフォーマンス向上に向けてまだまだできることが残されている。また、C#の用途としても、より高いパフォーマンスを求められる場面が増えてきている。そこで次期バージョンのC# 7では、こうしたパフォーマンス向上を目的とした新機能がいくつか提案されている。

 本稿では、C#へのパフォーマンス向上要求が増している背景と、C# 7以降で追加されるパフォーマンス向上に関連する新機能について説明する。

背景から

 先に、C#にパフォーマンス向上が求められる昨今の背景について話そう。

C#コンパイラー自身

 コンパイルにかかる時間は、開発者の生産性に関わる非常に重要なものである。コンパイル時間が長いと、その待ち時間に別の(それも多くは仕事と関係ない)作業を始めてしまい、気が付けばそちらに没頭していたということも少なくない。

 さらに、C#は常にコンパイル処理が動き続けている言語である。Visual StudioなどでC#コードを書いていると、1文字1文字、キーを打つたびに解析がかかり、即座にエラーを検出できる。

 マイクロソフト製のC#コンパイラーは、C# 5.0まではC++で書かれていたが、さまざまな理由があり、C# 6からはC#を使って書き直された*1。その結果、C#のパフォーマンスが向上することで、C#コンパイラー自身の性能向上も期待できる。

  • *1 現在、オープンソースで開発されているのは、このC#で書き直したコンパイラーである。この新しいコンパイラーは、製品名としては「.NET Compiler Platform」という。ソースコードのリポジトリなどには、コードネームだったRoslynという名称が残っている。

 実際、C#コンパイラーをC#で書き換える際、C#チームの面々はパフォーマンスに関してかなりの苦労をしたようである。もともとC++で作られていたものからの移植で、パフォーマンス劣化を避けたいというなかなか大変な目標があった。当初目標は「C++実装の2倍程度の遅さまで」だったそうで、それは達成したようだ(C#化によって並列処理がしやすくなり、マルチコアCPU環境ではC++実装よりも高速な場合もあり得る)。しかし、2倍と言わずもっと改善したいという要求は当然出てくる。

 筆者の体感として、C# 7の新機能にはC#チームの「内需」を感じている。前回までに説明してきたパターンマッチングや、今回のパフォーマンス向上がらみの新機能をまず欲しがっているのはC#チーム自身だろう。

 とはいえ、ものがコンパイラーである。パフォーマンス向上は全てのC#開発者にとって恩恵となる。

Webフレームワーク

 Webフレームワークのパフォーマンスに関心がある人は多いだろう。使われているプログラミング言語を問わず、数多くベンチマーク比較が行われている。多くのベンチマークがLinux上での比較になっていて、C#はMonoランタイム上で実行される場合が多い。

 しかし、LinuxやMac OS X上のMonoは最適化があまり進んでおらず*2、「C#/ASP.NETは遅い」という結果に至りがちだった。.NET Coreはいまだ正式リリースが見えてこないものの、パフォーマンス改善も日々行われており、徐々にこの状況は改善されそうである。

  • *2 ここ数年、Monoを使ったビジネスの主戦場がiOSやAndroidなどのスマートフォンになっていたため、サーバーOS向けの最適化にリソースが割けなかった。これが、マイクロソフト自らがLinuxやOS X向けに.NET Coreを作ることになった背景でもある。ちなみに、iOS/Android向けのMonoランタイムはなかなか良いパフォーマンスを発揮するようだ。

 とはいえ、.NET CoreやASP.NET Coreの登場で、他のプログラミング言語やWebフレームワークと比較される機会は増えるだろう。また、Linux上(特にエンタープライズ分野)では後発であるため、先発組と比べたメリットが求められ、よりシビアな目で見られることになるだろう。

 このような背景から、ランタイム(.NET Core)やWebフレームワーク(ASP.NET Core)側でのますますの最適化はもちろんのこと、C#という言語レベルでもパフォーマンス向上につながる構文の追加が求められている。

ゲーム

 近年、ゲーム開発用途としてC#が注目を集めている。

 Buildなど、マイクロソフトのイベントでもUnityの名前を聞くことが多くなっている。Unityには、ベースとなっているMonoのバージョンが古い(結果的に、C#のバージョンも古く、C# 3.0までの機能に制限される)という問題もあるが、改善の兆しはあるようだ。先日のBuildで、Unityの.NET Foundationへの参加が表明され、C# 6サポートやMonoランタイム・クラスライブラリのアップグレードに取り組むことが発表された。

 また、今夏リリース予定のWindows 10アニバーサリーアップデートでは、UWP(Universal Windows Platform)アプリをXbox One上で動かせるようになる。同時に、全ての開発者がXbox Oneのゲームを作って公開できるようになる(これまでは登録制で、限られた企業のみが可能だった)。

 ゲームの種類によっては、特にパフォーマンスを気にせず普通にC#で書いていても十分なものもある。しかし、アクション性が高いゲームとなると、ガベージコレクションをとことん避けるような極端なコードを書く必要がある。さらにいうと、そういう極端なC#コードを書ける人にとっても、現状のC#ではかゆいところに手が届かないと感じることがある。

 もちろん、C#の良さは生産性と安全性の高さにある。パフォーマンスを求めるに当たってその辺りがおろそかになるようでは困りものである。しかし、生産性や安全性を犠牲にせずとも、ゲーム開発者が求めるようなパフォーマンスに少しでも近づける余地は残されている。

背景まとめ

 ここまで話してきた背景について、図1にまとめよう。

図1: パフォーマンス改善の背景
図1: パフォーマンス改善の背景

 もちろん、これ以外にもパフォーマンス改善の恩恵を受ける分野は数多くあるだろう。

 それでは、具体的な改善内容について説明していこう。

改善内容

 生産性を重視するプログラミング言語では、多くの場合、メモリ管理はコンパイラーやランタイムライブラリによって自動管理されている。C#も生産性を一番に考えている言語ではあるが、パフォーマンスへの配慮で、いくらかメモリの手動管理の手段を残している。その最たるものが値型(構造体)の存在である。

 値型を使うことによって、不要なヒープ確保を避けられる。一般的に、パフォーマンス向上にはヒープ確保を避けることが非常に有効である。ヒープ確保を避けることで、ガベージコレクション(以下、GC)の負担を減らしたり、参照局所性を高めたりする効果がある。配列の確保が最たる例となるが、値型の配列の場合は図2、参照型の配列の場合は図3に示すようなメモリ配置になる。

図2: 値型の配列のメモリ配置の例
図2: 値型の配列のメモリ配置の例
図3: 参照型の配列のメモリ配置の例
図3: 参照型の配列のメモリ配置の例

 ちなみにこの例は、リスト1のような構造体の配列を想定している。この構造体は今後(後編を含む)のサンプルでも使う。

C#
public struct Point
{
  public int X;
  public int Y;
  public int Z;
}
リスト1: 今後のサンプルで使う構造体

 図3に示す通り、参照型の場合にはとびとびの配置(参照局所性が下がる)やヒープの新規確保、ゴミの発生(GC負担)などがあり、パフォーマンス的に不利になることがある。値型を使うことで、この問題を回避できる。

 この他、参照型を使う場合でも、不要なコピーを避けるためのクラスライブラリや構文を追加することでパフォーマンスの向上が期待できる。

 C# 7への追加が検討されている機能としても、不要なコピーやヒープ確保の回避を促進するものがいくつかある。その一例として、以下のようなものが検討されている。

  • 参照戻り値
  • ローカル関数
  • ValueTask構造体
  • スライスSystem.Slices名前空間とSpan構造体)
  • UTF-8文字列

 このうち、参照戻り値とローカル関数は、単純なC#言語構文の追加で、C# 7で実装されそうである。残りの3つは、先にクラスライブラリだけ実装され、その後、関連する言語構文が入りそうで、恐らくC# 7よりもう少し先の話になる。

 それぞれの機能の詳細については次回後編で説明する。

岩永 信之(いわなが のぶゆき)

岩永 信之(いわなが のぶゆき)

 

 

 ++C++; の中の人。C# 1.0がプレビュー版だった頃からC#によるプログラミング入門を公開していて、C#とともに今年で15年になる。最近の自己紹介は「C#でググれ」。

 

 

 

 

※以下では、本稿の前後を合わせて5回分(第4回~第8回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。

Build Insiderオピニオン:岩永信之(6)
4. 次期C# 7: 型に応じた分岐や型の分解機能、パターンマッチング

オブジェクトの型を階層的に調べて分岐処理を行いたい場合がある。そのための機能として、C#の次バージョンでは「パターンマッチング」が追加される。

Build Insiderオピニオン:岩永信之(6)
5. 次期C# 7: パターンマッチングの内部的な仕組み

C#の次バージョンに追加される「パターンマッチング」の挙動(パターンマッチングがどう展開されるのかや、その実現方法)について説明する。

Build Insiderオピニオン:岩永信之(6)
6. 【現在、表示中】≫ 次期C#とパフォーマンス向上(前編)―― 必要となった背景と改善内容

機能や構文ばかりが注目されるが、プログラミング言語ではパフォーマンスも重要だ。パフォーマンス向上に対する要求が高まってきた背景と、向上のための改善方法を説明。C# 7以降で追加が検討されている新機能にも言及する。

Build Insiderオピニオン:岩永信之(6)
7. 次期C#とパフォーマンス向上(後編)―― 予定・検討中の5つの新機構

前編に続き、次期C#のパフォーマンス向上について解説。C# 7以降での採用が予定もしくは検討されているパフォーマンス向上関連の新機能の内容を具体的に見ていこう。

Build Insiderオピニオン:岩永信之(6)
8. 見えてきたC# 7: C#の短期リリースサイクル化

C# 7にはどんな新機能が含まれるのかが見えてきた。これまでと比べて、C# 7はかなり速いペースでのリリースとなる。その背景にはどんな事情があるのだろうか。

サイトからのお知らせ

Twitterでつぶやこう!