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 keToLookup( 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
bukannyaReadAllLines
dan Anda kode mungkin akan lebih cepatBenchmarkDotnet
pengukuran perf aktual. Juga, coba dan isolasi kode aktual yang ingin Anda ukur dan tidak menyertakan IO dalam pengujian.Jawaban:
Saya berhasil mereplikasi masalah dengan kode sederhana di bawah ini:
Penting bahwa anggota tuple yang dibuat adalah string. Menghapus keduanya
.ToString()
dari kode di atas menghilangkan keuntungan dariToArray
. NET Framework. Berperilaku sedikit berbeda dari .NET Core, karena cukup menghapus hanya yang pertama.ToString()
untuk menghilangkan perbedaan yang diamati.Saya tidak tahu mengapa ini terjadi.
sumber
ToArray
atauToList
memaksa data berada dalam memori yang berdekatan; melakukan pemaksaan pada tahap tertentu dalam pipa, meskipun menambah biaya, dapat menyebabkan operasi di kemudian hari memiliki lebih sedikit cache prosesor yang hilang; cache prosesor sangat mahal.