我被要求提高用于高速数据采集的应用程序的内存效率。在多次运行 VS 内存分析器并扫描项目以查找明显问题后,我得出以下结论:尽管使用固定大小的循环缓冲区来存储获取的样本点,但 RAM 使用量相对于轮询周期会增加。例如:在 2 微秒内轮询一个信号的数据可以使用比以 50 微秒运行时多 5 倍的内存(私有字节)......即使缓冲区大小相同。
循环缓冲区是一组 SamplePoint 对象。这些对象中的每一个都包含一组 Shorts(16 位)用于每个信号的相应数据和一个日期对象(8 个字节)用于时间戳。为提高效率,循环缓冲区每次开始轮询时仅重新调整一次,并填充空样本点,然后“分配”。
此外,似乎当我们停止并运行应用程序时,它每次都会占用更多内存,就好像 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