【问题标题】:Eigen and huge dense 2D arrays本征和巨大的密集二维阵列
【发布时间】:2018-06-30 19:48:08
【问题描述】:

我在一个项目中使用 2D Eigen::Arrays,我喜欢在大型 2D 数组的情况下继续使用它们。

为了避免内存问题,我想使用内存映射文件来管理(读取/修改/写入)这些数组,但我找不到工作示例。

我发现的最接近的例子是基于boost::interprocessthis,但它使用共享内存(虽然我更喜欢持久存储)。

缺乏示例让我担心是否有更好的主流替代解决方案来解决我的问题。是这样吗?一个最小的例子会很方便。

编辑:

这是一个解释我在 cmets 中的用例的最小示例:

#include <Eigen/Dense>


int main()
{
    // Order of magnitude of the required arrays
    Eigen::Index rows = 50000;
    Eigen::Index cols = 40000;

    {
        // Array creation (this is where the memory mapped file should be created)
        Eigen::ArrayXXf arr1 = Eigen::ArrayXXf::Zero( rows, cols );

        // Some operations on the array
        for(Eigen::Index i = 0; i < rows; ++i)
        {
            for(Eigen::Index j = 0; j < cols; ++j)
            {
                arr1( i, j ) = float(i * j);
            }
        }

        // The array goes out of scope, but the data are persistently stored in the file
    }

    {
        // This should actually use the data stored in the file
        Eigen::ArrayXXf arr2 = Eigen::ArrayXXf::Zero( rows, cols );

        // Manipulation of the array data
        for(Eigen::Index i = 0; i < rows; ++i)
        {
            for(Eigen::Index j = 0; j < cols; ++j)
            {
                arr2( i, j ) += 1.0f;
            }
        }

        // The array goes out of scope, but the data are persistently stored in the file
    }

}

【问题讨论】:

  • 您可以创建巨大的交换文件并根据需要拥有操作系统交换页面
  • @Darklighter,您能否发表更多评论?
  • 您可以简单地通过页面/交换文件扩展您的虚拟内存,而不是尝试将文件用作一块内存。这允许您使用大于物理内存的矩阵。不过,它可能只在 SSD 上运行得相当好。
  • 正如您自己所说的,minimal reproducible example 您实际打算做的事情会非常方便。或者至少是一些伪代码。您是否存储了想要线性遍历/随机访问的现有数组?或者您是否在运行时生成了太大而无法放入 RAM 的巨大数组?您使用的数量级是多少?
  • 您应该能够想出一些东西,使用boost::interprocess 将文件映射为内存缓冲区,然后 (Eigen::Map)[eigen.tuxfamily.org/dox/group__TutorialMapClass.html] 来查看它并将其作为ArrayXXf.

标签: c++ multidimensional-array eigen memory-mapped-files eigen3


【解决方案1】:

于是我用谷歌搜索了

提升内存映射文件

并在第一个结果中遇到boost::iostreams::mapped_file

结合从this commentEigen::Map 的链接,我测试了以下内容:

#include <boost/iostreams/device/mapped_file.hpp>
#include <Eigen/Dense>
boost::iostreams::mapped_file file("foo.bin");

const std::size_t rows = 163840;
const std::size_t columns = 163840;
if (rows * columns * sizeof(float) > file.size()) {
    throw std::runtime_error("file of size " + std::to_string(file.size()) + " couldn’t fit float Matrix of " + std::to_string(rows) + "×"  + std::to_string(columns));
}

Eigen::Map<Eigen::MatrixXf> matrix(reinterpret_cast<float*>(file.data()), rows, columns);

std::cout << matrix(0, 0) << ' ' << matrix(rows - 1, columns - 1) << std::endl;
matrix(0, 0) = 0.5;
matrix(rows - 1, columns - 1) = 0.5;

使用

find_package(Boost REQUIRED COMPONENTS iostreams)
find_package(Eigen3 REQUIRED)
target_link_libraries(${PROJECT_NAME} Boost::iostreams Eigen3::Eigen)

然后我用谷歌搜索

windows 创建虚拟文件

first result 给了我

