.csvファイルを解析してルックアップする短いメソッドがあります。
ILookup<string, DgvItems> ParseCsv( string fileName )
{
var file = File.ReadAllLines( fileName );
return file.Skip( 1 ).Select( line => new DgvItems( line ) ).ToLookup( item => item.StocksID );
}
そして、DgvItemsの定義:
public class DgvItems
{
public string DealDate { get; }
public string StocksID { get; }
public string StockName { get; }
public string SecBrokerID { get; }
public string SecBrokerName { get; }
public double Price { get; }
public int BuyQty { get; }
public int CellQty { get; }
public DgvItems( string line )
{
var split = line.Split( ',' );
DealDate = split[0];
StocksID = split[1];
StockName = split[2];
SecBrokerID = split[3];
SecBrokerName = split[4];
Price = double.Parse( split[5] );
BuyQty = int.Parse( split[6] );
CellQty = int.Parse( split[7] );
}
}
そして、ToArray()
前に次のToLookup()
ように追加すると、
static ILookup<string, DgvItems> ParseCsv( string fileName )
{
var file = File.ReadAllLines( fileName );
return file.Skip( 1 ).Select( line => new DgvItems( line ) ).ToArray().ToLookup( item => item.StocksID );
}
後者は大幅に高速です。具体的には、140万行のテストファイルを使用すると、前者は約4.3秒、後者は約3秒かかります。
ToArray()
少し時間がかかるはずなので、もっと時間がかかると思います。なぜ実際に速いのですか?
追加情報:
同じ.csvファイルを別の形式に解析する別の方法があり、約3秒かかるため、この問題は3秒で同じことができるはずだと考えました。
元のデータ型はで
Dictionary<string, List<DgvItems>>
あり、元のコードはlinqを使用していないため、結果は類似しています。
BenchmarkDotNetテストクラス:
public class TestClass
{
private readonly string[] Lines;
public TestClass()
{
Lines = File.ReadAllLines( @"D:\20110315_Random.csv" );
}
[Benchmark]
public ILookup<string, DgvItems> First()
{
return Lines.Skip( 1 ).Select( line => new DgvItems( line ) ).ToArray().ToLookup( item => item.StocksID );
}
[Benchmark]
public ILookup<string, DgvItems> Second()
{
return Lines.Skip( 1 ).Select( line => new DgvItems( line ) ).ToLookup( item => item.StocksID );
}
}
結果:
| Method | Mean | Error | StdDev |
|------- |--------:|---------:|---------:|
| First | 2.530 s | 0.0190 s | 0.0178 s |
| Second | 3.620 s | 0.0217 s | 0.0203 s |
元のコードに基づいて別のテストベースを実行しました。問題はLinqではないようです。
public class TestClass
{
private readonly string[] Lines;
public TestClass()
{
Lines = File.ReadAllLines( @"D:\20110315_Random.csv" );
}
[Benchmark]
public Dictionary<string, List<DgvItems>> First()
{
List<DgvItems> itemList = new List<DgvItems>();
for ( int i = 1; i < Lines.Length; i++ )
{
itemList.Add( new DgvItems( Lines[i] ) );
}
Dictionary<string, List<DgvItems>> dictionary = new Dictionary<string, List<DgvItems>>();
foreach( var item in itemList )
{
if( dictionary.TryGetValue( item.StocksID, out var list ) )
{
list.Add( item );
}
else
{
dictionary.Add( item.StocksID, new List<DgvItems>() { item } );
}
}
return dictionary;
}
[Benchmark]
public Dictionary<string, List<DgvItems>> Second()
{
Dictionary<string, List<DgvItems>> dictionary = new Dictionary<string, List<DgvItems>>();
for ( int i = 1; i < Lines.Length; i++ )
{
var item = new DgvItems( Lines[i] );
if ( dictionary.TryGetValue( item.StocksID, out var list ) )
{
list.Add( item );
}
else
{
dictionary.Add( item.StocksID, new List<DgvItems>() { item } );
}
}
return dictionary;
}
}
結果:
| Method | Mean | Error | StdDev |
|------- |--------:|---------:|---------:|
| First | 2.470 s | 0.0218 s | 0.0182 s |
| Second | 3.481 s | 0.0260 s | 0.0231 s |
2
私はテストコード/測定を非常に疑っています。時間を計算するコードを投稿してください
—
Erno
私の推測では、がない場合
—
kimbaudi
.ToArray()
、への呼び出しはへ.Select( line => new DgvItems( line ) )
の呼び出しの前にIEnumerable を返しますToLookup( item => item.StocksID )
。また、特定の要素を検索することは、IEnumerableをArrayを使用するよりも悪くします。おそらく、ienumerableを使用するよりも、配列に変換してルックアップを実行する方が高速です。
サイドノート:置く
—
Dmitry Bychenko
var file = File.ReadLines( fileName );
- ReadLines
代わりにReadAllLines
、コードはおそらくより速くなるでしょう
BenchmarkDotnet
実際のパフォーマンス測定に使用する必要があります。また、測定したい実際のコードをテストして分離し、IOをテストに含めないでください。
なぜこれが反対票を投じたのかわかりません-いい質問だと思います。
—
Rufus L