6

これが私が今晩セットアップしたテストです。何か違うことを証明するために作られましたが、期待通りの結果にはなりませんでした。

IQueryableで10000のランダムクエリを使用してテストを実行しています。テスト中に、リストで同じことを実行すると、テストが20倍高速になることがわかりました。

下記参照。私のCarBrandManager.GetListは元々IQueryableを返しますが、今では最初にToList()を発行し、それからずっと速くなります。

なぜ私がこの大きな違いを見るのかについて誰かに教えてもらえますか?

var sw = new Stopwatch();
sw.Start();

int queries = 10000;

//IQueryable<Model.CarBrand> carBrands = CarBrandManager.GetList(context);
List<Model.CarBrand> carBrands = CarBrandManager.GetList(context).ToList();

Random random = new Random();
int randomChar = 65;

for (int i = 0; i < queries; i++)
{
    randomChar = random.Next(65, 90);
    Model.CarBrand carBrand = carBrands.Where(x => x.Name.StartsWith(((char)randomChar).ToString())).FirstOrDefault();
}

sw.Stop();
lblStopWatch.Text = String.Format("Queries: {0} Elapsed ticks: {1}", queries, sw.ElapsedTicks);
4

1 に答える 1

13

ここでは、潜在的に2つの問題が発生しています。GetList(context)まず、それが実装する知識を除けば、どのタイプのコレクションが返されるかは明らかではありませんIQueryable。つまり、結果を評価するときは、SQLクエリを作成し、そのクエリをデータベースに送信して、結果をオブジェクトに具体化することが非常に適切である可能性があります。または、XMLファイルを解析している可能性があります。または、RSSフィードをダウンロードするか、インターネットでODataエンドポイントを呼び出します。これらは明らかに、メモリ内の短いリストを単にフィルタリングするよりも時間がかかります。(結局のところ、実際にいくつの車のブランドが存在することができますか?)

しかし、それが返す実装が実際にはであると仮定しましょうList。したがって、テストしている唯一の違いは、それがIEnumerableまたはとしてキャストされているかどうかですIQueryableEnumerableクラスの拡張メソッドのメソッドシグネチャを。のメソッドシグネチャと比較しQueryableます。リストをIQueryableとして扱う場合、直接実行できるsExpressionだけでなく、評価する必要のあるsを渡します。Func

Entity FrameworkなどのカスタムLINQプロバイダーを使用している場合、これにより、フレームワークは実際の式ツリーを評価し、それらからSQLクエリとマテリアライゼーションプランを生成できます。ただし、LINQ to Objectsはメモリ内のラムダ式を評価するだけなので、リフレクションを使用するか、式をFuncsにコンパイルする必要があります。どちらも、パフォーマンスに大きな影響を及ぼします。

.ToList()単に呼び出すか、結果セットでs.AsEnumerable()を使用するように強制したくなるかもしれませんが、情報隠蔽の観点からは、これは間違いです。メソッドから返されるデータは、ある種のメモリ内オブジェクトであることがわかっていると想定します。現時点ではそうかもしれませんし、そうでないかもしれません。とにかく、それはのために定義されている契約の一部ではありませんFuncGetList(context)GetList(context)方法であるため、常にそのようになるとは限りません。返されるタイプは、クエリできるものである可能性が非常に高いと想定する必要があります。また、現時点で検索できる自動車ブランドはおそらく12しかありませんが、いつかは数千になる可能性があります(ここではプログラミングの実践について話しているのですが、必ずしも自動車業界の場合とは限りません。 )。したがって、現在の場合でも、車のリスト全体をダウンロードしてメモリでフィルタリングする方が常に高速であると想定するべきではありません。

CarBrandManager.GetList(context)がカスタムLINQプロバイダー(Entity Frameworkコレクションなど)に基づくオブジェクトを返す可能性がある場合は、データキャストをIQueryableのままにしておくことをお勧めします。ベンチマークではリストの使用が20倍高速であることが示されていますが、その違いは非常に小さいため、ユーザーが違いを区別することはできません。.Where().Take().Skip()いつの日か、データストアから本当に必要なデータを呼び出してロードするだけで、パフォーマンスが数桁向上することがありますが.ToList()、すぐに呼び出すと、テーブル全体がシステムのメモリにロードされることになります。

ただし、それが常にメモリ内リストを返すことがわかっている場合(名前が示すように)、。の代わりにを返すように変更する必要があります。または、.NET 4.5を使用している場合は、従わせる契約に応じて、おそらくまたはを使用します。CarBrandManager.GetList(context)IEnumerable<Model.CarBrand>IQueryable<Model.CarBrand>IReadOnlyList<Model.CarBrand>IReadOnlyCollection<Model.CarBrand>CarManager

于 2012-10-25T22:21:51.017 に答える