fsutil file createnew foo.bin 107374182400

运行程序两次给出:

0 0

0.5 0.5

不会增加内存使用量。

所以它就像一个魅力。

【讨论】:

    【解决方案2】:

    我认为为此编写自己的课程并不难。

    第一次初始化数组,创建一个大小为x * y * elem_sizememory map的文件。

    您甚至可以添加一个包含大小、x、y 等信息的小标题 - 这样,如果您重新打开这些标题,您就会获得所需的所有信息。

    现在您有一个大内存块,您可以使用成员函数elem(x,y)get_elem() / set_elem() 或使用[] operator,并在该函数中计算数据元素的位置。

    关闭文件或在两者之间提交,将保存数据。

    对于非常大的文件,最好在需要时仅map 部分文件以避免创建非常大的页表。

    Windows 特定(不确定 Linux 中是否可用):

    • 如果您不需要将数据保存在磁盘上,您可以使用delete on close 标志打开文件。如果内存不可用,这只会(临时)写入磁盘。

    • 对于稀疏数组,可以使用稀疏文件。这些文件仅将磁盘空间用于包含数据的块。所有其他块都是虚拟的,默认为全零。

    【讨论】:

      【解决方案3】:

      基于this comment 和这些答案(https://stackoverflow.com/a/51256963/2741329https://stackoverflow.com/a/51256597/2741329),这是我的工作解决方案:

      #include <boost/interprocess/file_mapping.hpp>
      #include <boost/interprocess/mapped_region.hpp>
      #include <Eigen/Dense>
      #include <iostream>
      #include <fstream>
      #include <filesystem>
      
      namespace fs = std::experimental::filesystem;
      namespace bi = boost::interprocess;
      
      int main() {
      
        std::string array_bin_path = "array.bin";
        const int64_t nr_rows = 28000;
        const int64_t nr_cols = 35000;
        const int64_t array_size = nr_rows * nr_cols * sizeof(float);
        std::cout << "array size: " << array_size << std::endl;
      
        // if the file already exists but the size is different, remove it
        if(fs::exists(array_bin_path))
        {
          int64_t file_size = fs::file_size(array_bin_path);
          std::cout << "file size: " << file_size << std::endl;
          if(array_size != file_size)
          {
            fs::remove(array_bin_path);
          }
        }
      
        // create a binary file of the required size
        if(!fs::exists(array_bin_path))
        {
          std::ofstream ofs(array_bin_path, std::ios::binary | std::ios::out | std::ios::trunc);
          ofs.seekp(array_size - 1);
          ofs.put(0);
          ofs.close();
        }
      
        // use boost interprocess to memory map the file
        const bi::file_mapping mapped_file(array_bin_path.c_str(), bi::read_write);
        bi::mapped_region region(mapped_file, bi::read_write);
      
        // get the address of the mapped region
        void * addr = region.get_address();
      
        const std::size_t region_size = region.get_size();
        std::cout << "region size: " << region_size << std::endl;
      
        // map the file content into a Eigen array
        Eigen::Map<Eigen::ArrayXXf> my_array(reinterpret_cast<float*>(addr), nr_rows, nr_cols);
      
        // modify the content
        std::cout << "initial array(0, 1) value: " << my_array(0, 1) << std::endl;
        my_array(0, 1) += 1.234f;
        std::cout << "final array(0, 1) value: " << my_array(0, 1) << std::endl;
      
        return 0;
      }
      

      它使用:

      • boost::interprocess 代替 boost::iostreams 因为它是仅标题。此外,mapped_region 在我想在单个映射文件上存储多个数组的情况下很方便。
      • std::fstream 创建二进制文件,std::experimental::filesystem 检查它。
      • Eigen::ArrayXXf 我的问题所要求的。

      【讨论】:

      • @ggael,你有上述解决方案的 cmets 吗?
      猜你喜欢
      • 2012-11-20
      • 2010-12-30
      • 2013-05-04
      • 2011-05-01
      • 1970-01-01
      • 2015-11-04
      • 2014-12-23
      • 2019-04-29
      • 2021-04-24
      相关资源
      最近更新 更多