Comment remettre à zéro la mémoire allouée par le Maréchal.AllocHGlobal?
Je suis à consacrer une mémoire non managée dans mon application via Marshal.AllocHGlobal
. Je suis alors la copie d'un ensemble d'octets à cet endroit, et la conversion de l'résultant segment de mémoire pour un struct
avant de libérer la mémoire à nouveau via Marshal.FreeHGlobal
.
Voici la méthode:
public static T Deserialize<T>(byte[] messageBytes, int start, int length)
where T : struct
{
if (start + length > messageBytes.Length)
throw new ArgumentOutOfRangeException();
int typeSize = Marshal.SizeOf(typeof(T));
int bytesToCopy = Math.Min(typeSize, length);
IntPtr targetBytes = Marshal.AllocHGlobal(typeSize);
Marshal.Copy(messageBytes, start, targetBytes, bytesToCopy);
if (length < typeSize)
{
//Zero out additional bytes at the end of the struct
}
T item = (T)Marshal.PtrToStructure(targetBytes, typeof(T));
Marshal.FreeHGlobal(targetBytes);
return item;
}
Cela fonctionne pour la plupart, cependant, si j'ai moins d'octets que la taille de la struct
exige, puis "aléatoire" des valeurs sont affectées à la dernière champs (je suis en utilisant LayoutKind.Sequential
sur la cible struct). Je tiens à zéro ces pendaison des domaines aussi efficacement que possible.
Pour le contexte, ce code est la désérialisation d'à haute fréquence de multidiffusion les messages envoyés à partir de C++ sur Linux.
Ici est une faute de cas de test:
//Give only one byte, which is too few for the struct
var s3 = MessageSerializer.Deserialize<S3>(new[] { (byte)0x21 });
Assert.AreEqual(0x21, s3.Byte);
Assert.AreEqual(0x0000, s3.Int); //hanging field should be zero, but isn't
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
private struct S3
{
public byte Byte;
public int Int;
}
L'exécution de ce test à plusieurs reprises les causes de la seconde affirmer l'échec avec une valeur différente à chaque fois.
MODIFIER
En fin de compte, j'ai utilisé leppie la suggestion de d'aller unsafe
et à l'aide de stackalloc
. Cette alloué un tableau d'octets qui a été remis à zéro en tant que de besoin, et de l'amélioration du débit entre 50% et 100%, selon la taille des messages (les messages les plus grands a vu le plus grand bénéfice).
La dernière méthode a fini ressemblant à:
public static T Deserialize<T>(byte[] messageBytes, int startIndex, int length)
where T : struct
{
if (length <= 0)
throw new ArgumentOutOfRangeException("length", length, "Must be greater than zero.");
if (startIndex < 0)
throw new ArgumentOutOfRangeException("startIndex", startIndex, "Must be greater than or equal to zero.");
if (startIndex + length > messageBytes.Length)
throw new ArgumentOutOfRangeException("length", length, "startIndex + length must be <= messageBytes.Length");
int typeSize = Marshal.SizeOf(typeof(T));
unsafe
{
byte* basePtr = stackalloc byte[typeSize];
byte* b = basePtr;
int end = startIndex + Math.Min(length, typeSize);
for (int srcPos = startIndex; srcPos < end; srcPos++)
*b++ = messageBytes[srcPos];
return (T)Marshal.PtrToStructure(new IntPtr(basePtr), typeof(T));
}
}
Malheureusement, cela nécessite encore un appel à Marshal.PtrToStructure
pour convertir les octets dans le type de cible.
OriginalL'auteur Drew Noakes | 2009-09-28
Vous devez vous connecter pour publier un commentaire.
Pourquoi ne pas simplement vérifier si
start + length
est danstypesize
?BTW: je voudrais juste aller
unsafe
ici et utiliser une boucle for pour pour mettre à zéro de la mémoire supplémentaire.Que trop vous donnera l'avantage de l'utilisation de
stackalloc
qui est beaucoup plus sûr et plus rapide queAllocGlobal
.stackalloc
trop. J'ai à répondre à différentes tailles de message que les deux équipes peuvent parfois gérer pour éviter les synchronisée versions si nous ajouter des champs sur la fin que l'autre extrémité ignore. De même, si vous n'avez pas besoin de valeurs, vous pouvez vous attendre et obtenir des zéros à la place, ce qui est le cas, je suis en train de réaliser ici.Je penche vers cette approche. Pourriez-vous entrer dans plus de détails quant à la pourquoi à l'aide de
stackalloc
est plus sûr et le plus rapide? Une fois que j'ai lebyte*
, ce serait le meilleur moyen pour copier?J'ai mis en place une version qui fonctionne avec
stackalloc
pour remplir un tableau sur la pile. Je ne pense pas qu'il soit possible d'obtenir autour de l'appel àMarshal.PtrToStructure
cependant, est-il?Nah, j'ai aussi n'a pas réalisé les génériques sucé tellement dur quand va dangereuses 🙁 Si vos types sont connus, vous pouvez générer toutes les "templates". Qui serait le garder, il est rapide.
Malheureusement, c'est une API générique qui permettra de traiter avec des inconnus et de types différents (bien que tous de taille fixe.)
OriginalL'auteur leppie
Dumpbin.exe sur kernel32.dll dit que ce n'est pas seulement une macro.
J'ai besoin de zéro à
dst + N
.IntPtr
ne prend pas en charge l'arithmétique, alors comment puis-je remédier à ce décalage?Ne pouvez-vous pas simplement zéro de l'ensemble de la mémoire tampon avant le Maréchal.Copie d'appel? De cette façon, quelle que soit la partie que vous ne pas écraser avec la structure restera à zéro. Vous pouvez faire de l'arithmétique sur la valeur du pointeur si vous le lancez à une longue et puis retour à IntPtr.
C'est la seule réponse correcte. Il n'y a pas de raison d'utiliser autre chose. Dans windows.h c'est une macro simplement pour l'interopérabilité du code. Le NtosKrnl version prétend être de la plate-forme, voir msdn.microsoft.com/en-us/library/windows/hardware/....
OriginalL'auteur Mattias S
Cela fonctionne parfaitement sur Windows:
OriginalL'auteur Jesus
Oui comme Jon Seigel dit, vous pouvez zéro à l'aide de Maréchal.WriteByte
Dans l'exemple suivant, j'ai zéro de la mémoire tampon avant de copier la structure.
L'autre solution, que je pensais était P/Invoke la fonction LocalAlloc et passer dans le LPTR drapeau.
OriginalL'auteur Hasani Blackwell
Je n'ai jamais fait ce genre de choses en C# avant, mais j'ai trouvé le Maréchal.WriteByte(IntPtr, Int32, Byte) dans MSDN. Essayer ça.
OriginalL'auteur Jon Seigel
Je pense que vous trouverez à être plusieurs fois plus rapide à l'aide de 64 bits wrights au lieu de 8 bits frères wright (que vous avez encore besoin de la dernière quelques octets).
OriginalL'auteur user2163234
Je pense que la meilleure façon de zéro, un tampon est présent, si vous ne voulez pas ou ne pouvez pas aller dans l'autre sens:
OriginalL'auteur mr_anderson