У меня есть программа, которая в настоящее время создает большие массивы и матрицы, размер которых может превышать 10 ГБ. Программа использует MPI для параллелизации рабочих нагрузок, но ограничена тем фактом, что каждому процессу требуется собственная копия массива или матрицы для выполнения своей части вычисления. Требования к памяти делают эту проблему неосуществимой с большим количеством процессов MPI, поэтому я рассматриваю Boost :: Interprocess как средство совместного использования данных между процессами MPI.C++ Совместное использование больших массивов и структур данных между процессами 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;
}
Я заметил, что это приводит к тому, всему общему объекту, который будет отображаться в каждые процессы пространство виртуальной памяти - это проблема с нашим вычислительным кластером, поскольку он ограничивает виртуальную память той же самой, что и физическая память. Есть ли способ поделиться этой структурой данных без необходимости отображать все пространство общей памяти - возможно, в виде обмена каким-то указателем? Будет ли попытка доступа к unmapped разделяемой памяти даже определить поведение? К сожалению, операции, которые мы выполняем в массиве, означают, что каждый процесс в конечном итоге должен получить доступ к каждому элементу в нем (хотя и не одновременно), я полагаю, что его можно разбить общий массив на части и обменять часть массива на нужные вам, но это не идеально).
«проблема с нашим вычислительным кластером, поскольку он ограничивает виртуальную память такой же, как и физическая память» - почему это имеет значение? Виртуальное адресное пространство для каждого процесса (адреса, на которых программа может отображать физическую память) отличается от виртуальной памяти (которая заключается в моделировании дополнительной физической памяти с использованием дискового пространства в качестве свопа). Вы говорите, что у ваших отдельных процессов заканчивается виртуальное адресное пространство? –
Хмм, я так не думаю, так как это 64-разрядная машина, так как было бы сложно вырваться из виртуального адресного пространства. У нас нет дисков для работы в качестве свопа на кластере, поэтому нет области подкачки, и, как только мы закончим физическую память, вот и все. Возможно, я ошибаюсь, но я думаю, что доступ к общему объекту памяти, использующему эту библиотеку, фактически отображает целостность объекта в адресное пространство каждого процесса - и это связано с тем, что память увеличивает потребление памяти? –
", и это связано с тем, что память увеличивает потребление памяти?" - нет, если вы явно не использовали разделяемую память в режиме make-a-private-copy-on-write, а затем записывали на нее. В противном случае на всем хосте будет только одна страница физической памяти, используемая для любой части данных. Возможно, вы можете рассчитать, когда это произойдет, если память будет дублирована, а затем попытайтесь ее перегрузить? –