【问题标题】:C++ Sharing Large Arrays and Data Structures Between MPI ProcessesC++ 在 MPI 进程之间共享大型数组和数据结构
【发布时间】:2013-07-10 07:51:19
【问题描述】:

我有一个程序,目前可以生成大小超过 10GB 的大型数组和矩阵。该程序使用 MPI 来并行化工作负载,但受到以下事实的限制:每个进程都需要自己的数组或矩阵副本才能执行其部分计算。大量 MPI 进程的内存要求使这个问题无法解决,因此我一直在研究 Boost::Interprocess 作为在 MPI 进程之间共享数据的一种方式。

到目前为止,我已经提出了以下方法,它创建了一个大向量并将其元素的总和并行化:

#include <cstdlib>
#include <ctime>
#include <functional>
#include <iostream>
#include <string>
#include <utility>

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/tuple/tuple_comparison.hpp>
#include <mpi.h>

typedef boost::interprocess::allocator<double, boost::interprocess::managed_shared_memory::segment_manager> ShmemAllocator;
typedef boost::interprocess::vector<double, ShmemAllocator> MyVector;

const std::size_t vector_size = 1000000000;
const std::string shared_memory_name = "vector_shared_test.cpp";

int main(int argc, char **argv) {
    int numprocs, rank;

    MPI::Init();
    numprocs = MPI::COMM_WORLD.Get_size();
    rank = MPI::COMM_WORLD.Get_rank();

    if(numprocs >= 2) {
        if(rank == 0) {
            std::cout << "On process rank " << rank << "." << std::endl;
            std::time_t creation_start = std::time(NULL);

            boost::interprocess::shared_memory_object::remove(shared_memory_name.c_str());
            boost::interprocess::managed_shared_memory segment(boost::interprocess::create_only, shared_memory_name.c_str(), size_t(12000000000));

            std::cout << "Size of double: " << sizeof(double) << std::endl;
            std::cout << "Allocated shared memory: " << segment.get_size() << std::endl;

            const ShmemAllocator alloc_inst(segment.get_segment_manager());

            MyVector *myvector = segment.construct<MyVector>("MyVector")(alloc_inst);

            std::cout << "myvector max size: " << myvector->max_size() << std::endl;

            for(int i = 0; i < vector_size; i++) {
                myvector->push_back(double(i));
            }

            std::cout << "Vector capacity: " << myvector->capacity() << " | Memory Free: " << segment.get_free_memory() << std::endl;

            std::cout << "Vector creation successful and took " << std::difftime(std::time(NULL), creation_start) << " seconds." << std::endl;
        }

        std::flush(std::cout);
        MPI::COMM_WORLD.Barrier();

        std::time_t summing_start = std::time(NULL);

        std::cout << "On process rank " << rank << "." << std::endl;
        boost::interprocess::managed_shared_memory segment(boost::interprocess::open_only, shared_memory_name.c_str());

        MyVector *myvector = segment.find<MyVector>("MyVector").first;
        double result = 0;

        for(int i = rank; i < myvector->size(); i = i + numprocs) {
            result = result + (*myvector)[i];
        }
        double total = 0;
        MPI::COMM_WORLD.Reduce(&result, &total, 1, MPI::DOUBLE, MPI::SUM, 0);

        std::flush(std::cout);
        MPI::COMM_WORLD.Barrier();

        if(rank == 0) {
            std::cout << "On process rank " << rank << "." << std::endl;
            std::cout << "Vector summing successful and took " << std::difftime(std::time(NULL), summing_start) << " seconds." << std::endl;

            std::cout << "The arithmetic sum of the elements in the vector is " << total << std::endl;
            segment.destroy<MyVector>("MyVector");
        }

        std::flush(std::cout);
        MPI::COMM_WORLD.Barrier();

        boost::interprocess::shared_memory_object::remove(shared_memory_name.c_str());
    }

    sleep(300);
    MPI::Finalize();

    return 0;
}

