2013-07-10 2 views
1

У меня есть программа, которая в настоящее время создает большие массивы и матрицы, размер которых может превышать 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 разделяемой памяти даже определить поведение? К сожалению, операции, которые мы выполняем в массиве, означают, что каждый процесс в конечном итоге должен получить доступ к каждому элементу в нем (хотя и не одновременно), я полагаю, что его можно разбить общий массив на части и обменять часть массива на нужные вам, но это не идеально).

+0

«проблема с нашим вычислительным кластером, поскольку он ограничивает виртуальную память такой же, как и физическая память» - почему это имеет значение? Виртуальное адресное пространство для каждого процесса (адреса, на которых программа может отображать физическую память) отличается от виртуальной памяти (которая заключается в моделировании дополнительной физической памяти с использованием дискового пространства в качестве свопа). Вы говорите, что у ваших отдельных процессов заканчивается виртуальное адресное пространство? –

+0

Хмм, я так не думаю, так как это 64-разрядная машина, так как было бы сложно вырваться из виртуального адресного пространства. У нас нет дисков для работы в качестве свопа на кластере, поэтому нет области подкачки, и, как только мы закончим физическую память, вот и все. Возможно, я ошибаюсь, но я думаю, что доступ к общему объекту памяти, использующему эту библиотеку, фактически отображает целостность объекта в адресное пространство каждого процесса - и это связано с тем, что память увеличивает потребление памяти? –

+0

", и это связано с тем, что память увеличивает потребление памяти?" - нет, если вы явно не использовали разделяемую память в режиме make-a-private-copy-on-write, а затем записывали на нее. В противном случае на всем хосте будет только одна страница физической памяти, используемая для любой части данных. Возможно, вы можете рассчитать, когда это произойдет, если память будет дублирована, а затем попытайтесь ее перегрузить? –

ответ

0

Поскольку данные, которые вы хотите предоставить, настолько велики, может быть более практичным рассматривать данные как истинный файл и использовать операции с файлами для чтения необходимых данных. Затем вам не нужно использовать разделяемую память для совместного использования файла, просто каждый процесс читается непосредственно из файловой системы.

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

Спасибо за предложение. Мое впечатление, что скорость диска быстро станет серьезным узким местом, когда вы пытаетесь увеличить количество процессоров. Тем не менее, это требует некоторых испытаний. –

+0

Это не очень хорошая идея. У MPI гораздо больше шансов решить эту проблему с разумной производительностью, чем параллельная файловая система. Также, как только будут обновлены данные, файловая система убьет вас. – Zulan

+0

@ Zulan: Скажем, вам нужно решение для системы на 2 ГБ. Тогда отображение всех в виртуальную память по-прежнему будет подвергать подкачку. Единственный способ сделать решение с общей памятью - получить больше оперативной памяти. Если вы замените свой диск SSD на вашей системе 2 ГБ, вы можете достичь приемлемого уровня производительности. – jxh