Performance surprise “avec” et des types nullables

Je suis juste une révision du chapitre 4 de C# en Profondeur qui traite avec des types nullables, et je vais ajouter une section sur l'utilisation de l' "que" de l'opérateur, qui permet d'écrire:

object o = ...;
int? x = o as int?;
if (x.HasValue)
{
    ... //Use x.Value in here
}

Je pensais que c'était vraiment bien, et qu'elle pourrait améliorer les performances sur le C# 1 équivalent, à l'aide de "est", suivi par un casting - après tout, de cette façon, nous avons seulement besoin de demander la dynamique de la vérification de type à la fois, et puis une simple vérification de valeur.

Cela ne semble pas être le cas, cependant. J'ai inclus un exemple de test d'application ci-dessous, qui, fondamentalement, résume tous les entiers dans un tableau d'objets - mais le tableau contient beaucoup de références nulles et de la chaîne de références ainsi que des entiers en boîte. L'indice de référence des mesures le code que vous auriez à utiliser en C# 1, le code en utilisant le "comme" de l'opérateur, et juste pour le plaisir d'un LINQ solution. À mon grand étonnement, le C# 1 du code est 20 fois plus rapide dans ce cas - et même le code LINQ (que je devrais devrait être plus lente, compte tenu de la itérateurs impliqués) bat le "comme" de code.

Est la .NET de la mise en œuvre de isinst pour les types nullables vraiment lent? Est-ce la supplémentaires unbox.any que les causes du problème? Est-il une autre explication pour cela? Au moment où il se sent comme je vais avoir à inclure une mise en garde contre l'utilisation de cette performance des situations délicates...

Résultats:

Cast: 10000000 : 121

Comme: 10000000 : 2211

LINQ: 10000000 : 2143

Code:

using System;
using System.Diagnostics;
using System.Linq;
class Test
{
const int Size = 30000000;
static void Main()
{
object[] values = new object[Size];
for (int i = 0; i < Size - 2; i += 3)
{
values[i] = null;
values[i+1] = "";
values[i+2] = 1;
}
FindSumWithCast(values);
FindSumWithAs(values);
FindSumWithLinq(values);
}
static void FindSumWithCast(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int sum = 0;
foreach (object o in values)
{
if (o is int)
{
int x = (int) o;
sum += x;
}
}
sw.Stop();
Console.WriteLine("Cast: {0} : {1}", sum, 
(long) sw.ElapsedMilliseconds);
}
static void FindSumWithAs(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int sum = 0;
foreach (object o in values)
{
int? x = o as int?;
if (x.HasValue)
{
sum += x.Value;
}
}
sw.Stop();
Console.WriteLine("As: {0} : {1}", sum, 
(long) sw.ElapsedMilliseconds);
}
static void FindSumWithLinq(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int sum = values.OfType<int>().Sum();
sw.Stop();
Console.WriteLine("LINQ: {0} : {1}", sum, 
(long) sw.ElapsedMilliseconds);
}
}
Pourquoi ne pas regarder la jitted code? Même VS débogueur peut le montrer.
Je suis juste curieux, avez-vous testé avec le CLR 4.0 ainsi?
Bon point. Fera à un certain point (même si ce n'est pas dans VS pour le moment 🙂 @divo: Oui, et c'est pire de tous. Mais alors que dans la version bêta, donc il peut y avoir beaucoup de débogage de code.
Aujourd'hui, j'ai appris que vous pouvez utiliser as sur les types nullables. Intéressant, car il ne peut pas être utilisé sur d'autres types de valeur. En fait, le plus surprenant.
il est parfaitement logique pour elle de ne pas travailler sur des types de valeur. Pensez-y, as tente de jeter un type et si elle échoue, elle renvoie null. Vous ne pouvez pas définir des types de valeur à null

OriginalL'auteur Jon Skeet | 2009-10-17