グラニがC#にこだわる理由

第1回神獄のヴァルハラゲートの裏側をCTOが語り尽くす!

C#の非同期構文をフル活用し業界最速の応答速度を実現

現在、多くのソーシャルゲームがPHPやPython、Rubyといった、いわゆるLightweight Languageを使って開発されていますが、⁠株)グラニではプログラミング言語としてC#を採用し、Windows環境で開発および運用を行っています。実は、現在提供している「神獄のヴァルハラゲート」は、2013年1月のリリース時点ではPHPを利用していました。しかし同年4月にC#への移植を開始し、3ヵ月後の7月16日にはC#への切り替えを完了させています。

このようにC#にこだわっている理由の一つがパフォーマンスです。ソーシャルゲームはユーザがアクションを起こすたびにデータが更新されるというしくみ上、どうしてもViewのキャッシュを効かせられなかったり、1万リクエスト/秒を超えるような高負荷が発生したりしますが、そうした状況においても平均100ミリ秒以下の応答速度を実現できるように注意しています。ユーザの体感はWebブラウザのレンダリング時間だけでなく、サーバが応答を返す際の最初の応答(Time To First Byte)も重要で、わずか50ミリ秒の差が体感速度に影響を与えます。私たちが調査する限り、神獄のヴァルハラゲートの応答速度は、業界最速と言ってもよいほど高速です。

その理由は、C#自体のパフォーマンスの高さに加えて、C# 5.0から搭載された非同期構文にあります。これを利用すれば、非同期処理ができる個所は片っ端から非同期にして一気に待つという処理が簡単に書けるため、アプリケーション全体で採用してレスポンスタイムの短縮化を図っています。たとえば、幅広い用途で利用しているKVSであるRedisへのアクセスについて、すべて非同期化し、自動でパイプライン化しています図1、リスト1⁠。

図1 Glimpseというツールを用いて、データベースやRedisへのアクセスを常にすべて視覚化している。タイムライン上で重なっているところは、並列にアクセスしている部分
図1 Glimpseというツールを用いて、データベースやRedisへのアクセスを常にすべて視覚化している。タイムライン上で重なっているところは、並列にアクセスしている部分
リスト1 C#による非同期プログラミングの一例。asyncとawaitを利用すれば、容易に非同期メソッドを作成できる
var frontHPs = await field.OwnGuild.Members
    .Where(x => x.Position == Position.Front)
    .Select(async x => new
    {
        Name = await x.Name,
        CurrentHP = (await x.UserStatus).CurrentHP
    })
    .WhenAll();

Lightweight LanguageとC#の本当の違い

Webアプリケーション開発において、C#を利用することがピンと来ないというエンジニアの方も多いでしょう。しかし世界では、多くのWebアプリケーションがC#で実装されています。たとえば世界最大級のプログラミング系FAQサイトであるStackoverflowは、Windows ServerとC#の組み合わせで開発されました。

たしかに現状では、Webアプリケーションの開発はLightweight Languageで行われるのが主流であり、その理由も理解しています。特にWeb業界はリリースサイクルが極めて短いため、言語としての軽量さは大きな武器になります。

さて、Lightweight Languageの⁠軽量さ⁠とは、実際のところ何を指すのでしょうか? これは、コードの作成や修正が容易であることとされています。一方、比較されるJavaやC#はそれが容易ではなく、重量級の言語であるというわけです。それは本当でしょうか。

私たちはC#こそが軽量なプログラミング言語であると考えています。なぜなら、プログラミング言語はそれ単体で評価するものではないからです。実際に開発する際に必要となる「プログラミング言語+IDE+ライブラリ/フレームワーク」を三位一体で評価するべきでしょう。その点において、⁠C#+Visual Studio+.NET Framework」の組み合わせほど高いレベルですべてを提供できているプログラミング言語はほかにありません。

たしかにC#単体の軽さはLightweight Languageにかなわないかもしれません。しかし、Visual Studioというロケットブースターが付いたとき、その軽さはほかのプログラミング言語を圧倒します。

