vb.net - 高速环形缓冲器

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

我被要求提高用于高速数据采集的应用程序的内存效率。在运行 VS 内存分析器几次并扫描项目是否存在明显问题后,我得出以下结论:尽管使用固定大小的循环缓冲区来存储获取的样本点,但 RAM 使用量相对于轮询周期会增加。例如:以 2 微秒的速度轮询一个信号的数据可以比以 50 微秒的速度运行时多使用 5 倍的内存(专用字节)……即使缓冲区大小相同。

循环缓冲区是 SamplePoint 对象的数组。这些对象中的每一个都包含一组 Shorts(16 位),用于每个信号的相应数据,以及一个用于时间戳的日期对象(8 字节)。为了提高效率,循环缓冲区仅在每次开始轮询时重新调暗一次,并填充空样本点,然后“分配”这些样本点。

此外,当我们停止并运行应用程序时,它似乎每次都会占用更多内存,就好像 Redim 没有释放以前的数组一样。

我的问题归结为以下几点:

实现包含具有数组的托管对象的循环缓冲区的最有效内存方式是什么? 另外,具有不同轮询速度的固定大小数组的内存使用量如何以及为何增加? 垃圾收集者没有时间吗?当子或函数退出时,局部变量是否立即被处理?

在继续之前,我想排除一些疑虑和担忧。 感谢您抽出时间。另外,我可以发布代码,但这毫无意义,因为代码太多而且分散。

编辑:这是我编写的一些精简代码,反射(reflect)了循环缓冲区的初始化、填充和重置。发现任何错误吗?

    ''' <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

最佳答案

垃圾收集器会在认为合适的时机处理对象。事实上,我现在正在开发的一个应用程序获取内存的速度非常快,以至于 GC 直到进程使用大约 1.4GB RAM 才开始释放 RAM(尽管只有大约 100K 正在使用,其余的都符合收集条件) )。

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

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

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

关于vb.net - 高速环形缓冲器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10943396/

相关文章:

iphone - 如何解决Instruments检测到的泄漏,其责任库是Apple的框架?

c# - 如何一次处理面板或窗体中的所有控件??? C#

C# 处理 IDisposable

mysql - 如何在一个gridview中查看两个表格?

.net - 我有 3 种方法来获取数组的 ubound

c# - C#和VB.NET运算符优先级

c - Valgrind 和全局变量

c# - C# 中的 Case/Switch 语句

c# - 使用 StringBuilder Remove 方法比在循环中创建新的 StringBuilder 方法更节省内存吗?

c# - GDI+ 区域、矩阵和 GraphicsPath 处理