【问题标题】:Preallocating memory with C++ in realtime environment在实时环境中使用 C++ 预分配内存
【发布时间】:2010-05-12 13:12:59
【问题描述】:

我有一个函数,它获取一个 n 字节的输入缓冲区,并且需要一个 n 字节的辅助缓冲区来处理给定的输入缓冲区。

(我知道向量在运行时分配内存,假设我正在使用一个使用静态预分配内存的向量。想象一下,这不是一个 STL 向量。)

通常的做法是

void processData(vector<T> &vec) {
    vector<T> &aux = new vector<T>(vec.size()); //dynamically allocate memory
    // process data
}
//usage:
processData(v)

由于我在实时环境中工作,我希望提前预分配我需要的所有内存。

缓冲区仅在启动时分配一次。我希望每当我分配一个向量时,我会自动为我的processData 函数分配辅助缓冲区。

我可以用模板函数做类似的事情

static void _processData(vector<T> &vec,vector<T> &aux) {
     // process data
}
template<size_t sz>
void processData(vector<T> &vec) {
    static aux_buffer[sz];
    vector aux(vec.size(),aux_buffer); // use aux_buffer for the vector
    _processData(vec,aux);
}
// usage:
processData<V_MAX_SIZE>(v);

但是,使用模板进行大量工作并不是很有趣(现在让我们重新编译所有内容,因为我更改了评论!),并且每当我使用此功能时,它都会迫使我做一些簿记。

有没有更好的设计解决这个问题?

【问题讨论】:

  • 强制性问题:您是否分析过您的代码以证明动态内存分配确实存在问题?我知道每个人都说您应该预先为实时内容分配所有内容,但这实际上取决于您的系统。
  • 根据需求,我过去所做的是动态分配一个预先确定的大小。然后将所需的大小与已分配的大小进行比较,如果没有足够的空间就重新分配。这将确保您始终有足够的空间,并且分配最终会停止,从而稳定系统。
  • @Kristo:“实时”意味着如果处理时间超过指定时间,则会出现错误。分析只能显示分析运行期间发生的最坏情况,而不是理论上的最坏情况。动态分配只有在分配器能够保证其所花费时间的上限时才是安全的。
  • @Mike,我称之为“硬实时”。我目前以编写“软实时”代码(即,一些破坏的帧不是问题)为生。我们在各处动态分配内存,这不是问题。我们的硬件足够强大,可以满足我们的需要。我只是想清除 OP 方面不必要的过早优化。
  • @Kristo,首先,公司的政策是不使用动态分配。但即使不是,在关键任务代码中,我也需要向 mgmt 验证我的代码是否正确。如何通过动态分配做到这一点?我如何验证没有碎片会破坏事物?我认为编写没有动态分配的代码比验证我的动态分配是否正常工作更容易。

标签: c++ memory-management real-time


【解决方案1】:

我不明白您如何才能准确地获得您所描述的内容。这样的事情对你来说可能是一个很好的妥协。

void processData(vector<T>& vec)
{
    static vector<T> aux(vec.size());
    if (vec.size() > aux.size()) {
       aux.resize(vec.size());
    }
    ...
}

【讨论】:

  • 请注意,这会将内存预留从启动转移到第一次调用——这可能已经是一个问题。此外,这两个版本通常都不是线程安全的(不确定这是否是 OP 的问题)
  • 这就是为什么我提到这不是所要求的。支持此解决方案的一点:它没有每个可能大小的静态向量,这可能是一种浪费。
【解决方案2】:

我认为您可以在启动时预先分配并 mlock() 一个足够大的内存池,然后使用带有内存池分配器的常规 STL 容器(FSBA 或您自己的Boost)。

我正在研究我们的实时软件,但测试表明我们的硬件上的内存分配速度足以满足我们的目的。

