2010-07-19 5 views
2

En arrière-plan, j'essaie de faire une simulation de bâtiment à grande échelle.Multiplication de liste rapide en C#

Le problème est que j'ai une liste du type personnalisé Point3D que je veux faire une multiplication de tableau rapide sur elle. Donc, à un pas de temps différent, je devrais avoir une valeur double avec le Point3D (j'ai surchargé l'opération de multiplication et de division de Point3D) pour chaque Point3D, le résultat sera alors stocké dans un Dictionary<double,List<Point3D>>. La clé de ce dictionnaire est le pas de temps différent, et la valeur est le déplacement correspondant.

Depuis que j'ai beaucoup de DOF, et beaucoup de pas de temps, il semble que l'opération ci-dessus est très lente. Y a-t-il moyen d'optimiser l'ensemble de l'opération?

Ceci est mon code actuel, et il est extrêmement lent. J'ai donc besoin d'idées pour l'optimiser.

public static Dictionary<double, List<Point3D>> ComputeTimeSeries(Dictionary<double, double> timeStep, List<Point3D> dofs) 
{ 
    var timeSeries = new Dictionary<double, List<Point3D>>(); 
    foreach(var keyValue in timeStep) 
    { 
     // the point3d*double operation is already being overloaded. 
     timeSeries.Add(keyValue.Key, dofs.Select(pt=>pt*keyValue.Value).ToList()); 
    } 
    return timeSeries; 
} 

Remarque: Je suis toujours bloqué sur .Net 3.5. Donc, probablement PLINQ et TPL ne va pas aider

+0

Quelle est exactement la valeur du pas de temps? Est-ce une forme de temps d'échantillonnage? L'intervalle entre les timesteps est-il différent? Je me demandais juste si l'utilisation d'un type de dictionnaire est un must? – ChrisBD

+0

La valeur de temps est une forme de temps échantillonné, avec une augmentation régulière entre les valeurs. L'intervalle entre les timesteps est le même. Et non, l'utilisation du dictionnaire n'est pas nécessaire. Mais l'utilisation du dictionnaire est-elle un problème? – Graviton

+0

Si vous n'avez pas besoin d'un accès rapide par clé, alors bien sûr Array ou simple liste sera préférable. –

Répondre

0

Je ne suis pas un expert en C#, mais peut-être le

dofs.Select(pt=>pt*keyValue.Value).ToList() 

partie pourrait bénéficier de parallélisation. En utilisant SIMD instructions et/ou discussions, vous pouvez effectuer dofs[0] *= keyValue.Value et dofs[1] *= keyValue.Value etc. en parallèle.

Ce code ressemble beaucoup au premier exemple donné dans l'article Optimize Managed Code For Multi-Core Machines. vous pourriez peut-être réécrire ce qui précède à quelque chose comme

Parallel.For(0, dofs.Length, delegate(int i) { 
    dofs[i] *= keyValue.Value; 
}); 
+0

Je suis toujours bloqué à .net 3.5, donc toutes les astuces cool PLINQ et TPL ne vont probablement pas aider – Graviton

1

Pour commencer, vous pouvez éliminer une réallocation et la copie à l'aide d'un paramètre de capacité lors de la création du nouveau dictionnaire:

var timeSeries = new Dictionary<double, List<Point3D>>(timeStep.Count); 

Et le code dans la boucle foreach semble indépendant l'un de l'autre, cela signifie que vous pouvez l'exécuter en parallèle. Dans ce .NET4 est aussi facile que de remplacer:

foreach(var keyValue in timeStep) { ... } 

avec

Parallel.Foreach(timestep, key, (key) => ...); 
+0

Malheureusement, je suis toujours bloqué à .net 3.5 – Graviton

+0

@Ngu: mais utilisez la capacité. –

1

Profiler vous donnera quelques idées. Aussi, essayez d'échapper à LINQ

public static Dictionary<double, List<Point3D>> ComputeTimeSeries(Dictionary<double, double> timeStep, List<Point3D> dofs) 
{ 
    var timeSeries = new Dictionary<double, List<Point3D>>(); 
    foreach(var keyValue in timeStep) 
    { 
     List<double> lst = new List<double>(); 
     foreach (Point3D point in dofs) 
     lst.Add(point* keyValue.Value); 

     timeSeries.Add(keyValue.Key, lst); // the point3d*double operation is already being overloaded. 
    } 
    return timeSeries; 
} 
+0

Je suis assez sûr que le code ci-dessus est le goulot d'étranglement, une idée d'où d'autre à profiler? – Graviton

+1

Le code de la question comporte plusieurs parties, le profil _is_ est une bonne idée. –

+2

Peut être .ToList() est un goulot d'étranglement ou une multiplication. –

0

Si vous pouviez changer la valeur de retour Dictionary<double, List<Point3D>>-Dictionary<double, IEnumerable<Point3D>> vous pouvez reporter le calcul réel jusqu'à ce qu'il soit nécessaire.

Vous pouvez supprimer le .ToList() et finir par ce qui suit:

public static Dictionary<double, IEnumerable<Point3D>> ComputeTimeSeries(Dictionary<double, double> timeStep, List<Point3D> dofs) 
{ 
    var timeSeries = new Dictionary<double, List<Point3D>>(); 
    foreach(var keyValue in timeStep)  
    { 
     // the point3d*double operation is already being overloaded. 
     timeSeries.Add(keyValue.Key, dofs.Select(pt=>pt*keyValue.Value)); 
    } 
    return timeSeries; 
} 

Maintenant, les calculs seraient effectués lorsque vous avez commencé à énumérer les valeurs au lieu de l'intérieur de la méthode ComputeTimeSeries. Cela ne rendrait pas les calculs plus rapides, mais vous les étaleriez probablement à temps, peut-être même à travers de nombreux threads.

+0

Mais cela signifierait aussi que le calcul est fait plusieurs fois, si l'énumération est énumérée plus d'une fois. D'ailleurs: je doute que la multiplication soit le vrai goulot d'étranglement. Pour chaque multiplication, il y a quelques appels d'interface/de délégué, qui sont * beaucoup * plus chers qu'une multiplication. – Niki

+0

Cela dépend de la façon dont les séries sont utilisées, mais oui. Il devrait être conscient de cela. –

2

Je voudrais essayer quelque chose comme ceci:

public static Dictionary<double, Point3D[]> ComputeTimeSeries(Dictionary<double, double> timeStep, Point3D[] dofs) 
{ 
    var timeSeries = new Dictionary<double, Point3D[]>(); 
    foreach(var keyValue in timeStep) 
    { 
     var tempArray = new Point3D[dofs.Length]; 
     for (int index=0; index < dofs.Length; index++) 
      tempArray[index] = dofs[index] * keyValue.Value; 
     timeSeries.Add(keyValue.Key, tempArray); 
    } 
    return timeSeries; 
} 

aide de l'option/ToList est plus lisible, mais les appels d'interface supplémentaires sont très coûteux par rapport à une simple multiplication.

+0

@nikie: Une optimisation serait de déclarer tempArray en dehors de la boucle foreach pour éviter le risque d'allocations multiples. Je ne pense pas que les allocations supplémentaires seront optimisées. – Brian

+0

@Brian: Je ne comprends pas. Si j'attribue une seule instance, comment le dictionnaire contiendrait-il plus d'une version différente des données par la suite? – Niki

+0

Oh, vous avez raison. Je suis un idiot. – Brian