Hidlibrary: ReadReport()のメモリリーク

作成日 2011年08月14日  ·  9コメント  ·  ソース: mikeobrien/HidLibrary

素晴らしい図書館マイク、この巨大な仕事に感謝します。

問題は、InputReportにデータを入力する場合です。

InputReport = device.ReadReport();

ここで、InputReportは次のとおりです。

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

特にデバイスから非同期モードでレポートを読み取る場合(たとえば、1秒間に100回)、これは非常に大きなリークメモリです。 数分で数百メガバイトかかることがあります。

私はC#プログラミングの初心者であり、上記で説明した問題の解決策が見つかりませんでした;(

あいさつ!

最も参考になるコメント

kaczart(および私)が気付いたパフォーマンスの問題に対する私の推奨される回避策は次のとおりです。
https://github.com/macaba/HidLibrary/commit/8f1864e1351ccca4772d5bbc18a0f94d7ba07fe7

使用法は次の線に沿っています:

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

これにより、バルクUSB転送テストケースの実行時間が159秒から47秒に短縮されました。

IsConnectedプロパティがHidDeviceEventMonitorで使用されているという理由だけで、kaczartのソリューションに熱心ではなかったため、現在接続されているUSBデバイスを列挙する元の機能を保持する必要がありました。 簡単に言えば; InsertedイベントとRemovedイベントがkaczartのコードで呼び出されることはないと思います(簡単に見てみると)。

全てのコメント9件

Kaczart、

残念ながら、そのコードはかなり古く、私はそれを与える時間がありませんでした
任意のTLC。 この時点で、私はそれを参照としてそこに持っています。 あなたの最高の
賭けはフォーク/改善することです。

NS

2011年8月14日日曜日午前10時37分、kaczart <
[email protected]>書き込み:

素晴らしい図書館マイク、この巨大な仕事に感謝します。

問題は、InputReportにデータを入力する場合です。

InputReport = device.ReadReport();

ここで、InputReportは次のとおりです。

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

特に非同期でレポートを読む場合、これは非常に大きなリークメモリです。
デバイスからのモード(たとえば、1秒間に100回)。 数分でそれができます
数百メガバイトかかります。

私はC#プログラミングの初心者ですが、問題の解決策が見つかりませんでした
上記の説明;(

あいさつ!

このメールに直接返信するか、GitHubで表示してください。
https://github.com/mikeobrien/HidLibrary/issues/11

Kaczart、

解決策を見つけましたか? 私はちょうど同じことに気づきました。
素晴らしい図書館マイク、どうもありがとう! 私はエンディシアのUSBスケールで作業していますが、これはとても楽しいです。

jrockfl、

残念ながら、この問題を解決するのに十分な時間とスキルがありませんでした:(

あいさつ

問題は、HidReportとDevicedataが解放されないため、ガベージコレクションを待機することです。
処理後にリリースするのは簡単な修正です...

最後に、この問題を修正する必要があり、見つけたものを共有します

1)まず、ライブラリ内のすべての読み取り/書き込みメソッドは、デバイスが接続されているかどうかを確認します。これは、IsConnectedメソッドがEnumerateHidDevices()を呼び出すたびに呼び出されるため、パフォーマンスが大幅に低下します。 実際には、デバイスから大きなデータパッケージを送信すると、PCホストはHIDデバイスよりも遅くなり、リングバッファーが制限されているために一部のパケットが失われます(デフォルトでは32レポートを512に拡張できますが、最終的な解決策ではありません)。
ps。 リングバッファを32から最大512のレポートに拡張できます。
NativeMethods .HidD_SetNumInputBuffers((int)device.ReadHandle、512); // XP以降で動作します。
2)次に、EnumerateHidDevices()には、「yieldreturndevicePath」による大量のメモリリークがあります。 69行目およびNativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet); 呼び出されることはありません。

私の解決策:
1)読み取り/書き込みごとにIsConnectedを呼び出さないでください。 HidDeviceに追加のisConnected変数を作成します。
public bool isConnected;
接続が成功した後(たとえば、Connect()関数で)変数をtrueに設定します。
device.isConnected = true;
すべてのDeviceEventMonitorInserted()およびDeviceEventMonitorRemoved()イベントで更新します。例:
if(Inserted!= null)Inserted();
と置換する:
if(挿入!= null)
{{
isConnected = true;
Inserted();
}
と:
if(Removed!= null)Removed();
と置換する:
if(削除されました!= null)
{{
isConnected = false;
NS();
}
読み取り/書き込み呼び出し関数ごとにこの変数をチェックするよりも:
if(IsConnected)の代わりにif(isConnected)
デバイスが接続されているかどうかを確認する必要があるたびに、パブリック変数isConnectedの状態を確認してください。 それは私のために働きます-読み取りスレッドの10-20%の代わりに0-1%のプロセッサ使用量。

2)HidDevice.csの69行目で削除:
//利回りreturndevicePath;
NativeMethods .SetupDiDestroyDeviceInfoList(deviceInfoSet);が可能になります。 ふさわしく呼ばれる。
次に、NativeMethods .SetupDiDestroyDeviceInfoList(deviceInfoSet)の後に次を追加します。
foreach(デバイス内の文字列devicePath)
リターンdevicePathを生成します。
巨大なメモリリークを修正します(私の場合、1MBの受信データごとに約256MB)

それが盛り上がることを願っています。

素晴らしい、あなたのヒントをありがとう、kaczart !!!

kaczart(および私)が気付いたパフォーマンスの問題に対する私の推奨される回避策は次のとおりです。
https://github.com/macaba/HidLibrary/commit/8f1864e1351ccca4772d5bbc18a0f94d7ba07fe7

使用法は次の線に沿っています:

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

これにより、バルクUSB転送テストケースの実行時間が159秒から47秒に短縮されました。

IsConnectedプロパティがHidDeviceEventMonitorで使用されているという理由だけで、kaczartのソリューションに熱心ではなかったため、現在接続されているUSBデバイスを列挙する元の機能を保持する必要がありました。 簡単に言えば; InsertedイベントとRemovedイベントがkaczartのコードで呼び出されることはないと思います(簡単に見てみると)。

読み取りと書き込みのたびにデバイス接続をチェックすることは、高周波アプリケーションの主要なパフォーマンスのボトルネックです。 私の特定のケースでは、デバイスの接続を再確認するよりも、デバイスが接続されていると想定する方が30倍高速でした。 往復は約70msから2msに減少しました。

そこで、プーリングを迅速に使用するために独自のIOを処理するための拡張メソッドをいくつか作成しました。

        [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;
            }
        }
このページは役に立ちましたか?
0 / 5 - 0 評価