Question C # énorme baisse de performance affectant une valeur flottante


J'essaie d'optimiser mon code et j'exécute VS Performance Monitor sur celui-ci.

enter image description here

Cela montre que la simple attribution de float prend une grande partie de la puissance de calcul? Je ne comprends pas comment c'est possible.

Voici le code pour TagData:

public class TagData
{
    public int tf;
    public float tf_idf;
}

Donc tout ce que je fais vraiment c'est:

float tag_tfidf = td.tf_idf;

Je suis confus.


13
2017-12-05 20:35


origine


Réponses:


Je posterai une autre théorie: ce pourrait être le manque de cache du premier accès aux membres de td. Une charge de mémoire prend de 100 à 200 cycles, ce qui, dans ce cas, semble représenter environ 1/3 de la durée totale de la méthode.

Points à tester cette théorie:

  1. Votre ensemble de données est-il grand? Il parie que c'est le cas.
  2. Accédez-vous à la TagDataest en ordre de mémoire aléatoire? Je parie qu'ils ne sont pas séquentiels en mémoire. Cela entraîne un dysfonctionnement du pré-récupérateur de mémoire de la CPU.
  3. Ajouter une nouvelle ligne int dummy = td.tf; avant la ligne chère. Cette nouvelle ligne sera désormais la ligne la plus chère car elle déclenchera l’échec du cache. Trouvez un moyen d'effectuer une opération de chargement factice que le JIT n'optimise pas. Peut-être ajouter tout td.tf valeurs à un local et passer cette valeur à GC.KeepAlive à la fin de la méthode. Cela devrait garder la charge de mémoire dans le x86 émis par JIT.

Je peux me tromper mais contrairement aux autres théories, la mienne est testable.

Essayer de faire TagData une struct. Cela fera tous les articles de term.tags séquentiel en mémoire et vous donner un bon coup de pouce de performance.


7
2017-12-05 21:04



Utilisez-vous LINQ? Si c'est le cas, LINQ utilise l'énumération paresseuse. La première fois que vous accédez à la valeur que vous avez retirée, cela va être pénible.

Si vous utilisez LINQ, appelez ToList () après votre requête pour ne payer qu'une seule fois le prix.

On dirait aussi que votre structure de données est sous optimale mais comme je n'ai pas accès à votre source (et que je ne pouvais probablement pas vous aider même si j'en avais :)), je ne peux pas vous dire ce qui serait mieux.

EDIT: Comme les commentateurs l’ont souligné, LINQ n’est peut-être pas à blâmer; Cependant, ma question est basée sur le fait que les deux instructions foreach utilisent IEnumerable. L'attribution TagData est un pointeur sur l'élément dans la collection de IEnumerable (qui peut ou non avoir été énuméré). Le premier accès aux données légitimes est la ligne qui extrait la propriété de l'objet. La première fois que cela se produit, il est possible qu’il exécute l’instruction LINQ entière et que le profilage utilise la moyenne, il peut être désactivé. On peut en dire autant pour tagScores (que je suppose être une base de données) dont le premier accès est très lent puis accélère. Je ne soulignais pas la solution juste un problème possible étant donné ma compréhension de IEnumerable.

Voir http://odetocode.com/blogs/scott/archive/2008/10/01/lazy-linq-and-enumerable-objects.aspx


1
2017-12-05 20:42



Comme nous pouvons voir que la prochaine ligne à la suspecte prend seulement 0.6 c'est à dire

float tag_tfidf = td.tf_idf;//29.6
string tagName =...;//0.6

Je soupçonne que cela est causé par le nombre excessif d'appels, et notez également float est un type de valeur, ce qui signifie qu'ils sont copiés par valeur. Ainsi, chaque fois que vous l’affectez, le runtime crée de nouvelles float(Single) struct et l'initialise en copiant la valeur de td.tf_idf ce qui prend beaucoup de temps.

Tu peux voir string tagName =...; ne prend pas beaucoup car il est copié par référence.

Edit: Comme les commentaires ont souligné, je peux me tromper à cet égard, cela pourrait être un bogue dans le profileur également. Essayez de re-profiler et voir si cela fait une différence.


0
2017-12-05 20:48