Kami memiliki metode singkat yang mem-parsing file .csv ke pencarian:
ILookup<string, DgvItems> ParseCsv( string fileName )
{
var file = File.ReadAllLines( fileName );
return file.Skip( 1 ).Select( line => new DgvItems( line ) ).ToLookup( item => item.StocksID );
}
Dan definisi 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] );
}
}
Dan kami menemukan bahwa jika kami menambahkan ekstra ToArray()
sebelum ToLookup()
seperti ini:
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 );
}
Yang terakhir ini secara signifikan lebih cepat. Lebih khusus lagi, ketika menggunakan file uji dengan 1,4 juta baris, yang pertama membutuhkan waktu sekitar 4,3 detik dan yang terakhir membutuhkan waktu sekitar 3 detik.
Saya berharap ToArray()
harus mengambil waktu ekstra sehingga yang terakhir harus sedikit lebih lambat. Mengapa sebenarnya lebih cepat?
Informasi tambahan:
Kami menemukan masalah ini karena ada metode lain yang mengurai file .csv yang sama ke format yang berbeda dan dibutuhkan sekitar 3 detik sehingga kami pikir yang satu ini harus dapat melakukan hal yang sama dalam 3 detik.
Tipe data asli adalah
Dictionary<string, List<DgvItems>>
dan kode asli tidak menggunakan LINQ dan hasilnya mirip.
Kelas tes 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 );
}
}
Hasil:
| Method | Mean | Error | StdDev |
|------- |--------:|---------:|---------:|
| First | 2.530 s | 0.0190 s | 0.0178 s |
| Second | 3.620 s | 0.0217 s | 0.0203 s |
Saya melakukan tes dasar lain pada kode asli. Tampaknya masalahnya bukan pada 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;
}
}
Hasil:
| Method | Mean | Error | StdDev |
|------- |--------:|---------:|---------:|
| First | 2.470 s | 0.0218 s | 0.0182 s |
| Second | 3.481 s | 0.0260 s | 0.0231 s |
.ToArray()
, panggilan untuk .Select( line => new DgvItems( line ) )
mengembalikan IEnumerable sebelum panggilan ke ToLookup( item => item.StocksID )
. Dan mencari elemen tertentu lebih buruk menggunakan IEnumerable daripada Array. Mungkin lebih cepat untuk mengkonversi ke array dan melakukan pencarian daripada menggunakan ienumerable.
var file = File.ReadLines( fileName );
- ReadLines
bukannya ReadAllLines
dan Anda kode mungkin akan lebih cepat
BenchmarkDotnet
pengukuran perf aktual. Juga, coba dan isolasi kode aktual yang ingin Anda ukur dan tidak menyertakan IO dalam pengujian.