同一ファイルに対して、 複数種類のファイルハッシュを高速に計算する

ネットワークドライブ上の GB オーダーのファイルに対して、複数のハッシュアルゴリズムでファイルハッシュを計算する必要に駆られていた。
「そんなシチュエーションが本当にあるのか?」と思うかも知れないが、あったのだから仕方ない。

最初は、 PowerShell を使って 愚直に Get-FileHash を 2回 計算していた。
しかし当たり前だが、 2回ファイルをダウンロードすることになるこの方法は遅くて仕方ない。

ということで、 C# でコンパイルしたコード上で、 一度メモリに読み込ませてから複数のハッシュアルゴリズムで計算できるようなコードを作成した。

バッファーサイズを 80KiB 程度とすると、 .NET の仕様で 85KB を境にメモリの扱いが変わる (LOH と呼ばれる特別なヒープに移動する) ことから、 このサイズを超えると一般的に動作速度が下がると言われている。
しかし、今回のコードの場合バッファーが小さいと、 Task 周りの処理がボトルネックになってしまうので、 8MiB くらいのサイズとしている。

80KiB としているのは、 .NET mscorlib の System.IO.Stream.CopyTo メソッドなどでもおなじみなのだが、これが必ずしも正解とは限らないわけだな。


パフォーマンス

800Mbps 位でシーケンシャルリードできるリモート上の 4GB のファイルを、 4回 ずつ計測した平均を計測した。
Get-FileHash の 2種類目 以外はクライアントキャッシュが効かないように注意し、 計測の各回でハッシュを計算するファイルはそれぞれ別のファイルで、中身はランダムバイナリとした。
Get-FileHash については、 計測の各回では同じファイルに対して -Algorithm パラメータを変えて複数回連続で呼び出した。

計算したハッシュの種類 Get-FileHash 上記の改良スクリプト
1種類 (SHA1) 48.8s 43.3s
2種類 (MD5, SHA1) 60.4s 44.2s
3種類 (MD5, SHA1, SHA256) 83.3s 48.2s

ん? 計算したハッシュが 1種類 の場合でも、改良スクリプトの方が早いぞ?


うわっ… HashAlgorithm.ComputeHash の実行速度、低すぎ…?

PowerShell v5 相当

PowerShell v6 相当

上記のように、 Get-FileHash は、内部的に HashAlgorithm.ComputeHash でハッシュ計算を実行している。

で、その HashAlgorithm.ComputeHash がどうなっているかというと、 4KiB 毎に ファイルの読み込みと、 ハッシュストリームへの書き込みを 同期的に 行っている。

ファイルの読み込みも、 (使うアルゴリズムにもよるけど) ハッシュの計算も、 どちらもコストがかかるので、同期的にやってたらそりゃ遅いわ。

DI っぽく EF Core 1.0 + SQLite を Full .NET と .NET Core のコンソールアプリケーションで使ってみる [Entity Framework Core]

しばやん御大の Entity Framework Core についての以下の記事を読んで、


コンソールアプリでも、 ソースコードに 接続文字列 や ログの設定を書かずに、設定ファイルから Dependency Injection (依存性の注入: 以下DI) するにはどうしたらよいのかな? と思ったので、 ASP.NET Core の流儀を参考にしながら やってみようと思う。

データベースは、扱いが簡単な SQLite にする。

記事の最後に、 Visual Studio 2015 ですぐに使えるサンプルプロジェクトを用意しているので、 手っ取り早く結果を見たければ、 そのサンプルプロジェクトを見てみてほしい。

実現すること

まずは、何を実現させるのかをハッキリさせておこう。

  1. 接続文字列と ログ表示の設定を、外のファイルから指定すること
  2. マイグレーションなどを行うため、 EF Tools からも、上記設定が利用されるようにすること

ここで言う EF Tools とは、Entity Framework Core の コマンドラインツール のことだ。
このツールを使うと、 パッケージマネージャーコンソールから [cci]Add-Migration[/cci] とか [cci]Update-Database[/cci] と実行したり、 dotnet.exe から [cci]dotnet ef[/cci] コマンドを 実行することで、 コード生成やマイグレーションなど を利用することができる。

EF Tools でデータベースを取り扱う際、その接続文字列は DbContext に設定されたものが使用される。
コード内に接続文字列を書いてしまうと、マイグレーションするためのデータベースファイルが決め打ちになってしまい、変更ができなくなる。
このため、 EF Tools を実行した際も、依存性の注入が行えるようにしたい。

前準備

続きを読む

VS Express で .NET Core の xUnit.net を使ったテストのデバッグを行う

.NET で 単体テストと言えば、いまや xUnit.net が事実上の標準となっている。
ASP.NET Core や .NET Core のドキュメントでも、単体テストは xUnit.net を使うように案内されている。

ところが Visual Studio の Express Edition 系列 の テストエクスプローラは、 xUnit.net に対応していない。
それでも Full .NET Framework や dnx では、そのままテストプロジェクトを「実行」してしまえば、とりあえずデバッグ実行はできていた。
しかし、 .NET Core + dotnet-test-xunit では、それすらもできなくなってしまい、 [cci]dotnet test[/cci] コマンドの出力を確認するしかなくなってしまった。

デバッグ実行ができないのは流石に不便… ということで、デバッグ実行を行うハックを紹介しよう。

# ライセンス的に Visual Studio Community 使えるのなら、そちらを使うべき。
# ただ、たとえ Express ではなくても、 この方法を使うと xUnit.net の出力が文字化けする問題も防げるぞ。

エントリーポイントを作成し、コンソールプロジェクトにする

続きを読む

.NET Core での コンソールアプリの文字化けを直す

だいぶ予定が遅れたようだが、 .NET Core 1.0 の RC2 が5月中旬にリリースされた。

2000年に初めて .NET がリリースされてから、 mono が登場したりといったことはあったものの、 16年越しでついに クロスプラットフォームのアプリケーションを作成できる環境が、マイクロソフト公式から提供された。

ということで、さっそく .NET から VS2015 DotNetCore Tools Preview 1 をインストールして、 Hello World を書いてみよう。

using System;
using System.Text;

namespace netcore_console_test_01 {
    public class Program {
        public static void Main(string[] args) {
            Console.WriteLine("はろー わ~るど!");
        }
    }
}

160530_1

…文字化けするぞ!!?

続きを読む