Chúng tôi có một phương pháp ngắn phân tích tệp .csv để tra cứu:
ILookup<string, DgvItems> ParseCsv( string fileName )
{
var file = File.ReadAllLines( fileName );
return file.Skip( 1 ).Select( line => new DgvItems( line ) ).ToLookup( item => item.StocksID );
}
Và định nghĩa của 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] );
}
}
Và chúng tôi thấy rằng nếu chúng tôi thêm một phần ToArray()
trước ToLookup()
như thế này:
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 );
}
Thứ hai là nhanh hơn đáng kể. Cụ thể hơn, khi sử dụng tệp thử nghiệm với 1,4 triệu dòng, cái trước mất khoảng 4,3 giây và cái sau mất khoảng 3 giây.
Tôi hy vọng ToArray()
sẽ mất thêm thời gian để sau này nên chậm hơn một chút. Tại sao nó thực sự nhanh hơn?
Thông tin bổ sung:
Chúng tôi đã tìm thấy sự cố này vì có một phương pháp khác phân tích cùng một tệp .csv sang định dạng khác và mất khoảng 3 giây nên chúng tôi nghĩ rằng phương pháp này sẽ có thể thực hiện điều tương tự trong 3 giây.
Kiểu dữ liệu gốc là
Dictionary<string, List<DgvItems>>
và mã gốc không sử dụng linq và kết quả là tương tự nhau.
Lớp kiểm tra điểm chuẩnDotNet:
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 );
}
}
Kết quả:
| Method | Mean | Error | StdDev |
|------- |--------:|---------:|---------:|
| First | 2.530 s | 0.0190 s | 0.0178 s |
| Second | 3.620 s | 0.0217 s | 0.0203 s |
Tôi đã làm một thử nghiệm khác dựa trên mã gốc. Có vẻ như vấn đề không nằm ở 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;
}
}
Kết quả:
| 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()
, lệnh gọi .Select( line => new DgvItems( line ) )
trả về IEnumerable trước khi gọi đến ToLookup( item => item.StocksID )
. Và việc tìm kiếm một yếu tố cụ thể còn tệ hơn khi sử dụng IEnumerable so với Array. Có lẽ nhanh hơn để chuyển đổi thành một mảng và thực hiện tra cứu hơn là sử dụng một số lượng lớn.
var file = File.ReadLines( fileName );
- ReadLines
thay vì ReadAllLines
và mã của bạn có thể sẽ nhanh hơn
BenchmarkDotnet
để đo hoàn hảo thực tế. Ngoài ra, hãy thử và cô lập mã thực tế bạn muốn đo và không bao gồm IO trong thử nghiệm.