Nous avons une méthode courte qui analyse le fichier .csv en recherche:
ILookup<string, DgvItems> ParseCsv( string fileName )
{
var file = File.ReadAllLines( fileName );
return file.Skip( 1 ).Select( line => new DgvItems( line ) ).ToLookup( item => item.StocksID );
}
Et la définition de 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] );
}
}
Et nous avons constaté que si nous ajoutons un supplément ToArray()
avant ToLookup()
comme ceci:
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 );
}
Ce dernier est nettement plus rapide. Plus précisément, lorsque vous utilisez un fichier de test avec 1,4 million de lignes, le premier prend environ 4,3 secondes et le dernier prend environ 3 secondes.
Je pense que cela ToArray()
devrait prendre plus de temps, donc ce dernier devrait être légèrement plus lent. Pourquoi est-ce réellement plus rapide?
Informations supplémentaires:
Nous avons trouvé ce problème car il existe une autre méthode qui analyse le même fichier .csv dans un format différent et cela prend environ 3 secondes, nous pensons donc que celui-ci devrait être capable de faire la même chose en 3 secondes.
Le type de données d'origine est
Dictionary<string, List<DgvItems>>
et le code d'origine n'a pas utilisé linq et le résultat est similaire.
Classe de test 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 );
}
}
Résultat:
| Method | Mean | Error | StdDev |
|------- |--------:|---------:|---------:|
| First | 2.530 s | 0.0190 s | 0.0178 s |
| Second | 3.620 s | 0.0217 s | 0.0203 s |
J'ai fait une autre base de test sur le code original. Semble que le problème n'est pas sur 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;
}
}
Résultat:
| 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'appel à.Select( line => new DgvItems( line ) )
renvoie un IEnumerable avant l'appel àToLookup( item => item.StocksID )
. Et rechercher un élément particulier est pire en utilisant IEnumerable que Array. Probablement plus rapide pour convertir en tableau et effectuer une recherche que d'utiliser un ienumerable.var file = File.ReadLines( fileName );
-ReadLines
au lieu deReadAllLines
et votre code sera probablement plus rapideBenchmarkDotnet
pour la mesure réelle de la perf. Essayez également d'isoler le code réel que vous souhaitez mesurer et n'incluez pas d'E / S dans le test.Réponses:
J'ai réussi à reproduire le problème avec le code simplifié ci-dessous:
Il est important que les membres du tuple créé soient des chaînes. Supprimer les deux
.ToString()
du code ci-dessus élimine l'avantage deToArray
. Le .NET Framework se comporte un peu différemment du .NET Core, car il suffit de supprimer uniquement le premier.ToString()
pour éliminer la différence observée.Je ne sais pas pourquoi cela se produit.
la source
ToArray
ouToList
force les données à être dans la mémoire contiguë; faire ce forçage à un stade particulier du pipeline, même s'il ajoute du coût, peut entraîner une opération ultérieure pour avoir moins de ratés de cache de processeur; les erreurs de cache du processeur sont étonnamment coûteuses.