ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

针对代码中缓冲区管理(m_VFBuffer 或 darray2d)的优化,以下是详细的改进方案,旨在减少内存占用、降低内存碎片风险、提高性能并避免内存溢出

针对代码中缓冲区管理(m_VFBuffer 或 darray2d)的优化,以下是详细的改进方案,旨在减少内存占用、降低内存碎片风险、提高性能并避免内存溢出 针对代码中缓冲区管理m_VFBuffer 或 darray2d的优化以下是详细的改进方案旨在减少内存占用、降低内存碎片风险、提高性能并避免内存溢出。优化将基于文件 1 和文件 2 的特点重点解决频繁内存分配、未使用缓冲区浪费以及数据累积问题。包含具体代码实现和说明。1. 优化目标减少内存分配降低频繁分配大内存块如 darray2d 或 buf的开销减少内存碎片。消除未使用缓冲区移除未使用的 m_VFBuffer[1] 或 darray2d[1]减少内存浪费。复用缓冲区通过全局缓冲区或对象池避免重复分配。限制缓冲区大小根据实际需求动态调整缓冲区防止过大分配。提高性能减少垃圾回收压力优化高频调用场景。2. 当前问题分析文件 1使用类级别的 m_VFBuffer2 × 1000000 × 8 字节 ≈ 16 MB在构造函数中分配。m_VFBuffer[1] 未使用浪费 8 MB/实例。buf 数组在 TVJDataReady 中根据 readCount 动态分配可能导致频繁分配和碎片。优点缓冲区复用分配开销低。缺点多实例场景下内存占用高buf 频繁分配可能导致碎片。文件 2使用局部变量 darray2d2 × 1000000 × 8 字节 ≈ 16 MB每次 TVJDataReady 调用分配。darray2d[1] 未使用浪费 8 MB/调用。优点局部变量隔离性强适合数据隔离场景。缺点高频调用导致频繁分配内存碎片风险高性能开销大。共同问题buf[1] 未使用表明可能只需要单一通道数据。readCount 未受限可能导致分配过大缓冲区。osc.Channels[0].XValues 数据累积可能占用大量内存。3. 优化方案以下是针对缓冲区管理的具体优化措施优先基于文件 1 的全局缓冲区方式更高效并提供文件 2 的替代方案。3.1 移除未使用的缓冲区问题m_VFBuffer[1]文件 1或 darray2d[1]文件 2分配了 8 MB 但未使用浪费内存。优化方案确认 RawWaveDataCache.GetMulitData 是否需要两个通道。如果只处理 Vce 数据buf[0]可只分配单一通道。文件 1 修改csharpprivate double[][] m_VFBuffer new double[1][]; // 只分配一个通道 public FormCheckLinkModule(ListWorkStation wsList) { InitializeComponent(); m_VFBuffer[0] new double[100000]; // 减小初始大小 for(int i 0; i wsList.Count; i) { AddPage(wsList[i]); } }文件 2 修改csharppublic void TVJDataReady(DaqChannelLink link) { double[][] darray2d new double[1][]; // 只分配一个通道 darray2d[0] new double[100000]; // 减小初始大小 RawWaveDataCache.GetMulitData(TVJ, ws.Id, 0, ref darray2d, out int readCount, out bool overflow); double[] buf new double[readCount]; // 单一数组 Buffer.BlockCopy(darray2d[0], 0, buf, 0, readCount * 8); ... }效果减少 8 MB/实例文件 1或 8 MB/调用文件 2的内存占用。简化代码移除无用分配。3.2 复用缓冲区以减少分配问题文件 2 的 darray2d 每次调用分配 16 MB高频调用导致内存碎片和性能开销。文件 1 的 buf 数组根据 readCount 动态分配频繁调用可能导致碎片。优化方案文件 1保留 m_VFBuffer 的全局缓冲区方式添加 buf 缓冲区复用。文件 2将 darray2d 改为类级别缓冲区复用内存。实现对象池为 buf 创建固定大小的缓冲区池避免频繁分配。代码示例基于文件 1文件 2 可类似修改csharpprivate double[][] m_VFBuffer new double[1][]; // 单一通道 private double[] m_BufPool; // 缓冲区池 private const int MaxBufferSize 100000; // 最大缓冲区大小 public FormCheckLinkModule(ListWorkStation wsList) { InitializeComponent(); m_VFBuffer[0] new double[MaxBufferSize]; m_BufPool new double[MaxBufferSize]; // 初始化缓冲区池 for(int i 0; i wsList.Count; i) { AddPage(wsList[i]); } } public void TVJDataReady(DaqChannelLink link) { int index link.DevIndex; int chan link.ChannelIndex; int sampleRate link.SampleRate; string desc link.DaqDeviceDesc; WorkStation ws m_WorkStation[link.Position]; Oscilloscope osc m_OscMap[ws.Id]; RawWaveDataCache.GetMulitData(TVJ, ws.Id, 0, ref m_VFBuffer, out int readCount, out bool overflow); if (readCount MaxBufferSize) { readCount MaxBufferSize; // 限制 readCount Console.WriteLine($Warning: readCount truncated to {MaxBufferSize}); } Buffer.BlockCopy(m_VFBuffer[0], 0, m_BufPool, 0, readCount * 8); Listlong time new Listlong(readCount); for(int i 0; i readCount; i) { time.Add(i m_CountMap[ws.Id]); } m_CountMap[ws.Id] readCount; if (m_ResampleCountMap[ws.Id] m_MaxResampleCount) { osc.Channels[0].Clear(); m_ResampleCountMap[ws.Id] 0; return; } if (osc.Channels[0].XValues.Count 1000) // 降低重采样阈值 { osc.Channels[0].Resample(200); // 保留更少点 m_ResampleCountMap[ws.Id]; } osc.Channels[0].Add(time, m_BufPool.Take(readCount).ToArray()); osc.AutoScale(); }说明m_VFBuffer 只分配一个通道初始大小减小到 100000可根据实际 readCount 调整。m_BufPool 作为固定大小的缓冲区池避免每次分配新的 buf 数组。添加 readCount 上限检查防止分配过大缓冲区。效果减少内存分配频率降低垃圾回收压力。避免内存碎片提高高频调用场景的性能。3.3 动态调整缓冲区大小问题m_VFBuffer[0] 和 darray2d[0] 固定分配 1000000 个 double8 MB可能过大或不足。优化方案根据 RawWaveDataCache.GetMulitData 的典型 readCount 值动态分配缓冲区。实现可调整的缓冲区大小定期检查是否需要扩展csharpprivate double[][] m_VFBuffer new double[1][]; private int m_BufferSize 10000; // 初始较小大小 private const int MaxBufferSize 100000; // 最大上限 public FormCheckLinkModule(ListWorkStation wsList) { InitializeComponent(); m_VFBuffer[0] new double[m_BufferSize]; for(int i 0; i wsList.Count; i) { AddPage(wsList[i]); } } private void ResizeBufferIfNeeded(int requiredSize) { if (requiredSize m_BufferSize m_BufferSize MaxBufferSize) { m_BufferSize Math.Min(requiredSize, MaxBufferSize); m_VFBuffer[0] new double[m_BufferSize]; Console.WriteLine($Resized buffer to {m_BufferSize}); } } public void TVJDataReady(DaqChannelLink link) { ... ResizeBufferIfNeeded(link.Buffer.Length); // 检查并调整缓冲区 RawWaveDataCache.GetMulitData(TVJ, ws.Id, 0, ref m_VFBuffer, out int readCount, out bool overflow); if (readCount m_BufferSize) { readCount m_BufferSize; // 限制 readCount } double[] buf new double[readCount]; Buffer.BlockCopy(m_VFBuffer[0], 0, buf, 0, readCount * 8); ... }效果初始分配较小缓冲区例如 10000 × 8 字节 ≈ 80 KB减少内存占用。动态扩展缓冲区适应不同 readCount但设置上限MaxBufferSize防止溢出。3.4 限制数据累积问题osc.Channels[0].Add 将数据添加到 XValues可能累积大量数据导致内存溢出。优化方案降低重采样阈值和保留点数csharpprivate int m_MaxResampleCount 100; // 降低最大重采样次数 public void TVJDataReady(DaqChannelLink link) { ... if (m_ResampleCountMap[ws.Id] m_MaxResampleCount) { osc.Channels[0].Clear(); m_ResampleCountMap[ws.Id] 0; return; } if (osc.Channels[0].XValues.Count 1000) // 降低阈值 { osc.Channels[0].Resample(200); // 保留更少点 m_ResampleCountMap[ws.Id]; } int maxPoints 1000; // 限制每次添加的点数 int pointsToAdd Math.Min(readCount, maxPoints); Listlong time new Listlong(pointsToAdd); for(int i 0; i pointsToAdd; i) { time.Add(i m_CountMap[ws.Id]); } osc.Channels[0].Add(time, m_BufPool.Take(pointsToAdd).ToArray()); ... }效果限制 XValues 的大小减少内存占用。更频繁的重采样和清理防止数据无限累积。4. 文件 1 vs 文件 2 的优化选择文件 1全局 m_VFBuffer推荐场景高频调用、单实例或少实例场景。优化重点移除 m_VFBuffer[1]减小 m_VFBuffer[0] 初始大小。使用 m_BufPool 复用 buf 数组。动态调整缓冲区大小限制 readCount。优点内存分配一次性能高适合实时数据采集。缺点多实例可能占用大量内存需限制实例数量。文件 2局部 darray2d推荐场景低频调用、需要数据隔离的场景。优化重点将 darray2d 改为类级别缓冲区如 m_VFBuffer避免频繁分配。移除 darray2d[1]减小初始大小。添加 m_BufPool 和动态缓冲区调整。优点数据隔离适合多线程或独立处理场景。缺点频繁分配导致性能开销大内存碎片风险高。推荐优先优化文件 1 的全局缓冲区方式性能更优。若需数据隔离可结合对象池实现文件 2 的优化。5. 完整优化代码示例基于文件 1以下是整合上述优化的完整代码适用于文件 1csharp#define CRRC2000 using CommonCtrlLib; using CommonInterface; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace CheckLinkModule { public partial class FormCheckLinkModule : Form { private Dictionarystring, WorkStation m_WorkMap new Dictionarystring, WorkStation(); private Dictionarystring, WorkStation m_WorkStation new Dictionarystring, WorkStation(); private Dictionarystring, Oscilloscope m_OscMap new Dictionarystring, Oscilloscope(); private Dictionarystring, long m_CountMap new Dictionarystring, long(); private Dictionarystring, int m_ResampleCountMap new Dictionarystring, int(); private double[][] m_VFBuffer new double[1][]; // 单一通道 private double[] m_BufPool; // 缓冲区池 private int m_BufferSize 10000; // 初始缓冲区大小 private const int MaxBufferSize 100000; // 最大缓冲区 private const int MaxResampleCount 100; // 降低重采样次数 public FormCheckLinkModule(ListWorkStation wsList) { InitializeComponent(); m_VFBuffer[0] new double[m_BufferSize]; m_BufPool new double[MaxBufferSize]; for(int i 0; i wsList.Count; i) { AddPage(wsList[i]); } } private void ResizeBufferIfNeeded(int requiredSize) { if (requiredSize m_BufferSize m_BufferSize MaxBufferSize) { m_BufferSize Math.Min(requiredSize, MaxBufferSize); m_VFBuffer[0] new double[m_BufferSize]; Console.WriteLine($Resized buffer to {m_BufferSize}); } } private void tabControl1_DrawItem(object sender, DrawItemEventArgs e) { Rectangle rec tabControl1.ClientRectangle; StringFormat StrFormat new StringFormat { LineAlignment StringAlignment.Center, Alignment StringAlignment.Center }; SolidBrush bru new SolidBrush(Color.White); SolidBrush bruFont new SolidBrush(Color.Black); Font font new System.Drawing.Font(微软雅黑, 9F); Pen pen new Pen(Color.Silver); e.Graphics.FillRectangle(bru, 0, 0, tabControl1.Width, tabControl1.Height); for (int i 0; i tabControl1.TabPages.Count; i) { Rectangle recChild tabControl1.GetTabRect(i); e.Graphics.FillRectangle(new SolidBrush(Color.Green), recChild); e.Graphics.DrawRectangle(pen, recChild); e.Graphics.DrawString(tabControl1.TabPages[i].Text, font, bruFont, recChild, StrFormat); } } public void AddPage(WorkStation ws) { TabPage tab new TabPage(ws.Name); tabControl1.TabPages.Add(tab); Oscilloscope osc new Oscilloscope() { IsMarkerVisible true, ChartTitle Vce, AxisColor Color.Black, ActiveColor Color.Red, ForeColor Color.DarkCyan, Dock DockStyle.Fill, ShowLegend true, IsZeroMarkerVisible false, MarkerColor Color.DarkCyan, ShowActionPanel true, ShowPathIndicator true, Font new Font(微软雅黑, 9F), BackColor Color.FromKnownColor(KnownColor.Window), AllowConfigAxis true, XLabel Count, YLabel V, YName Vce, XUnit , YUnit V, XName Count }; osc.OscilloscopeProps.MarkerAEnabled true; osc.OscilloscopeProps.MarkerBEnabled false; tabControl1.TabPages[tabControl1.TabPages.Count - 1].Controls.Add(osc); osc.CreateChannel(CommonCtrlLib.DrawingMode.XYLines, Vce曲线, ws.Name, Color.Red); m_WorkMap.Add(ws.Name, ws); m_OscMap.Add(ws.Id, osc); m_WorkStation.Add(ws.Id, ws); m_CountMap.Add(ws.Id, 0); m_ResampleCountMap.Add(ws.Id, 0); } public void TVJDataReady(DaqChannelLink link) { int index link.DevIndex; int chan link.ChannelIndex; int sampleRate link.SampleRate; string desc link.DaqDeviceDesc; WorkStation ws m_WorkStation[link.Position]; Oscilloscope osc m_OscMap[ws.Id]; ResizeBufferIfNeeded(link.Buffer.Length); RawWaveDataCache.GetMulitData(TVJ, ws.Id, 0, ref m_VFBuffer, out int readCount, out bool overflow); if (readCount m_BufferSize) { readCount m_BufferSize; Console.WriteLine($Warning: readCount truncated to {m_BufferSize}); } Buffer.BlockCopy(m_VFBuffer[0], 0, m_BufPool, 0, readCount * 8); int maxPoints 1000; // 限制每次添加的点数 int pointsToAdd Math.Min(readCount, maxPoints); Listlong time new Listlong(pointsToAdd); for(int i 0; i pointsToAdd; i) { time.Add(i m_CountMap[ws.Id]); } m_CountMap[ws.Id] pointsToAdd; if (m_ResampleCountMap[ws.Id] MaxResampleCount) { osc.Channels[0].Clear(); m_ResampleCountMap[ws.Id] 0; return; } if (osc.Channels[0].XValues.Count 1000) { osc.Channels[0].Resample(200); m_ResampleCountMap[ws.Id]; } osc.Channels[0].Add(time, m_BufPool.Take(pointsToAdd).ToArray()); osc.AutoScale(); } public void TCDataReady(DaqChannelLink link) { int index link.DevIndex; int chan link.ChannelIndex; double[] buffer link.Buffer; int sampleRate link.SampleRate; int bufSize buffer.Length; string desc link.DaqDeviceDesc; WorkStation ws m_WorkStation[link.Position]; RawWaveDataCache.GetData(TC, ws.Id, bufSize, ref buffer, out int readCount, out bool overflow); } public void VCEDataReady(DaqChannelLink link) { int index link.DevIndex; int chan link.ChannelIndex; double[] buffer link.Buffer; int sampleRate link.SampleRate; int bufSize buffer.Length; string desc link.DaqDeviceDesc; WorkStation ws m_WorkStation[link.Position]; RawWaveDataCache.GetData(VCE, ws.Id, bufSize, ref buffer, out int readCount, out bool overflow); } } }6. 优化效果内存占用移除 m_VFBuffer[1]减少 8 MB/实例。初始缓冲区减小到 1000080 KB动态扩展至 100000800 KB远低于原 8 MB。限制 readCount 和 pointsToAdd防止过大分配。性能提升m_BufPool 复用缓冲区消除 buf 的频繁分配。降低重采样阈值1000和保留点数200减少 XValues 内存占用。内存碎片全局缓冲区和对象池减少分配频率降低碎片风险。健壮性添加 readCount 上限检查和动态缓冲区调整防止意外溢出。日志记录便于调试和监控。7. 进一步优化建议分析 readCount 范围通过日志记录 readCount 的典型值优化 m_BufferSize 和 MaxBufferSize。异步处理若 TVJDataReady 高频调用考虑异步处理数据csharppublic async Task TVJDataReadyAsync(DaqChannelLink link) { await Task.Run(() TVJDataReady(link)); }性能监控使用 System.Diagnostics 监控内存和 CPUcsharplong memoryBefore GC.GetTotalMemory(false); TVJDataReady(link); long memoryAfter GC.GetTotalMemory(false); Console.WriteLine($Memory used: {(memoryAfter - memoryBefore) / 1024 / 1024} MB);清理无用代码移除注释掉的代码如 //Random random new Random();提高可读性。8. 总结通过以下措施优化缓冲区管理移除未使用的 m_VFBuffer[1] 或 darray2d[1]减少 8 MB 浪费。使用全局 m_VFBuffer 和 m_BufPool 复用缓冲区降低分配开销。动态调整缓冲区大小初始 80 KB最大 800 KB。限制 readCount 和 pointsToAdd防止过大分配。降低重采样阈值和次数控制 XValues 增长。推荐基于文件 1 的全局缓冲区方式结合上述优化适用于高频调用场景。若需文件 2 的隔离性可将 darray2d 改为类级变量并应用相同优化。如需针对特定场景例如 readCount 范围、调用频率进一步优化请提供更多细节我可以提供更精确的代码调整
返回列表