Question

Interlocked.Increment (ref x) est-il plus rapide ou plus lent que x ++ pour les ints et les extensions sur diverses plates-formes?

Était-ce utile?

La solution

Il est plus lent puisqu'il force l'action à se dérouler de manière atomique et agit comme une barrière de mémoire, éliminant ainsi la capacité du processeur de réorganiser les accès mémoire autour de l'instruction.

Vous devriez utiliser Interlocked.Increment lorsque vous souhaitez que l'action soit atomique à l'état pouvant être partagé entre les threads - elle n'est pas destinée à remplacer intégralement x ++.

Autres conseils

Selon notre expérience, InterlockedIncrement () et al sur Windows ont des impacts assez importants. Dans un exemple, nous avons pu éliminer le verrouillage et utiliser ++ / - à la place. Cela seul réduit le temps d'exécution de 140 secondes à 110 secondes. Mon analyse est que le verrouillage force un aller-retour en mémoire (sinon, comment les autres noyaux le verraient-ils?). Une lecture / écriture en cache L1 correspond à environ 10 cycles d’horloge, mais une lecture / écriture en mémoire ressemble davantage à 100.

Dans cet exemple, j'ai estimé le nombre d'opérations d'incrément / décrément à environ 1 milliard. Ainsi, sur un processeur 2Ghz, cela correspond à environ 5 secondes pour le ++ / - et 50 secondes pour le verrouillage. Répartissez la différence sur plusieurs threads et son nombre proche de 30 secondes.

Pensez-y un instant, et vous réaliserez qu'un appel Incrémenter ne peut être plus rapide qu'une simple application de l'opérateur d'incrément. Si c'était le cas, alors l'implémentation de l'opérateur d'incrémentation par le compilateur appellerait Increment en interne, et ils effectueraient les mêmes opérations.

Mais, comme vous pouvez le constater en le testant vous-même, ils ne fonctionnent pas de la même manière.

Les deux options ont des objectifs différents. Utilisez l'opérateur d'incrémentation en général. Utilisez Increment lorsque vous souhaitez que l'opération soit atomique et que vous soyez certain que tous les autres utilisateurs de cette variable utilisent également des opérations verrouillées. (S'ils ne coopèrent pas tous, cela n'aide pas vraiment.)

C'est plus lent. Cependant, c’est la méthode générale la plus performante que je connaisse pour assurer la sécurité des threads sur les variables scalaires.

Il sera toujours plus lent car il doit effectuer un verrouillage du bus de la CPU plutôt que de simplement mettre à jour un registre. Cependant, les processeurs modernes atteignent des performances proches des registres, de sorte qu’elles sont négligeables même en traitement en temps réel.

Mon test de performance:

volatile: 65 174 400

verrouiller: 62 428 600

verrouillé: 113 248 900

TimeSpan span = TimeSpan.FromSeconds(5);

object syncRoot = new object();
long test = long.MinValue;

Do(span, "volatile", () => {

    long r = Thread.VolatileRead(ref test);

    r++;

    Thread.VolatileWrite(ref test, r);
});

Do(span, "lock", () =>
{
    lock (syncRoot)
    {
        test++;
    }
});

Do(span, "interlocked", () =>
{
    Interlocked.Increment(ref test);
});
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top