C#のIDEサポートはあらゆるプログラミング言語の中で最高レベルですが、それは言語設計自体がVisual Studioの存在を前提にしているためです。ほかのプログラミング言語のIDEがどれだけ進化しても、構文レベルでIDEを意識しているC#を追い抜くことは絶対にないでしょう。

さらにC#は進化し続けています。2008年にC# 3.0となり、2010年はC# 4.0、そして2012年にはC# 5.0と、着実にバージョンアップを重ねてきました。2014年も新しい機能が発表されようとしています。10年前と現在でプログラミング環境に求められるものは大きく変わっており、それに追随してC#は進化しているのです。

また、そもそもプログラミング言語は、ただ書けて動けばよいというものではありません。より軽量に、より高速に、そしてソースを書くことがより楽しくなっていくべきです。C#は進化し続けることで、こうした要求に応え続けています。

もちろん、ひたすら機能を追加して複雑怪奇なプログラミング言語になっては本末転倒です。その意味で、C#の言語設計者であるAnders Hejlsbergは驚異的なバランス感覚を持っていると感じています。けっして進化を止めない一方、C#にそぐわない機能は採り入れない頑固さもあります。そしていざ新しい発想を採り入れるとなれば、誰でも使いやすいように徹底的に洗練した形で実装しています。

C#の魅力を支えるVisual Studio

Lightweight Languageに慣れたエンジニアから見ると、型というと重たい、面倒くさいという印象があるかもしれません。しかしIDEと組み合わせたC#は、そうした印象を一気に変えてしまう力を持っています。

たとえばC#+Visual Studioは、コードを書いているそばからリアルタイムにエラーを検出して通知する機能を備えています図2⁠。

図2 C#+Visual Studioでは、リアルタイムにコードをチェックし、エラーを検出すれば通知が行われるため、コーディングに集中できる
図2 C#+Visual Studioでは、リアルタイムにコードをチェックし、エラーを検出すれば通知が行われるため、コーディングに集中できる

ユニットテストがあるから大丈夫だと思うかもしれませんが、リアルタイムは⁠速さ⁠であり⁠軽さ⁠につながります。そもそも文法エラーを気にしながらコードを書くなどというのは人間のやるべきことではなく、機械に任せるのが正しいプログラマの姿勢ではないでしょうか。さらにVisual Studioであれば、コード整形のために手動でスペースを入れたり括弧の位置を整えたりする必要もありません。コードフォーマットを呼び出せば一発で完了します。

型はドキュメントにもなります。メソッド名だけでは情報として不十分であり、引数の型や戻り値の型が組み合わさることで、そのメソッドが何をするためのものかが明確になります。最終的にドキュメントを書いて保管するのであれば、最初から言語構文で型を書いたほうが、コンパイル検出もできるのでずっと効率的です。

型の存在はIDEが行うリファクタリングにも強みを発揮します。C#+Visual Studioの組み合わせなら、たとえばリネームを安全かつ確実に実行してくれます。

そもそもプログラミングの世界において、完璧な設計や、完璧な名前付けなどは存在しないでしょう。不適切な名前は、そのあとのメンテナンスに重大な影響を及ぼし、エンジニアを苦しめます。大切なのは、変えるべきときに変えられることであり、名前が不適切になったらすぐに変更することが大切です。

そのためには、漏れなく簡単にすべてをリネームできる必要があります。C#+Visual Studioの組み合わせであれば、メニューの中から「リネーム」を選ぶだけでコードを解析し、完璧にリネームを行ってくれます。しかし、テキストベースの一括置換だとどうなるでしょう。置換が誤爆して意図しないところが書き換えられるかもしれず、危険を伴う作業になります。このような危険性を認識すると、エンジニアの手が止まり、本来行うべきリネームが行われず、わかりづらいコードのまま放置されることになりがちでしょう。

機械ができることは機械が完璧に行う。それが心理的な抵抗をなくし、本当の軽量さ、変更耐性、修正の容易さを実現することにつながります。

