2

高速データ取得に使用するアプリケーションのメモリ効率を改善するように依頼されました。VSメモリプロファイラーを数回実行し、明らかな問題がないかプロジェクトをスキャンした後、次の結論に達しました。取得したサンプルポイントを格納するために固定サイズの循環バッファーを使用しているにもかかわらず、RAM使用量はポーリング期間に比べて増加します。例:2マイクロ秒で1つの信号のデータをポーリングすると、バッファサイズが同じであっても、50マイクロ秒で実行する場合の5倍のメモリ(プライベートバイト)を使用できます。

循環バッファは、SamplePointオブジェクトの配列です。これらの各オブジェクトには、信号ごとの対応するデータ用のShorts(16ビット)の配列と、タイムスタンプ用の1つの日付オブジェクト(8バイト)が含まれています。効率を上げるために、循環バッファは、ポーリングを開始するたびに1回だけ再調整され、空のサンプルポイントで満たされ、その後「割り当て」られます。

また、アプリケーションを停止して実行すると、Redimが前のアレイを解放していないかのように、毎回さらに多くのメモリを消費しているようです。

私の質問は次のように要約されます。

配列を持つ管理対象オブジェクトを含む循環バッファを実装するための最もメモリ効率の高い方法は何ですか?また、ポーリング速度が異なる固定サイズのアレイを使用すると、メモリ使用量がどのように、そしてなぜ増加するのでしょうか。ガベージコレクターには時間がありませんか?サブ変数または関数が終了すると、ローカル変数はすぐに破棄されますか?

これらは、先に進む前に除外したい疑問と懸念の一部です。お時間を割いていただきありがとうございます。また、コードを投稿することもできますが、たくさんあり、散らばっているので意味がありません。

編集:これは、循環バッファの初期化、ポピュレーション、およびリセットを反映する、私が作成した要約コードです。バグがありますか?

    ''' <summary>
''' Initialize internal list circular buffer.
''' </summary>
''' <param name="sizeOfBuffer"></param>
''' <remarks>
''' This is done for efficiency to avoid creating new samples points 
''' and redimensioning samplepoint data arrays for every read. Instead
''' the buffer is created and each samplepoint re-used. 
''' </remarks>
Friend Sub InitializeCircularBuffer(ByVal sizeOfBuffer As Integer, ByVal smpleDataSize As Integer, ByVal name As String)

    Dim mutexName As String = CreateMutexName(name)
    'First check for already existing mutex, otherwise create a new one
    Try
        _Mutex = Mutex.OpenExisting(mutexName)
    Catch ex As WaitHandleCannotBeOpenedException
        'Intialize mutex for each shared memory with unique names
        _Mutex = New Mutex(False, mutexName)
    Catch ex As UnauthorizedAccessException
        'Intialize mutex for each shared memory with unique names
        _Mutex = New Mutex(False, mutexName)
    End Try

    _Mutex.WaitOne()
    Try
        _SampleDataSize = smpleDataSize

        'Check size is valid, otherwise use the shared memory numSamples as default
        If sizeOfBuffer <= 0 Then
            _CircularBufferSize = _DefaultBufferSize
        Else
            _CircularBufferSize = sizeOfBuffer
        End If

        'Initialize/Reset circular buffer
        If _CircularBuffer Is Nothing Then
            _CircularBuffer = New List(Of SHM_SamplePoint)
        Else
            _CircularBuffer.Clear()
        End If

        'Create empty sample points with redimensioned data arrays in buffer 
        For i = 0 To _CircularBufferSize - 1
            _CircularBuffer.Add(New SHM_SamplePoint(_SampleDataSize))
        Next

        'Set current index to last place in buffer
        'It is incremented to first place when buffer
        'is being populated
        _CurrentIndex = _CircularBufferSize - 1

        _CircularBufferInitialized = True
    Catch ex As Exception
    Finally
        _Mutex.ReleaseMutex()
    End Try
End Sub

''' <summary>
''' Packages raw data and populates circular buffer.
''' </summary>
Friend Sub PopulateCircularBuffer(ByRef rawData() As Double, ByVal rawTimeStamps() As Double, ByVal numSamples As Integer, Optional ByVal startIndex As Integer = 0)
    _Mutex.WaitOne()
    Try
        _NumNewSamples = numSamples
        If _NumNewSamples > 0 Then
            For i As Integer = startIndex To _NumNewSamples - 1
                'Get index of next sample to be overwritten
                _CurrentIndex = (_CurrentIndex + 1) Mod _CircularBufferSize
                'Assign time-stamp
                _CircularBuffer(_CurrentIndex).TimeStamp = Date.FromOADate(rawTimeStamps(i))
                'Assign data
                Array.ConstrainedCopy(rawData, (i * _SampleDataSize), _CircularBuffer(_CurrentIndex).Data, 0, _SampleDataSize)
            Next
        End If
    Catch ex As Exception
    Finally
        _Mutex.ReleaseMutex()
    End Try
End Sub

''' <summary>
''' Empty the circular buffer.
''' </summary>
''' <remarks></remarks>
Friend Sub ResetCircularBuffer()
    For i As Integer = 0 To _CircularBuffer.Count - 1
        _CircularBuffer(i).Data = Nothing
        _CircularBuffer(i).TimeStamp = Nothing
    Next
    _CircularBuffer.Clear()
    _CircularBuffer.TrimExcess()
    'Signal garbage collection
    GC.Collect()
    _CircularBufferSize = 0
    _CircularBufferInitialized = False
End Sub
4

2 に答える 2

2

ガベージコレクターは、適切なタイミングであると判断したときにオブジェクトを破棄します。実際、私が取り組んでいる1つのアプリはメモリを非常に高速に取得するため、プロセスが約1.4GBのRAMを使用するまでGCはRAMの解放を開始しません(約100Kのみがアクティブに使用されていますが、残りは収集の対象になります) )。

あなたの説明に基づいて、メモリ使用率がサンプルレートに反比例する必要がある理由はまったくわかりません(固定サイズのバッファが与えられた場合)。関連するコードを投稿するのが賢明であるというコメントに同意します。

現在ワークステーションを使用している場合(またはその逆の場合)は、サーバーガベージコレクターを使用して、結果が異なるかどうかを確認してください。

「ワークステーション」ガベージコレクションと「サーバー」ガベージコレクションのどちらを使用する必要がありますか?

于 2012-06-08T05:19:19.660 に答える
0

配列がByValにコピーされるため、「ByVal rawTimeStamps()As Double」をByRefに変更すると、メモリフットプリントがわずかに改善されます。

また、生のタイムスタンプを保存し、Date.FromOADateのみを呼び出して、バッファーから取得した値を変換します。これにより、不要になる可能性のあるDateオブジェクトが作成されます(バッファーがラップアラウンドする場合)。

于 2012-11-14T20:05:06.063 に答える