Hidlibrary: ReadReport() fuite de mémoire

Créé le 14 août 2011  ·  9Commentaires  ·  Source: mikeobrien/HidLibrary

Grande bibliothèque Mike, merci beaucoup pour cet énorme travail.

Le problème est lorsque je veux remplir InputReport avec des données :

InputReport = device.ReadReport();

où InputReport est :

HidLibrary.HidReport InputReport = new HidLibrary.HidReport(device.Capabilities.InputReportByteLength);

C'est une très grande fuite de mémoire, surtout lorsque je lis des rapports en mode asynchrone à partir de l'appareil (par exemple 100 fois par seconde). En quelques minutes, cela peut prendre des centaines de mégaoctets.

Je suis débutant en programmation c# et je n'ai pas trouvé de solution au problème décrit ci-dessus ;(

les salutations!

Commentaire le plus utile

Voici ma solution de contournement suggérée pour le problème de performances que kaczar (et moi-même) avons remarqué :
https://github.com/macaba/HidLibrary/commit/8f1864e1351ccca4772d5bbc18a0f94d7ba07fe7

L'utilisation est du type :

device.ConnectionCheckOverride = device.IsConnected;
methodRequiringFasterUSBReadWrite();
device.ConnectionCheckOverride = false;

Cela a réduit le temps d'exécution de mon cas de test de transfert USB en masse de 159 secondes à 47 secondes.

Je n'aimais pas la solution de kaczart simplement parce que la propriété IsConnected est utilisée dans HidDeviceEventMonitor, donc nécessaire pour conserver la fonctionnalité d'origine d'énumération des périphériques USB actuellement connectés. Tout simplement; Je ne pense pas que les événements Inserted et Removed seront jamais appelés dans le code de kaczart (d'après un bref aperçu de celui-ci).

Tous les 9 commentaires

Kaczar,

Malheureusement ce code est assez ancien et je n'ai pas eu le temps de le donner
toute TLC. À ce stade, je l'ai juste là comme référence. Votre meilleur
le pari est de bifurquer/d'améliorer.

m

Le dimanche 14 août 2011 à 10h37, kaczar <
[email protected]> a écrit :

Grande bibliothèque Mike, merci beaucoup pour cet énorme travail.

Le problème est lorsque je veux remplir InputReport avec des données :

InputReport = device.ReadReport();

où InputReport est :

HidLibrary.HidReport InputReport = nouveau
HidLibrary.HidReport(ukp.Capabilities.InputReportByteLength);

C'est une très grande mémoire de fuite, surtout lorsque je lis des rapports de manière asynchrone
mode de l'appareil (par exemple 100 fois pendant une seconde). En quelques minutes, il peut
prend des centaines de mégaoctets.

Je suis débutant en programmation c# et je n'ai pas trouvé de solution au problème
décrit ci-dessus ;(

les salutations!

Répondez directement à cet e-mail ou consultez-le sur GitHub :
https://github.com/mikeobrien/HidLibrary/issues/11

Kaczar,

Avez-vous trouvé une solution? Je viens de remarquer la même chose.
Super bibliothèque Mike, merci beaucoup ! Je travaille avec une balance USB Endicia et c'est assez amusant.

jrockfl,

malheureusement, je n'ai pas eu le temps et les compétences suffisantes pour résoudre ce problème :(

les salutations

Le problème est que HidReport et Devicedata ne sont pas libérés, il attend donc une récupération de place.
C'est une solution facile cependant de le libérer après qu'il ait été traité...

Enfin, j'ai dû résoudre ce problème et je partage ce que j'ai trouvé

1) Tout d'abord, toutes les méthodes de lecture/écriture de la bibliothèque vérifient si le périphérique est connecté, ce qui coûte beaucoup de performances, car la méthode IsConnected appelle à chaque fois EnumerateHidDevices(). En pratique, lorsque j'envoie un gros paquet de données depuis l'appareil, PC Host est plus lent que l'appareil HID et certains paquets sont perdus en raison de la mémoire tampon en anneau limitée (32 rapports par défaut, peuvent être étendus à 512 mais ce n'est pas la solution finale).
ps. vous pouvez étendre votre tampon d'anneau de 32 à 512 rapports maximum en appelant :
NativeMethods .HidD_SetNumInputBuffers((int )device.ReadHandle, 512); // Cela fonctionnera pour XP et plus tard.
2) Deuxièmement, dans EnumerateHidDevices () est une fuite de mémoire ÉNORME en raison de "yield return devicePath;" à la ligne 69 et NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet); ne sera jamais appelé.

