Convertir efficacement le tableau d'octets à Virgule
Si j'ai un tableau d'octets et que vous voulez convertir une zone contiguë de 16 octets du bloc de ce tableau, contenant .net est la représentation d'un Decimal
, dans une bonne Decimal
struct, quel est le moyen le plus efficace de le faire?
Voici le code qui est apparu dans mon profiler comme la plus grande CPU consommateur dans un cas que je suis à l'optimisation.
public static decimal ByteArrayToDecimal(byte[] src, int offset)
{
using (MemoryStream stream = new MemoryStream(src))
{
stream.Position = offset;
using (BinaryReader reader = new BinaryReader(stream))
return reader.ReadDecimal();
}
}
Pour se débarrasser de MemoryStream
et BinaryReader
, j'ai pensé à l'alimentation d'une matrice de BitConverter.ToInt32(src, offset + x)
s dans la Decimal(Int32[])
constructeur serait plus rapide que la solution que je vous présente ci-dessous, mais la version ci-dessous est, curieusement, deux fois plus rapide.
const byte DecimalSignBit = 128;
public static decimal ByteArrayToDecimal(byte[] src, int offset)
{
return new decimal(
BitConverter.ToInt32(src, offset),
BitConverter.ToInt32(src, offset + 4),
BitConverter.ToInt32(src, offset + 8),
src[offset + 15] == DecimalSignBit,
src[offset + 14]);
}
C'est 10 fois plus rapide comme le MemoryStream/BinaryReader
combo, et je l'ai testé avec un tas de valeurs extrêmes pour s'assurer qu'il fonctionne, mais la représentation décimale n'est pas aussi simple que pour les autres types primitifs, donc je ne suis pas encore convaincu que cela fonctionne à 100% de la possible des valeurs décimales.
En théorie, cependant, il pourrait y avoir un moyen de copier ces 16 octets contigus à un autre endroit de la mémoire et de déclarer que, pour être une Virgule, sans aucune vérification. Quelqu'un est-il au courant d'une méthode pour faire cela?
(Il n'y a qu'un seul problème: Bien que les décimales sont représentés comme des 16 octets, certaines de ces valeurs ne constituent pas valide décimales, donc faire un décochémemcpy
pourrait briser les choses...)
Ou est-il un autre moyen plus rapide?
Le problème ici n'est pas que BinaryReader est si lent, c'est que le séparateur Décimal constructeur est donc très rapide. Si bien que la surcharge de la construction de ces objets devient perceptible dans l'A/B testing. La sécurité et la vitesse sont des objectifs contradictoires.
Je n'ai pas dit
BinaryReader
est lente. Mais inutile indirections que ce soit évidemment ralentit les choses, peu importe à quelle vitesse ils sont. Si j'avais eu un BinaryReader
pour commencer, plutôt que d'un tableau d'octets, je doute qu'il y aurait de toute façon plus rapide de lire un nombre décimal à partir d'elle que de l'appeler à son ReadDecimal
méthode.OriginalL'auteur Eugene Beresovsky | 2013-06-07
Vous devez vous connecter pour publier un commentaire.
@Eugene Beresovksy lire à partir d'un flux est très coûteux. MemoryStream est certainement un outil puissant et versatile, mais il a une jolie coût élevé pour une lecture directe d'un tableau binaire. Peut-être à cause de cela, la deuxième méthode est plus performante.
J'ai un 3ème solution pour vous, mais avant de me l'écrire, il est nécessaire de dire que je n'ai pas testé la performance de celle-ci.
C'est une façon de faire de la construction basée sur un binaire sans se soucier de la forme canonique de
System.Decimal
. C'est l'inverse de la valeur par défaut .net peu de méthode d'extraction:ÉDITÉ:
Cette solution peut-être de ne pas exécuter mieux, mais aussi n'ont pas ce problème:
"(There's only one problem: Although decimals are represented as 16 bytes, some of the possible values do not constitute valid decimals, so doing an uncheckedmemcpy could potentially break things...)"
.new Decimal(Int32[])
solution.Je sais que c'est un vieux post, mais je me demandais si vous avez essayé la variante
int[] tmp = new int[4]; Buffer.BlockCopy(src, offset, tmp, 0, 16); return new decimal(tmp);
.BitConverter
est assez lente, ce qui pourrait pencher la réponse à cette solution un peu.OriginalL'auteur Jonny Piazzi
Même si c'est une vieille question, j'ai été un peu intrigué, il a donc décidé de lancer quelques expériences. Commençons par le code de test.
Résultat:
Time: 1.68s serialization, 1.81s deserialization
. C'est notre référence. J'ai aussi essayéBuffer.BlockCopy
à unint[4]
, ce qui nous donne 0.42 s pour la désérialisation. À l'aide de la méthode décrite dans la question, la désérialisation descend à 0,29 s.Eh bien oui, la façon la plus rapide de le faire est d'utiliser le code unsafe, ce qui est bien ici, parce que les décimales sont les types de valeur:
Sur ce point, notre résultat est:
Time: 0.07s serialization, 0.16s deserialization
. Assez sûr, le plus rapide c'est d'aller pour l'obtenir... encore, vous devez accepter dangereux ici, et je suppose choses est écrit de la même manière qu'il est lu.OriginalL'auteur atlaste