高速データ取得に使用するアプリケーションのメモリ効率を改善するように依頼されました。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