IComparer<T> や IEqualityComparer<T> などのインターフェースを実装する際、クラスと構造体のどちらがいいのか迷ったことがあったので、インターフェースにキャストしたクラス・構造体からの、メソッド呼び出し速度を測定してみた。
[測定コード]
※コマンドプロンプトで実施(csc /optimize)
※2 回目以降は類似の結果だったため、2 回目の結果を採択
※単位は[s]
まとめると、
構造体 ≫ クラス > インターフェース(クラス) > インターフェース(構造体)
※左の方が速い
当たり前だが、インターフェースにキャストした状態からのメソッド呼び出しは、直接呼び出しよりも遅い。
そして、インターフェースにキャストした状態では、クラスより構造体の方が遅い。
理由はたぶん、インターフェース(構造体)のメソッド呼び出しは、自身(構造体インスタンス)のボックス化解除が必要だから?
インターフェースを引数に取るメソッドが、上記のように where 制約で実装されていれば構造体がベストだが、.NET Framework の標準メソッドの多くはインタフェース引数に where 制約を使っていない。
where 制約を使うと、型引数が増えて呼び出し側での指定が必要になり、コードが汚くなるからだと思われる。(参考:メソッドの型推論で型パラメータの制約は使われない)
結論としては、一般的な使い方をするなら、インターフェースはクラスで実装した方がいい。
※プロパティも実質メソッドなので、プロパティを実装するインターフェースについても同様
実際に Array.Sort や Enumerable.OrderBy で測定したところ、インターフェース(クラス)の方がいい結果になった。
Intel(R) Celeron(R) CPU G530 @ 2.40GHz/DDR3 8GB(4GB x 2)
[測定コード]
static class MeasureMethodCall
{
static void Main(string[] args)
{
var size = 10000000;
var cmpc = new ComparerClass(); // クラスベース
var cmps = new ComparerStruct(); // 構造体ベース
for( var i=0; i<11; i++ ){
Console.WriteLine("[{0}]", i);
// インターフェースにキャストした状態からのメソッド呼び出し
InterfaceCall(size, cmpc);
InterfaceCall(size, cmps);
// おまけ:直接のメソッド呼び出し
DirectCall(size, cmpc);
DirectCall(size, cmps);
}
}
static void InterfaceCall(int size, IComparer<int> comparer)
{
var sw = Stopwatch.StartNew();
for( var i=0; i<size; i++ ) comparer.Compare(0, i);
sw.Stop();
Console.WriteLine("[InterfaceCall][{0}][{1:struct;0;class }] {2}",
size, comparer.GetType().IsValueType.GetHashCode(), sw.Elapsed);
}
static void DirectCall<T>(int size, T comparer) where T : IComparer<int>
{
var sw = Stopwatch.StartNew();
for( var i=0; i<size; i++ ) comparer.Compare(0, i);
sw.Stop();
Console.WriteLine("[DirectCall ][{0}][{1:struct;0;class }] {2}",
size, comparer.GetType().IsValueType.GetHashCode(), sw.Elapsed);
}
}
class ComparerClass : IComparer<int>
{
public int Compare(int x, int y){ return x.CompareTo(y); }
}
struct ComparerStruct : IComparer<int>
{
public int Compare(int x, int y){ return x.CompareTo(y); }
}
[結果]
インターフェース(クラス) | 0.0758722 |
---|---|
インターフェース(構造体) | 0.1012821 |
クラス | 0.0674410 |
構造体 | 0.0083828 |
※2 回目以降は類似の結果だったため、2 回目の結果を採択
※単位は[s]
まとめると、
構造体 ≫ クラス > インターフェース(クラス) > インターフェース(構造体)
※左の方が速い
当たり前だが、インターフェースにキャストした状態からのメソッド呼び出しは、直接呼び出しよりも遅い。
そして、インターフェースにキャストした状態では、クラスより構造体の方が遅い。
理由はたぶん、インターフェース(構造体)のメソッド呼び出しは、自身(構造体インスタンス)のボックス化解除が必要だから?
インターフェースを引数に取るメソッドが、上記のように where 制約で実装されていれば構造体がベストだが、.NET Framework の標準メソッドの多くはインタフェース引数に where 制約を使っていない。
where 制約を使うと、型引数が増えて呼び出し側での指定が必要になり、コードが汚くなるからだと思われる。(参考:メソッドの型推論で型パラメータの制約は使われない)
結論としては、一般的な使い方をするなら、インターフェースはクラスで実装した方がいい。
※プロパティも実質メソッドなので、プロパティを実装するインターフェースについても同様
実際に Array.Sort や Enumerable.OrderBy で測定したところ、インターフェース(クラス)の方がいい結果になった。
検証環境
Windows 7 64bit/.NET 4.5.2Intel(R) Celeron(R) CPU G530 @ 2.40GHz/DDR3 8GB(4GB x 2)
0 件のコメント:
コメントを投稿