【讨论】:

    【解决方案3】:

    vector aux(vec.size(),aux_buffer); // use aux_buffer for the vector

    这是 STL 中的新功能吗?还是自定义扩展?

    典型的解决方案是使用自定义分配器。但是,这不一定在代码中“更漂亮”。

    Some intro slides(警告:powerpoint!)
    Wikipedia
    Google

    【讨论】:

    • 引用我自己的话:“(我知道向量在运行时分配内存,假设我正在使用一个使用静态预分配内存的向量。想象这不是一个 STL 向量。)”。跨度>
    【解决方案4】:

    即使你成功地做到了这一点,它也可能达不到你想要的效果。根据您使用的操作系统及其实现虚拟内存的方式,您可能会发现您得到lazy allocation,其中只有部分内存分配被实际分配和映射,并且稍后会映射更多页面作为页面的结果故障。如果您的操作系统有 mlock 或等效项,那么您也许可以解决这个问题。

    【讨论】:

    • 页面错误?实时关键任务系统?我想不是。如果我们没有磁盘,我们会将内存页面存储在哪里?所有代码都应该适合内存,并留在那里。还是我错过了什么?
    • @Elazar:您没有说您使用的是什么操作系统,所以我只能假设您使用的是“普通或花园”操作系统,例如 Linux。如果您使用的是 VxWorks(或者至少是没有 VM 的操作系统),那当然是另一回事了。
    • vanilla Linux 是实时系统的“公共花园”吗?我认为 VxWorks 比 Linux 更配得上这个称号。
    【解决方案5】:

    假设我正在使用矢量 它使用静态预分配内存

    那么您应该能够在编译时获得预分配内存的大小(或最大大小)。 如果这样的向量将其大小作为模板参数,那么使用 processData 函数会更容易。

    template<class T, size_t sz>
    class vector 
    { 
     enum { size = sz } //either max size
    ...
    }
    
    template<class Vector>
    static void _processData(Vector &vec,Vector &aux)
    {
         // process data
    }
    template<class Vector>
    void processData(Vector &vec) {
        static aux_buffer[Vector::size];
        //no need to pass size into constructor, as Vector type knows it already
        Vector aux(aux_buffer); // use aux_buffer for the vector
        _processData(vec,aux);
    }
    // usage:
    vector<int, 30> v1;
    vector<float, 40> v2;
    //no need to specify template parameter explicitly
    //every call uses its own function instance and its own buffer of different size
    processData(v1);
    processData(v2);
    

    【讨论】:

      【解决方案6】:

      您需要担心的不是vector,而是内存分配器。

      你可以完美地创建一个内存分配器,它预先分配它的内存,然后在你构建它时将它传递给向量,这就是 Alloc 模板参数的用途!

      为了确保内存不是“虚拟”分配的,请在分配时触摸它。

      scoped_array<byte> buffer = new byte[SIZE];
      memset(buffer.get(), 0, SIZE);
      

      现在,您“只需”实现一个引用此内存池的自定义分配器并将其传递给向量实现:)

      【讨论】:

      • 自定义分配器如何知道我将调用它多少次?
      • 自定义意味着您自己编程,因此您可以对其功能做任何您想做的事情,例如记录分配和释放以供将来分析等...
      【解决方案7】:

      你能创建一个包含你的向量和一个同样大小的缓冲区的小结构吗?然后你会让你的向量随身携带它的处理缓冲区。如果你通过引用或指针传递它,你应该避免复制开销。伪代码如下:

      struct Container
      {
          vector<T> vec;
          vector<T> buf;
      
          Container(int size)
          {
              vec.reserve(size);
              buf.reserve(size);
          }
      };
      

      当前采用向量参数的任何函数都将采用Container

      【讨论】:

        【解决方案8】:

        也许您可以覆盖 new 和 delete 运算符,但您必须管理 你自己的全部记忆。您可以在开始时分配尽可能多的内存:

        void* operator new (std::size_t size) throw (std::bad_alloc);
        void* operator new[] (std::size_t size) throw (std::bad_alloc);
        void operator delete (void* ptr) throw ();
        void operator delete[] (void* ptr) throw ();
        

        【讨论】:

          猜你喜欢
          • 2013-09-04
          • 2023-03-25
          • 2021-05-18
          • 1970-01-01
          • 2019-01-17
          • 2014-07-17
          • 2011-04-28
          • 2012-10-10
          • 1970-01-01
          相关资源
          最近更新 更多