LINQは関数型とオブジェクト指向の融合

私はC#のLINQ to ObjectsをJavaScriptに移植したライブラリlinq.jsを作成するほど、C#の中でも特にLINQのエキスパートです。このLINQはC#の世界を一変させました。もはや、LINQがあるからC#を使っていると言っても過言ではありません。最高に使えて、最高に書いていて楽しい機能です。

LINQは単純に言えばコレクション処理ライブラリで[1]⁠、それ自体は珍しくありません。C#以外のプログラミング言語にも、filterやmapといったしくみはあります。ただLINQのコレクション処理は標準で遅延実行されます。filterやmapを重ねても、中間配列を作ることはありません。LINQは合成可能であり、WhereとSelectを組み合わせてリストを内包表記、さらにOrderByとSkipWhileを入れてTakeしてと無限大に手が広がります。

ただ、HaskellやScala、Ruby 2.0以降ではEnumerable#Lazyで書けるため、今となっては遅延実行も特別というほどではありません。それでもLINQがほかのプログラミング言語と違うのは、Visual StudioのIntelliSense(入力補完)との抜群の相性の良さなのです。⁠.」⁠ドット)を入力すれば、選ぶべきメソッドの候補が現れ、カードをドローするようにメソッドチェインを重ねていくことができます図3⁠。その実行パイプラインの中を通る値もすべて型付けされていて、補完が完璧に効きます。LINQは関数型とオブジェクト指向の融合であり、Visual Studioの上に乗ったそれは喩えようのないほどの美しさを誇るのです。

図3 ドットを打てば次に選ぶべきメソッドの候補が現れる。このIDEとの緊密な連携が、C#のLINQを特別なものにしている
図3 ドットを打てば次に選ぶべきメソッドの候補が現れる。このIDEとの緊密な連携が、C#のLINQを特別なものにしている

道具にすぎないからこそこだわって選びたい

C#はWebアプリケーションだけでなく、ゲームやデスクトップアプリケーション、組み込みとあらゆる領域でプログラムを書くことができる万能な言語です。あらゆるプログラミング言語を見ても、すべての領域で実用的なレベルで使えるものは非常に珍しいと言えるでしょう。

実際、グラニではWebアプリケーションの開発以外の領域でもC#を活用しています。たとえばソーシャルゲームに関わる多くのプランナーは、データの入力や解析にExcelを活用しているでしょう。

グラニではこうした作業を効率的に行えるようにするために、⁠Visual Studio Tools for Office」というC#でExcelを拡張できるOffice開発ツールを利用し、データベースと連携させるなどExcelを拡張して利用しています。このようにC#を利用して、自社の業務フローに最適な環境を整えているというわけです。

ゲームの世界との親和性も高まっています。iOSやAndroidなど複数のプラットフォームに対応したゲームエンジンとして最も有名なUnityは、C#でコードを書くことができます。

グラニがUnityを用いれば、ネイティブクライアントはUnityでC#、サーバサイドもまたC#となり、クライアントからサーバサイドまですべてC#で統一できることになります。これにより、開発環境の統一やコードの共有と自動生成など、C#のメリットを最大限に引き出すことが可能になります。

そもそもプログラミング言語は道具にすぎず、大切なのはサービスです。しかし、だからこそ良いサービスを作るために道具へのこだわりが必要だと考えます。私たちは強いこだわりでC#を選びました。C#だからこそすばらしい成果が出せる、私たちはそれを証明していきます。

また、このようにすばらしいC#をぜひ多くの人たちに使ってほしいと考えています。しかしモデルケースがなければ誰もついてこないでしょう。そのためにグラニは先陣を切り、成果物をOSSとして公開し、ノウハウを共有していきます。こうした私たちの活動によってC#がみなさんの一番の選択肢になればよいですし、C#と言えばグラニと言われる存在を目指しています。

提供/株式会社グラニ

http://grani.jp/

おすすめ記事

記事・ニュース一覧