我注意到这会导致整个共享对象映射到每个进程的虚拟内存空间 - 这是我们的计算集群的一个问题,因为它将虚拟内存限制为与物理内存相同。有没有办法共享这个数据结构而不必映射出整个共享内存空间——也许是以共享某种指针的形式?尝试访问未映射的共享内存是否会被定义为行为?不幸的是,我们在数组上执行的操作意味着每个进程最终都需要访问其中的每个元素(尽管不是同时进行的——我想它可以将共享数组分解成多个部分并为您需要的部分交换数组的部分,但是这并不理想)。

【问题讨论】:

  • “这是我们的计算集群的一个问题,因为它将虚拟内存限制为与物理内存相同” - 为什么这很重要?每个进程的虚拟地址空间(程序可以映射物理内存的地址)不同于虚拟内存(虚拟内存是关于使用磁盘空间作为交换来模拟额外的物理内存)。您是说您的各个进程的虚拟地址空间不足?
  • 嗯,我不这么认为,因为它是 64 位机器,很难用完虚拟地址空间。我们没有磁盘驱动器作为集群上的交换,所以没有分页区域,因此一旦我们用完物理内存,就是这样。我可能错了,但我认为使用这个库访问共享内存对象实际上将整个对象映射到每个进程的地址空间 - 这会消耗内存导致内存使用量上升?
  • "这会消耗内存导致内存使用率上升?" - 除非您在写时生成私有复制模式下明确使用共享内存,然后再写入它。否则,在整个主机上仍然只有一个物理内存页用于数据的任何给定部分。或许你可以计算一下如果内存被复制,它什么时候会失败,然后尝试重载它?
  • 在我看来,您可能正在尝试使用低级技术细节来解决高级算法问题。如果您的算法使用大量数据并且每个进程都需要访问所有内容,那么它本质上就无法很好地扩展。您确定没有办法更好地分解全局数据并根据局部性进行不同的工作分配吗?
  • 了解更多有关数据访问模式的信息会很有帮助,尤其是有关更新的信息。

标签: c++ mpi boost-interprocess


【解决方案1】:

由于你要分享的数据很大,把数据当作一个真实的文件,用文件操作来读取你想要的数据,可能会更实用。那么,就不需要使用共享内存来共享文件了,只需要让各个进程直接从文件系统中读取即可。

ifstream file ("data.dat", ios::in | ios::binary);
file.seekg(someOffset, ios::beg);
file.read(array, sizeof(array));

【讨论】:

  • 感谢您的建议。我的印象是,当您尝试增加处理器数量时,磁盘速度将很快成为严重的瓶颈。尽管如此,它仍需要进行一些测试。
  • 这不是一个好主意。 MPI 比并行文件系统有更好的机会以合理的性能解决这个问题。此外,一旦数据有更新,文件系统就会杀死你。
  • @Zulan:假设您需要 2 GB 系统的解决方案。然后,将所有内容映射到虚拟内存中无论如何都会产生分页。使共享内存解决方案高效的唯一方法是获得更多 RAM。如果您将磁盘替换为 2 GB 系统上的 SSD,您可能会达到可接受的性能水平。
  • @jhx:这不适用于并行(集群)系统,尤其是没有本地磁盘的系统。在不同节点的 RAM 之间复制数据比在 RAM 和并行文件系统之间复制数据要便宜得多。此外,与从一个节点读取并行文件系统并将其分布在节点之间相比,从每个节点使用低级别调用访问并行文件系统通常会导致性能非常差。这就是存在高级并行 I/O 库的原因。您的想法可能适用于具有本地 ext3 的 SMP 系统,但适用于具有例如光泽它不会。
  • @Zulan:我假设每个节点都有一个本地磁盘。我假设一次数据传输。我假设将传递消息以表达来自其他节点的文件的相关差异。我假设最终结果的本地更改可以存储在本地,并且最终结果可以在最后收集。这是很多假设,但这是我通常构建分布式解决方案的方式。
猜你喜欢
  • 2015-03-29
  • 2017-03-15
  • 2021-09-22
  • 2013-03-23
  • 2016-10-05
  • 1970-01-01
  • 2016-11-07
  • 2011-06-13
  • 2013-07-21
相关资源
最近更新 更多