Ma solution :
1) N'appelez pas IsConnected à chaque lecture/écriture. Créez une variable isConnected supplémentaire dans HidDevice :
public bool isConnected;
Définissez la variable sur true après une connexion réussie (par exemple dans votre fonction Connect()) :
appareil.isConnecté = vrai ;
Mettez-le à jour dans chaque événement DeviceEventMonitorInserted() et DeviceEventMonitorRemoved(), par exemple :
if (Inséré != null ) Inséré();
remplacer par:
if (Inséré != null )
{
isConnected = true ;
Inséré();
}
et:
if (Supprimé != null ) Supprimé();
remplacer par:
if (Supprimé != null )
{
isConnected = false ;
Supprimé();
}
Ensuite, vérifiez cette variable à chaque fonction d'appel en lecture/écriture :
if (isConnected) au lieu de if (IsConnected)
Chaque fois que vous devez vérifier si l'appareil est connecté, vérifiez l'état de la variable publique isConnected. Cela fonctionne pour moi - 0-1% d'utilisation du processeur au lieu de 10-20% pour le fil de lecture.

2) dans la ligne #69 de HidDevice.cs, supprimez :
//yield return devicePath;
Il permettra à NativeMethods .SetupDiDestroyDeviceInfoList(deviceInfoSet); être appelé proprement.
Ensuite, après NativeMethods .SetupDiDestroyDeviceInfoList(deviceInfoSet), ajoutez :
foreach (string devicePath dans devices)
return return devicePath;
Cela corrigera une fuite de mémoire ÉNORME (environ 256 Mo pour chaque 1 Mo de données reçues dans mon cas)

J'espère que ça aide.

génial, merci pour vos conseils, kaczart !!!

Voici ma solution de contournement suggérée pour le problème de performances que kaczar (et moi-même) avons remarqué :
https://github.com/macaba/HidLibrary/commit/8f1864e1351ccca4772d5bbc18a0f94d7ba07fe7

L'utilisation est du type :

device.ConnectionCheckOverride = device.IsConnected;
methodRequiringFasterUSBReadWrite();
device.ConnectionCheckOverride = false;

Cela a réduit le temps d'exécution de mon cas de test de transfert USB en masse de 159 secondes à 47 secondes.

Je n'aimais pas la solution de kaczart simplement parce que la propriété IsConnected est utilisée dans HidDeviceEventMonitor, donc nécessaire pour conserver la fonctionnalité d'origine d'énumération des périphériques USB actuellement connectés. Tout simplement; Je ne pense pas que les événements Inserted et Removed seront jamais appelés dans le code de kaczart (d'après un bref aperçu de celui-ci).

La vérification d'une connexion de périphérique à chaque lecture et écriture est un goulot d'étranglement majeur des performances dans les applications à haute fréquence. Dans mon cas particulier, il était 30 fois plus rapide de supposer que l'appareil était connecté plutôt que de revérifier la connexion d'un appareil ; les allers-retours sont passés d'environ 70 ms à 2 ms.

Je viens donc d'écrire quelques méthodes d'extension pour gérer mes propres E/S pour une utilisation rapide en pool :

        [DllImport("hid.dll", SetLastError = true)]
        static internal extern bool HidD_SetOutputReport(IntPtr hidDeviceObject, byte[] lpReportBuffer, int reportBufferLength);

        [DllImport("kernel32.dll", SetLastError = true)]
        static internal extern bool ReadFile(IntPtr hFile, [Out] byte[] lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);
        public static bool FastWrite(this HidLibrary.HidDevice device, byte[] outputBuffer)
        {
            try
            {
                if (NativeMethods.HidD_SetOutputReport(device.Handle, outputBuffer, outputBuffer.Length))
                    return true;
                else
                    return false;
            }
            catch
            {
                return false;
            }
        }
        public static ReadStatus FastRead(this HidLibrary.HidDevice device, byte[] inputBuffer)
        {
            try
            {
                uint bytesRead;
                if (NativeMethods.ReadFile(device.Handle, inputBuffer, (uint)inputBuffer.Length, out bytesRead, IntPtr.Zero))
                {
                    return ReadStatus.Success;
                }
                else
                {
                    return ReadStatus.NoDataRead;
                }
            }
            catch (Exception)
            {
                return ReadStatus.ReadError;
            }
        }
Cette page vous a été utile?
0 / 5 - 0 notes