BlackJumboDog .NET Core 性能向上 メモ 2

これの続ぎ

リクエストを処理する中で、遅くなっているものは何か?
よく考えられるもは、ロジックの無駄な処理(処理する必要のないものを処理している)。リソースの使い方。
で、まずはロジックの無駄という意味では、意味のない処理は見つからず。
で、遅くなっているところというのを考えると
リソースの確保の部分。
new byte [ XX ];
これは、ある程度のサイズになったとき、Heapの確保が発生するので、場合によって、数msかかる。これが1か所ならまぁいいのですが、中の処理では、Socketの読み込みバッファから、エンコード用のバッファ、出力用のバッファまで広く使っており、1回のリクエストで、少なくとも5回以上は生成しておりました。

これを何とかできないか?と考えたのが、BufferPoolというクラスを作ることでした。

https://github.com/darkcrash/bjd5/blob/3212cf26eca2f1c90ee545df4b3307fc654abf73/Bjd.Common/Memory/BufferPool.cs

これの役目は・・・

  • byte配列の確保を毎回やらせないことで、GCのコストを抑える。Heapの確保と解放をあまり発生させない。
  • 一定数事前に配列を生成し、領域を確保することで生成のコストを抑える。
  • 必要なサイズに応じたものを返す。
  • 足りなければ、作る。
  • 足りなくて作ったものが、既定の確保しておく領域を上回る場合には、解放する。

これで得られる恩恵は、思ってた以上に大きく、リクエストあたり数ms減りました。

これに対して個人的な見解は・・・・
GCはリソースやメモリの再利用があるので、そこである程度同じことさせているときは、次第に早くなるのですが、HTTPリクエストやら、Socket通信などでは、同じリソースのサイズを要求しないことが多く、メモリの断片化が起きたり。GCが動くことで、CPUリソースを消費したりということではないかなぁと思ったりしたが、それを明確に確認できる方法は思いつかなかった。。。GCの呼び出し回数が減っているというくらいかな。

 

続く・・・

 

BlackJumboDog .NET Core 性能向上 メモ1

BlackJumboDog .NET Coreの性能を向上させようといろいろ模索する中での話。

今やっていた性能向上というのは、WebServerで、クライアントからの接続要求をどれくらい処理できるか?です。
具体的には、Fiddlerを使って、1000回~100000回のリクエストを処理させて、エラーがおきないように、そして500回あたりのリクエスト(FiddlerがTimelineをグラフ化してくれるサイズ)をどれくらい早くできるか?

そんなところで、比較対象がなければということで、MSのIIS(InternetInfomationService)を手っ取り早いところで、相手にしてみました。Hyper-VのゲストOSとして、Windows Server 2016 を。リソースは、4コアのメモリ4GBをあてました。検証で利用するファイルは、BJDの初期で入っているindex.htmlです。サイズは4Kb強

IIS on Windows Server 1

私が注目したのはまず、右上に出ている78msという時間。これはこのグラフ全体が78msということっぽいようで、500回のリクエストを80ms以内にすべて応答しているということです。グラフの色が出ているところが通信中の状態(HTTPリクエストからレスポンスが返るまで)で、空白が、何もしていない時間のようです。こうしてみたとき、空白の領域は多く、Fiddlerが遊んでる状態といいますか、サーバーとクライアントはKeepAliveによってただSocketがつながっているだけの状態。1リクエストは数msで処理されている状態。

 

対して、BlackJumboDog .NET Coreはというと・・・

全体が、500ms。グラフもぎりぎりまで密集しており、空白はほとんどない。リクエストからレスポンスまでの時間が長く、Fiddlerが待っている状態。

リクエスト数に対しての処理時間でいけば、単純に1msとなりますが、並列で動いているために、1リクエストは実際のところ10ms、20msとかそんな感じ。

一日に数回しかないような処理ならば、速いものですが、このリクエストはブラウザ一つで、ページの読み込み、スクリプトの読み込み、画像リソースの読み込みとか考えたとき、1ページ表示するのに重ければ、10回以上はあるでしょう。

これを考えたとき、静的なファイルはダウンロードさせずに、ブラウザにキャッシュ化したり、リクエスト数を減らすためにファイルを結合してみたりとか、トラフィックがネックであれば、GZIP圧縮をかけてみたりといったこともできます。

ですが・・・それでは面白くない。
実際、これだけの差があることを.NET Coreのせいにしてしまうというのも一つなのですが、.NET Coreでも、KestrelというASP.NET Coreを動かすシンプルなアプリケーションサーバーは、ものすごい勢いでリクエストを処理できたりします。(実際はアプリケーションの処理によってはそんなに早くならないですが)

https://github.com/aspnet/KestrelHttpServer

 

そこで、何とか早くならないかと模索してみた・・・つづく

dotnet publish [ProjectDirectory] -o [output] の出力先

project.jsonのものを置き換えていたところで気づいたのでメモ

dotnet publish [ProjectDirectory] -o [outputdirName]

とした場合、感覚としてdotnetコマンドを実行しているシェルのカレントディレクトリだろうと思っていたが、どうやら [ProjectDirectory] がカレントディレクトリになっているようだ。

なのでdotnetコマンド実行しているカレントディレクトリに配置したい場合は

dotnet publish [ProjectDirectory] -o ..\[outputdirName]

としないと予想しないところに出てしまうようだ。

追記 2017-03-11

dotnet pack [ProjectDirectory] -o [outputdirName]

でも同じことが起きた。
このことから、[ProjectDrectory]をカレントにしてしまっているような感じはする

 

xUnit fail of overload Testmethod on Visual Studio 2017 .NET Core

Visual Studio 2017 が出たぜー!というところで、さっそくBlackJumboDogもアップグレードしていったところ。

xUnitで謎のテストエラーが続発した・・・

System.InvalidOperatrionException
普通に考えたところで、メソッドの呼び出し方を間違えたとのことなのだが、型を動的にしているわけでもないので、あれま・・・とか思っていたところ。

よくよく見ていると、そのメソッドはオーバーロードしていた。

引数3つのTestメソッドと、引数2つのTestメソッドがいる状態
エラーメッセージは

メッセージ: System.InvalidOperationException : The test method expected 3 parameter values, but 2 parameter values were provided.

となっていて「引数が3つあるぞ、でも2つしかないぞ。」という困ったぞーという意味の例外。

なので・・・

オーバーロードをやめて名前を付けた。

というわけで、成功!

ひとまず回避策は見えたが、これが仕様なのか、不具合なのかは調べていない。。。