高速循环缓冲器

时间:2012-06-08 05:14:42

标签: vb.net memory-leaks dispose circular-buffer

我被要求提高用于高速数据采集的应用程序的内存效率。在多次运行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

2 个答案:

答案 0 :(得分:2)

垃圾收集器会在感觉好的时候处理对象。实际上,我正在处理的一个应用程序现在获取内存的速度非常快,以至于GC不会开始释放RAM,直到该进程使用大约1.4GB的RAM(即使只有大约100K正在使用中,其余的也有资格收集)。

根据您的描述,我根本不清楚为什么内存利用率应该与采样率成反比(给定一个固定大小的缓冲区)。我同意评论,发布相关代码是明智的。

如果您当前正在使用工作站(或反之亦然),您可以尝试使用服务器垃圾收集器来查看结果是否有所不同。

Should we use "workstation" garbage collection or "server" garbage collection?

答案 1 :(得分:0)

通过将“ByVal rawTimeStamps()As Double”更改为ByRef,您将获得内存占用的小幅改进,因为数组将被复制为ByVal。

此外,存储原始时间戳并仅调用Date.FromOADate来转换从缓冲区中拉出的值 - 您将创建可能永远不需要的Date对象(如果缓冲区已经环绕)。