J'ai remarqué qu'une structure enveloppant un seul flottant est significativement plus lente que d'utiliser un flottant directement, avec environ la moitié de la performance.Pourquoi l'ajout d'un champ supplémentaire à struct améliore grandement ses performances?
using System;
using System.Diagnostics;
struct Vector1 {
public float X;
public Vector1(float x) {
X = x;
}
public static Vector1 operator +(Vector1 a, Vector1 b) {
a.X = a.X + b.X;
return a;
}
}
Cependant, lors de l'ajout d'un champ « extra » supplémentaire, la magie semble se produire et la performance devient de nouveau plus raisonnable:
struct Vector1Magic {
public float X;
private bool magic;
public Vector1Magic(float x) {
X = x;
magic = true;
}
public static Vector1Magic operator +(Vector1Magic a, Vector1Magic b) {
a.X = a.X + b.X;
return a;
}
}
Le code que je l'habitude de référence ci se présente comme suit:
class Program {
static void Main(string[] args) {
int iterationCount = 1000000000;
var sw = new Stopwatch();
sw.Start();
var total = 0.0f;
for (int i = 0; i < iterationCount; i++) {
var v = (float) i;
total = total + v;
}
sw.Stop();
Console.WriteLine("Float time was {0} for {1} iterations.", sw.Elapsed, iterationCount);
Console.WriteLine("total = {0}", total);
sw.Reset();
sw.Start();
var totalV = new Vector1(0.0f);
for (int i = 0; i < iterationCount; i++) {
var v = new Vector1(i);
totalV += v;
}
sw.Stop();
Console.WriteLine("Vector1 time was {0} for {1} iterations.", sw.Elapsed, iterationCount);
Console.WriteLine("totalV = {0}", totalV);
sw.Reset();
sw.Start();
var totalVm = new Vector1Magic(0.0f);
for (int i = 0; i < iterationCount; i++) {
var vm = new Vector1Magic(i);
totalVm += vm;
}
sw.Stop();
Console.WriteLine("Vector1Magic time was {0} for {1} iterations.", sw.Elapsed, iterationCount);
Console.WriteLine("totalVm = {0}", totalVm);
Console.Read();
}
}
Avec les résultats de référence:
Float time was 00:00:02.2444910 for 1000000000 iterations.
Vector1 time was 00:00:04.4490656 for 1000000000 iterations.
Vector1Magic time was 00:00:02.2262701 for 1000000000 iterations.
paramètres du compilateur/environnement: OS: Windows 10 64 bits Toolchain: VS2017 Cadre: .Net 4.6.2 cible: Tout processeur Prefer 32 bits
Si 64 bits est défini comme la cible, nos résultats sont plus prévisibles, mais bien pire que ce que nous voyons avec Vector1Magic sur le 32 bit cible:
Float time was 00:00:00.6800014 for 1000000000 iterations.
Vector1 time was 00:00:04.4572642 for 1000000000 iterations.
Vector1Magic time was 00:00:05.7806399 for 1000000000 iterations.
Pour les sorciers réels, j'ai inclus une décharge de l'iL ici: https://pastebin.com/sz2QLGEx
Une recherche plus poussée indique que cela semble être spécifique à l'exécution de Windows, car le compilateur mono produit la même IL. Sur le mono runtime, les deux variantes de struct ont des performances environ 2x plus lentes par rapport au flottant brut. C'est assez différent de la performance que nous voyons sur .Net.
Que se passe-t-il ici?
* Notez que cette question comportait à l'origine un processus de référence imparfait (merci Max Payne pour l'avoir signalé), et a été mise à jour pour refléter plus fidèlement les horaires.
Im devinant cela est dû à l'emballage struct maintenant avoir un meilleur alignement de la mémoire. –
Vous devez ajouter une itération de préchauffage pour exclure les interférences possibles de JIT ou d'autres traitements ponctuels. – PetSerAl
Si je passe en 64 bits, j'obtiens des performances moins bonnes pour votre vecteur "magique". – Adrian