动态向量的特征库内存使用

时间:2014-04-08 08:59:27

标签: c++ winapi memory eigen allocator

我有一个存储float32对象的二进制文件(9748422 * 5)。从这样的集合(190MB大小),我创建了一组Eigen::VectorXd向量(每个有5个组件),因此9748422。基础类型是double,因此大约是存储它们的输入大小的两倍。

但是,幸运的是,这个过程总共需要2.5GB。这是PROCESS_MEMORY_COUNTERS

的日志
    PageFaultCount: 0x000A3C40
    PeakWorkingSetSize: 0xA3C42000
    WorkingSetSize: 0xA3C42000
    QuotaPeakPagedPoolUsage: 0x00004ED8
    QuotaPagedPoolUsage: 0x00004ED8
    QuotaPeakNonPagedPoolUsage: 0x000057A8
    QuotaNonPagedPoolUsage: 0x000057A8
    PagefileUsage: 0xA3A9B000
    PeakPagefileUsage: 0xA3A9B000

我已经跟踪了Eigen的内部分配器,它确实似乎“分配”了我在纸上计算的大小。但是,Eigen对其大多数动态向量使用aligned_alloc。这会造成这么大的破坏吗?如果没有想到什么,你能否推荐另一个地方来寻找发生这种情况的原因?

我无法提供可编辑的(在线)cpp示例,但这里是我正在做的事情的骨架:

struct SSCCE_struct
{
    Eigen::VectorXd m_data;
};

typedef std::vector<SSCCE_struct*> TVector;

int main(int argc, char* argv[])
{
    TVector outputVertices;
    HANDLE bpcHandle;
    bpcHandle = CreateFileA("D:\\sample.bpc",              
        GENERIC_READ,          
        FILE_SHARE_READ,       
        NULL,                 
        OPEN_EXISTING,        
        FILE_ATTRIBUTE_NORMAL, 
        NULL);                 

    LARGE_INTEGER  len_li;
    GetFileSizeEx (bpcHandle, &len_li);
    INT64 len = len_li.QuadPart; //(len_li.u.HighPart << 32) | len_li.u.LowPart;

    unsigned long long noPoints = len / 20;
    unsigned long noPointsRead = 0;
    unsigned long long currPointIdx = 0;

    outputVertices.resize( noPoints );

    DebugTrace( "No points %lu \n", noPoints );

    float buffer[ 5 * 1024 ];
    DWORD noBytesRead = 0;
    do 
    {
        ReadFile(bpcHandle, buffer, sizeof(buffer), &noBytesRead, NULL);
        noPointsRead = noBytesRead / 20;
        for (unsigned long idx = 0; idx < noPointsRead; ++idx )
        {
            outputVertices[ currPointIdx + idx ] = new SSCCE_struct();

            outputVertices[ currPointIdx + idx ]->m_data.resize(5);

            for (unsigned kdx = 0; kdx < 5; ++kdx)
            {
                outputVertices[ currPointIdx + idx ]->m_data[ kdx ] = buffer[ 5 * idx + kdx ];
            }
        }

        currPointIdx += noPointsRead;

    } while (noBytesRead);


    CloseHandle(bpcHandle);
}
}

稍后编辑

我执行了David回答中指出的测试,解决方案是完全避免动态分配。有几个组合可以尝试,这里是所有这些的结果:

1

struct SSCCE_struct
{
    Eigen::Matrix<double,1,5> m_data;
};

typedef std::vector<SSCCE_struct*> TVector;

产生1.4 GB(1.1 GB浪费)

2

 struct SSCCE_struct
 {
    Eigen::VectorXd m_data;
 };

 typedef std::vector< SSCCE_struct* > TVector;

产生2.5 GB(2.2 GB废物)

3

struct SSCCE_struct
{
    Eigen::Matrix<double,1,5> m_data;
};

typedef std::vector<SSCCE_struct> TVector;

产生381 GB(40 MB的浪费 - 完全合理且可能是可预测的)。

2 个答案:

答案 0 :(得分:4)

你这里有很多指针,每个指针都有分配开销。指针指的是小对象,因此开销很大。

最重要的是,动态分配的对象必然比固定大小的对象具有更多的开销。这是因为固定大小的对象不需要存储矩阵维度。

以下是指针开销的来源:

  1. Eigen::VectorXd使用动态分配的存储空间。这意味着一个指针。
  2. 您将对象存储在std::vector<SSCCE_struct*>中。这是另一个指针,带有开销。
  3. 存储这些对象的最有效方法是删除间接。您可以通过切换到:

    来实现
    1. Matrix<double, 5, 1>。这是一个固定大小的对象,因此没有间接。更重要的是,如上所述,它不需要在运行时存储矩阵维度,因为它们在编译时是已知的。对于这样一个重要的小物体。
    2. 将对象存储在std::vector<SSCCE_struct>中。再次,你失去了一个间接层。
    3. 通过这些更改,在使用发行版设置进行编译时,程序的内存使用量在我的计算机上降至383MB。这更符合你的期望。

      差异似乎在Eigen::VectorXd和固定大小的对象之间。如果我使用Eigen::VectorXdstd::vector<SSCCE_struct>,则内存使用量会跳至918MB。当我转到std::vector<SSCCE_struct*>时,它会进一步跳转到1185MB。

      这些测量将高度依赖于编译器。我已经使用VS2013编译32位代码。

答案 1 :(得分:2)

我不允许发表评论,所以我会再发一个答案,即使我认为上述答案实际上解释了浪费内存的来源(大量分配)。

据我所知,你想要使用很多penta,所以你使用的是结构SSCCE_struct的向量,即

std::vector<SSCCE_struct*> TVector;

您是否考虑过使用

Eigen::Matrix< double, Dynamic, 5 > outputVertices;
outputVertices.resize( noPoints, 5 );

这可以避免浪费内存。我也会考虑将其用于矢量化(以帮助特征/编译器更好地矢量化你用它们做的任何事情),即使5不是一个非常方便的矢量化数字,如4或8。

编辑:我意识到我点击帖子后的时间已经晚了3年......