25
C++ User Group Mee-ng, Nizhniy Novgorod, 19.04.14 Опыт внедрения технологий параллельных вычислений для повышения производительности численных расчетов Крутько Е.С. НИЦ «Курчатовский институт» [email protected]

Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

  • Upload
    yandex

  • View
    349

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

C++  User  Group  Mee-ng,  Nizhniy  Novgorod,  19.04.14  

Опыт  внедрения  технологий  параллельных  вычислений  для  повышения  производительности  численных  расчетов  

Крутько  Е.С.  НИЦ  «Курчатовский  институт»  

[email protected]  

Page 2: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

План  презентации  

•  Рассмотрение  кода  программы  моделирования  динамики  твердого  тела  по  методу  конечных  элементов  

•  Использование  openmp  •  Явное  создание  вычислительных  потоков  •  Использование  MPI  •  Комбинированный  подход  

Репозиторий  проекта:  hnps://github.com/eskrut/directIntegra-onSolver.git  

2  

Page 3: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

Кратко  о  теории  

M q̈+ Cq̇+Kq = f

q = {dx0, dy0, dz0, · · · dxN�1, dyN�1, dzN�1}T

M =

0

BBB@

m0 0 0 00 m1 0 0

0 0. . . 0

0 0 0 mN�1

1

CCCA

C = ⇠M

q̇ =qt+�t � qt��t

2�t

, q̈ =qt+�t � 2qt + qt��t

2�t

qt+�t(a0M + a1C) = f �Kq+ a2Mqt � (a0M � a1C)qt��t

1

Задача   решается   по   методу  к о н е ч н ы х   э л е м е н т о в   с  использованием   явной   схемы  интегрирования   по   времени.  Вся   необходимая   логика   МКЭ  реализована   во   внешней  библиотеке,  необходимо  только  по с трои т ь   н а   е е   о снове  решатель.  

3  

Page 4: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

Объект  исследования  

~250  000    узлов  

4  

Page 5: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

Ветка:  Коммит:  

Основной  код  программы   master  9f49d14  

int main(int argc, char ** argv)!{! int discretParam, numOutput;! double t, tStop, dt, dtOut, tNextOut, transT;! double ampX, freqX, ksi;! bool recreateMesh = false;! bool makeNodesRenumbering = true;! po::options_description desc("Program options");! desc.add_options()! ("help,h", "print help message")! ("dt", po::value<double>(&dt)->default_value(1e-5), "integration time step")! ("tStop,t", po::value<double>(&tStop)->default_value(1e1), "time range")! ("transT", po::value<double>(&transT)->default_value(1e-3), "load transition time")! ("ampX,a", po::value<double>(&ampX)->default_value(1e-3), "displacement amplitude")! ("freqX,f", po::value<double>(&freqX)->default_value(1e0), "displacement frequency [Hz]")! ("ksi,k", po::value<double>(&ksi)->default_value(1e-1), "damping factor")! ("numOutput,n", po::value<int>(&numOutput)->default_value(500), "number of outputs")! ("discretParam,d", po::value<int>(&discretParam)->default_value(2), "discretisation parameter")! ("recreateMesh,m", "recreate mesh")! ("no-nr", "do not optimize nodes numbering")! ;! po::variables_map vm;! po::store(po::parse_command_line(argc, argv, desc), vm);! po::notify(vm);! if (vm.count("help") || vm.count("h")) { std::cout << desc << "\n"; return 1; }! if (vm.count("recreateMesh") || vm.count("m")) { recreateMesh = true; }! if (vm.count("no-nr")) { makeNodesRenumbering = false; }!! t = tNextOut = 0;! dtOut = tStop/numOutput;! double a0, a1, a2;! a0 = 1.0/(dt*dt); a1 = 1.0/(2.0*dt); a2 = 2.0*a0;

5  

Page 6: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

Ветка:  Коммит:  

Основной  код  программы   master  9f49d14  

// Create appropriate mesh! std::unique_ptr<sbfMesh> meshPtr(new sbfMesh);! if( recreateMesh || meshPtr->readMeshFromFiles() ){! meshPtr.reset(createMesh(discretParam));! if ( makeNodesRenumbering ) meshPtr->optimizeNodesNumbering();! meshPtr->writeMeshToFiles();! }! sbfMesh *mesh = meshPtr.get();! const int numNodes = mesh->numNodes();!! // Create and compute stiffness matrix! std::unique_ptr<sbfStiffMatrixBlock3x3> stiffPtr(createStiffMatrix(mesh, recreateMesh));! sbfStiffMatrixBlock3x3 *stiff = stiffPtr.get();!! // Create nodes data storages! NodesData<double, 3> displ_m1(mesh), displ("displ", mesh), displ_p1(mesh);! NodesData<double, 3> force(mesh), mass("mass", mesh), demp(mesh), rezKU(mesh);! mass.null(); force.null(); demp.null(); displ_m1.null(); displ.null();!! // Fill mass and demping arrays! computeMassDemp(mass, demp, mesh, stiff->propSet(), ksi, recreateMesh);!! // Get indexes of kinematic loaded nodes! std::vector<int> loadedNodesDown, loadedNodesUp;! double maxY = mesh->maxY();! for(int ct = 0; ct < numNodes; ct++){! if( std::fabs(mesh->node(ct).y()) < 1e-5) loadedNodesDown.push_back(ct);! if( std::fabs(mesh->node(ct).y() - maxY) < 1e-5 ) loadedNodesUp.push_back(ct);! }! const int numLDown = loadedNodesDown.size(), numLUp = loadedNodesUp.size();!! // Stiffness matrix iteration halper! std::unique_ptr<sbfMatrixIterator> iteratorPtr(stiff->createIterator());! sbfMatrixIterator *iterator = iteratorPtr.get();

6  

Page 7: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

Ветка:  Коммит:  

Основной  код  программы   master  9f49d14  

// Time integration loop! report.createNewProgress("Time integration");! while( t < tStop ) { // Time loop! // Update displacements of kinematically loaded nodes! double tmp[3], temp; getDispl(t, tmp, ampX, freqX, transT);! for(int nodeCt = 0; nodeCt < numLDown; ++nodeCt) for(int ct = 0; ct < 3; ct++)! displ.data(loadedNodesDown[nodeCt], ct) = tmp[ct];! for(int nodeCt = 0; nodeCt < numLUp; ++nodeCt) for(int ct = 0; ct < 3; ct++)! displ.data(loadedNodesUp[nodeCt], ct) = tmp[ct]/3;!! for(int nodeCt = 0; nodeCt < numNodes; nodeCt++) {! // Make multiplication of stiffness matrix over displacement vector! iterator->setToRow(nodeCt);! double *rez = rezKU.data() + nodeCt*3;! rez[0] = rez[1] = rez[2] = 0.0;! while(iterator->isValid()) {! int columnID = iterator->column();! double *vectPart = displ.data() + columnID*3;! double *block = iterator->data();! for(int rowCt = 0; rowCt < 3; ++rowCt)! for(int colCt = 0; colCt < 3; ++colCt)! rez[rowCt] += block[rowCt*3+colCt]*vectPart[colCt];! iterator->next();! }! // Perform finite difference step! for(int ct = 0; ct < 3; ct++) {! temp=force.data(nodeCt, ct) - rezKU.data(nodeCt, ct) +! a2*mass.data(nodeCt, ct)*displ.data(nodeCt, ct) -! (a0*mass.data(nodeCt, ct) - a1*demp.data(nodeCt, ct))*displ_m1.data(nodeCt, ct);! displ_p1.data(nodeCt, ct) = temp/(a0*mass.data(nodeCt, ct) + a1*demp.data(nodeCt, ct));! }! }! // Make output if required! if(t >= tNextOut) { displ.writeToFile();tNextOut += dtOut;report.updateProgress(t/tStop*100);}! // Prepere for next step! t += dt;! displ_m1.copyData(displ); displ.copyData(displ_p1);! } // End time loop! report.finalizeProgress();! return 0; 7  

Page 8: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

Ветка:  Коммит:  

Поиск  узкого  места   master  6d51e09  

// Time integration loop! report.createNewProgress("Time integration");! std::chrono::nanoseconds period1(0), period2(0), period3(0), period4(0);! while( t < tStop ) { // Time loop! auto timePoint_0 = Clock::now();! // Update displacements of kinematically loaded nodes! // .... //! auto timePoint_1 = Clock::now();! for(int nodeCt = 0; nodeCt < numNodes; nodeCt++) {! // Make multiplication of stiffness matrix over displacement vector! // .... //! }! auto timePoint_2 = Clock::now();! for(int nodeCt = 0; nodeCt < numNodes; nodeCt++) {! // Perform finite difference step! // .... //! }! auto timePoint_3 = Clock::now();! // Make output if required! // Prepere for next step! // .... //! auto timePoint_4 = Clock::now();! period1 += std::chrono::duration_cast<std::chrono::nanoseconds>(timePoint_1 - timePoint_0);! period2 += std::chrono::duration_cast<std::chrono::nanoseconds>(timePoint_2 - timePoint_1);! period3 += std::chrono::duration_cast<std::chrono::nanoseconds>(timePoint_3 - timePoint_2);! period4 += std::chrono::duration_cast<std::chrono::nanoseconds>(timePoint_4 - timePoint_3);! } // End time loop! report.finalizeProgress();!! report("Prepare part time : ", period1.count());! report("Matrix multiplication time : ", period2.count());! report("Finite difference step time : ", period3.count());! report("Finilizing part time : ", period4.count());!!Вывод: Prepare part time : 26608769!Matrix multiplication time : 87985118534!Finite difference step time : 7031352999!Finilizing part time : 2039084873! 8  

Page 9: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

OpenMP  

OpenMP   (Open   Mul--­‐Processing)   —   открытый   стандарт   для   распараллеливания  программ   на   языках   Си,   Си++   и   Фортран.   Описывает   совокупность   директив  компилятора,   библиотечных   процедур   и   переменных   окружения,   которые  предназначены   для   программирования   многопоточных   приложений   на  многопроцессорных  системах  с  общей  памятью  //Wikipedia  

#pragma  omp  parallel  #pragma  omp  for  #pragma  omp  sec-ons  #pragma  omp  single  #pragma  omp  parallel  for  #pragma  omp  parallel  sec-ons  #pragma  omp  task  #pragma  omp  master  #pragma  omp  cri-cal  #pragma  omp  barrier    

void  omp_set_num_threads(int  );  int  omp_get_num_threads(void);  int  omp_get_max_threads(void);  int  omp_get_thread_num(void);  int  omp_get_num_procs(void);  int  omp_in_parallel(void);  void  omp_set_dynamic(int  );  void  omp_set_lock(omp_lock_t  *);  int  omp_test_lock(omp_lock_t  *);      

OMP_SCHEDULE  type[,chunk]  OMP_NUM_THREADS  num  OMP_DYNAMIC  dynamic  OMP_NESTED  nested  OMP_STACKSIZE  size  OMP_WAIT_POLICY  policy  OMP_MAX_ACTIVE_LEVELS  levels  OMP_THREAD_LIMIT  limit  

9  

Page 10: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

Ветка:  Коммит:  

Первая  попытка  с  openMP  (не  работает)   openMP  99940bc  

CMakeLists.txt!!find_package(OpenMP REQUIRED) !set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") !!main.cpp!!#include <omp.h> !// ... //! // Stiffness matrix iteration halper! std::unique_ptr<sbfMatrixIterator> iteratorPtr(stiff->createIterator()); ! sbfMatrixIterator *iterator = iteratorPtr.get(); !! // Time integration loop! report.createNewProgress("Time integration"); ! while( t < tStop ) { // Time loop! // Update displacements of kinematically loaded nodes! // ... //!#pragma omp parallel for ! for(int nodeCt = 0; nodeCt < numNodes; nodeCt++) { ! // Make multiplication of stiffness matrix over displacement vector! iterator->setToRow(nodeCt); ! // ... //! while(iterator->isValid()) { ! // ... //! iterator->next(); ! } ! // Perform finite difference step! for(int ct = 0; ct < 3; ct++) { ! temp=force.data(nodeCt, ct) // ... //;! displ_p1.data(nodeCt, ct) = temp/(a0*mass.data(nodeCt, ct)// ... //);! } ! } ! // Make output if required! // Prepere for next step! } // End time loop 10  

Page 11: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

Ветка:  Коммит:  

Нормальная  работа  openMP   openMP    3f10371  

// Time integration loop! report.createNewProgress("Time integration");! while( t < tStop ) { // Time loop! double tmp[3], temp;! // Update displacements of kinematically loaded nodes! // ... //!#pragma omp parallel private(temp)! {! // Stiffness matrix iteration halper! std::unique_ptr<sbfMatrixIterator> iteratorPtr(stiff->createIterator());! sbfMatrixIterator *iterator = iteratorPtr.get();!#pragma omp for! for(int nodeCt = 0; nodeCt < numNodes; nodeCt++) {! // Make multiplication of stiffness matrix over displacement vector! iterator->setToRow(nodeCt);! // ... //! while(iterator->isValid()) {! // ... //! iterator->next();! }! // Perform finite difference step! for(int ct = 0; ct < 3; ct++) {! temp=force.data(nodeCt, ct) - rezKU.data(nodeCt, ct) +! a2*mass.data(nodeCt, ct)*displ.data(nodeCt, ct) -! (a0*mass.data(nodeCt, ct) - a1*demp.data(nodeCt, ct))*displ_m1.data(nodeCt, ct);! displ_p1.data(nodeCt, ct) = temp/(a0*mass.data(nodeCt, ct) + a1*demp.data(nodeCt, ct));! }! }//End of #pragma omp for! }//End of #pragma omp parallel! // Make output if required! // Prepere for next step! } // End time loop

11  

Page 12: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

Ветка:  Коммит:  

Реорганизация  области  параллельности   openMP    4579611  

// Time integration loop! double tmp[3]; !#pragma omp parallel ! { ! // Stiffness matrix iteration halper! std::unique_ptr<sbfMatrixIterator> iteratorPtr(stiff->createIterator()); ! sbfMatrixIterator *iterator = iteratorPtr.get(); ! while( t < tStop ) { // Time loop! // Update displacements of kinematically loaded nodes!#pragma omp master ! getDispl(t, tmp, ampX, freqX, transT); !#pragma omp for nowait! for(int nodeCt = 0; nodeCt < numLDown; ++nodeCt) for(int ct = 0; ct < 3; ct++) ! displ.data(loadedNodesDown[nodeCt], ct) = tmp[ct]; !#pragma omp for ! for(int nodeCt = 0; nodeCt < numLUp; ++nodeCt) for(int ct = 0; ct < 3; ct++) ! displ.data(loadedNodesUp[nodeCt], ct) = tmp[ct]/3;!#pragma omp for ! for(int nodeCt = 0; nodeCt < numNodes; nodeCt++) { ! // Make multiplication of stiffness matrix over displacement vector! iterator->setToRow(nodeCt); ! // ... //! while(iterator->isValid()) { ! // ... //! iterator->next(); ! } ! // Perform finite difference step! for(int ct = 0; ct < 3; ct++) ! // ... //! }//End of #pragma omp for!#pragma omp master ! {// Make output if required! t += dt;}//End of #pragma omp master!#pragma omp for ! for(int nodeCt = 0; nodeCt < numNodes; nodeCt++) { ! // Prepere for next step! for(int ct = 0; ct < 3; ct++) { ! displ_m1.data()[3*nodeCt+ct] = displ.data()[3*nodeCt+ct];} ! }//End of #pragma omp for! } // End time loop! }//End of #pragma omp parallel 12  

Page 13: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

Ветка:  Коммит:  

Параллельное  выполнение  секций  кода   openMP    84f7a19  

sbfMesh *mesh = new sbfMesh, tmp;! std::unique_ptr<sbfMesh> c(new sbfMesh);! std::unique_ptr<sbfMesh> plus(new sbfMesh);!#pragma omp parallel sections private(tmp)! {!#pragma omp section! {! //Construct C letter! std::unique_ptr<sbfMesh> roundInner(sbfMesh::makeCylinderPart(1,2,0,2*std::atan(1),0,1,d,2*d,d));! std::unique_ptr<sbfMesh> roundOuter(sbfMesh::makeCylinderPart(5,6,0,2*std::atan(1),0,1,d,8*d,d));! std::unique_ptr<sbfMesh> round(new sbfMesh);! round->addMesh(roundInner.get());! round->addMesh(roundOuter.get());! std::unique_ptr<sbfMesh> part0(sbfMesh::makeBlock(4, 1, 1, 4*d, d, d));! std::unique_ptr<sbfMesh> part4(sbfMesh::makeBlock(3, 1, 1, 3*d, d, d));! part0->translate(0, 4, 0);! c->addMesh(part0.get());! tmp = *round.get();! tmp.translate(4, 19, 0);! c->addMesh(tmp);! // ... ///! }//End of #pragma omp section!!#pragma omp section! {! //Construct plus sign! std::unique_ptr<sbfMesh> part10(sbfMesh::makeBlock(11, 1, 1, 11*d, d, d));! std::unique_ptr<sbfMesh> part11(sbfMesh::makeBlock(1, 3, 1, d, 3*d, d));! std::unique_ptr<sbfMesh> part12(sbfMesh::makeBlock(10, 1, 1, 10*d, d, d));! part11->translate(0, 1, 0);! part12->translate(0, 4, 0);! tmp = *part10.get();! // ... //! }//End of #pragma omp section! }//End of#pragma omp parallel sections private(tmp) 13  

Page 14: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

Явное  создание  отдельных  вычислительных  потоков  

Основные  инструменты  работы  с  потоками:  •  POSIX  /  WinAPI  •  boost::thread  •  std::thread  

14  

Page 15: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

Ветка:  Коммит:  

Создание  новых  потоков  вычисления  матричного  умножения  и  разностного  шага  

threads  94fc6ee  

#include <thread>! // ... //! int numThreads;! desc.add_options()! ("nt", po::value<int>(&numThreads)->default_value(8), "number of worker threads”);! // ... //! // Time integration loop! report.createNewProgress("Time integration");! while( t < tStop ) { // Time loop! // Update displacements of kinematically loaded nodes! // ... //! auto procFunc = [&](int start, int stop){! std::unique_ptr<sbfMatrixIterator> iteratorPtr(stiff->createIterator());! sbfMatrixIterator *iterator = iteratorPtr.get();! for(int nodeCt = start; nodeCt < stop; nodeCt++) {! // Make multiplication of stiffness matrix over displacement vector! iterator->setToRow(nodeCt);! while(iterator->isValid()) { /* ... */ }! // Perform finite difference step! for(int ct = 0; ct < 3; ct++) { /* ... */ }! }! };! std::vector<std::thread> threads; threads.resize(numThreads);! for(int ct = 0; ct < numThreads; ct++) threads[ct] =! std::thread( std::bind( procFunc,! numNodes/numThreads*ct,! ct != (numThreads-1) ? numNodes/numThreads*(ct+1) : numNodes) );! for(auto & th : threads) th.join();! // Make output if required! // Prepere for next step! } // End time loop

15  

Page 16: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

Ветка:  Коммит:  

Потоки  с  оповещениями  «начинай»/«сделал»   threads    �b6882  

#include <mutex>!#include <condition_variable>! // ... //! struct event{! std::mutex mtx;! std::condition_variable cond;! bool state{false};! void wait() {! std::unique_lock<std::mutex> lock(mtx);! while(!state) cond.wait(lock);! state = false;! }! void set() {! std::unique_lock<std::mutex> lock(mtx);! state = true;! cond.notify_one();! }! };! ! std::vector<event*> eventsStart, eventsStop;! std::vector<std::thread> threads; threads.resize(numThreads);!! auto procFunc = [&](int start, int stop, event *evStart, event *evStop){! std::unique_ptr<sbfMatrixIterator> iteratorPtr(stiff->createIterator());! sbfMatrixIterator *iterator = iteratorPtr.get();! evStop->set();! while(true){! evStart->wait();! for(int nodeCt = start; nodeCt < stop; nodeCt++) {! // Make multiplication of stiffness matrix over displacement vector! // Perform finite difference step! }! evStop->set();! }! }; 16  

Page 17: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

Ветка:  Коммит:  

Потоки  с  оповещениями  «начинай»/«сделал»   threads    �b6882  

for(int ct = 0; ct < numThreads; ct++) {! eventsStart.push_back(new event);! eventsStop.push_back(new event);! threads[ct] = std::thread(std::bind(! procFunc, numNodes/numThreads*ct,! ct != (numThreads-1) ? numNodes/numThreads*(ct+1) : numNodes,! eventsStart[ct], eventsStop[ct]));! threads[ct].detach();! }! for(auto ev : eventsStop) ev->wait();! ! // Time integration loop! report.createNewProgress("Time integration");! while( t < tStop ) { // Time loop! // Update displacements of kinematically loaded nodes! ! for(auto ev : eventsStart) ev->set();! for(auto ev : eventsStop) ev->wait();! ! // Make output if required! // Prepere for next step! } // End time loop

17  

Page 18: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

Ветка:  Коммит:  

Параллельное  выполнение  независимых  секций  кода  

threads    7f1534d  

#include <thread>!!sbfMesh *createMesh(int discretParam)!{! report("Recreating mesh");! sbfMesh *mesh = new sbfMesh;! ! std::unique_ptr<sbfMesh> c(new sbfMesh);! std::unique_ptr<sbfMesh> plus(new sbfMesh);! ! auto threadC = std::thread([&](){! sbfMesh tmp;! //Construct C letter! // ... //! c->addMesh(part4.get());! });! ! auto threadPlus = std::thread([&](){! sbfMesh tmp;! //Construct plus sign! // ... //! plus->translate(2.5, 12.5, 0);! });! ! threadC.join();! threadPlus.join();! ! // Use of "c" and "plus" to make mesh//! ! return mesh;!}

18  

Page 19: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

MPI  

Message  Passing  Interface  (MPI,  интерфейс  передачи  сообщений)  —  программный  интерфейс  для  передачи  информации,  который  позволяет  обмениваться  сообщениями  между  процессами,  выполняющими  одну  задачу.  //Wikipedia  

MPI_Comm_size  (comm,&size)  MPI_Comm_rank  (comm,&rank)    MPI_Send(&buffer,count,type,dest,tag,comm)  MPI_Isend(&buffer,count,type,dest,tag,comm,request)  MPI_Recv(&buffer,count,type,source,tag,comm,status)  MPI_Irecv(&buffer,count,type,source,tag,comm,request)    MPI_Bcast  (&buffer,count,datatype,root,comm)  MPI_Reduce  (&sendbuf,&recvbuf,count,datatype,op,root,comm)  MPI_Allreduce  (&sendbuf,&recvbuf,count,datatype,op,comm)    MPI_Barrier  (comm)  

19  

Page 20: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

Ветка:  Коммит:  

Использование  MPI   MPI  b42b24a  

CMakeLists.txt!!find_package(MPI REQUIRED)!include_directories(${MPI_INCLUDE_PATH})!target_link_libraries(${PROJECT_NAME} ${MPI_LIBRARIES})!set(CMAKE_CXX_COMPILE_FLAGS ${CMAKE_CXX_COMPILE_FLAGS} ${MPI_COMPILE_FLAGS})!set(CMAKE_CXX_LINK_FLAGS ${CMAKE_CXX_LINK_FLAGS} ${MPI_LINK_FLAGS})!!main.cpp!!#include <mpi.h>! // ... //! MPI_Init(&argc, &argv);! int procID, numProcs;! MPI_Comm_size(MPI_COMM_WORLD, &numProcs);! MPI_Comm_rank(MPI_COMM_WORLD, &procID);! ! // Create appropriate mesh! std::unique_ptr<sbfMesh> meshPtr(new sbfMesh);! if ( procID == 0 ) if( recreateMesh || meshPtr->readMeshFromFiles() ){! meshPtr.reset(createMesh(discretParam));! }! sbfMesh *mesh = meshPtr.get();! const int numNodes = mesh->numNodes();! MPI_Bcast(const_cast<int*>(&numNodes), 1, MPI_INT, 0, MPI_COMM_WORLD);!

20  

Page 21: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

Ветка:  Коммит:  

Использование  MPI   MPI    b42b24a  

// Create and compute stiffness matrix! std::unique_ptr<sbfStiffMatrixBlock3x3> stiffPtr(new sbfStiffMatrixBlock3x3);! int numBlocks[2], type;! if ( procID == 0 ) {! stiffPtr.reset(createStiffMatrix(mesh, recreateMesh));! numBlocks[0] = stiffPtr->numBlocks();! numBlocks[1] = stiffPtr->numBlocksAlter();! type = static_cast<int>(stiffPtr->type());! }! MPI_Bcast(numBlocks, 2, MPI_INT, 0, MPI_COMM_WORLD);! MPI_Bcast(&type, 1, MPI_INT, 0, MPI_COMM_WORLD);! if ( procID != 0 ) {! stiffPtr.reset(new sbfStiffMatrixBlock3x3(numBlocks[0], numNodes, numBlocks[1]));! stiffPtr->setType(static_cast<MatrixType>(type));! }! sbfStiffMatrixBlock3x3 *stiff = stiffPtr.get();! MPI_Bcast(const_cast<int *>(stiff->indData()), numBlocks[0], MPI_INT, 0, MPI_COMM_WORLD);! MPI_Bcast(const_cast<int *>(stiff->shiftIndData()), numNodes+1, MPI_INT, 0, MPI_COMM_WORLD);! if (numBlocks[1]) {! MPI_Bcast(const_cast<int *>(stiff->indDataAlter()), numBlocks[1], MPI_INT, 0, MPI_COMM_WORLD);! MPI_Bcast(const_cast<int *>(stiff->shiftIndDataAlter()), numNodes+1, MPI_INT, 0, MPI_COMM_WORLD);! }! MPI_Bcast(const_cast<double *>(stiff->data()), numBlocks[0]*9, MPI_DOUBLE, 0, MPI_COMM_WORLD);! if ( procID != 0 ) stiff->updataAlterPtr();!! // ... //!! // Fill mass and demping arrays! if ( procID == 0 ) computeMassDemp(mass, demp, mesh, stiff->propSet(), ksi, recreateMesh);! MPI_Bcast(mass.data(), numNodes*3, MPI_DOUBLE, 0, MPI_COMM_WORLD);! MPI_Bcast(demp.data(), numNodes*3, MPI_DOUBLE, 0, MPI_COMM_WORLD);!

21  

Page 22: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

Ветка:  Коммит:  

Использование  MPI   MPI    b42b24a  

// Time integration loop! int nodeStart = numNodes/numProcs*procID;! int nodeStop = procID != numProcs - 1 ? numNodes/numProcs*(procID+1) : numNodes;! if ( procID == 0 ) report.createNewProgress("Time integration");! while( t < tStop ) { // Time loop! // Update displacements of kinematically loaded nodes! // ... //! ! MPI_Bcast(displ.data(), numNodes*3, MPI_DOUBLE, 0, MPI_COMM_WORLD);! rezKU.null();! reduceBuf.null();! for(int nodeCt = nodeStart; nodeCt < nodeStop; nodeCt++) {! // Make multiplication of stiffness matrix over displacement vector! iterator->setToRow(nodeCt);! while(iterator->isValid()) { /* ... */ iterator->next(); }! // Perform finite difference step! for(int ct = 0; ct < 3; ct++) {! // ... //! reduceBuf.data()[3*nodeCt+ct] = temp/(a0*mass.data()[3*nodeCt+ct] + a1*demp.data()[3*nodeCt+ct]);! }! }! MPI_Allreduce(reduceBuf.data(), displ_p1.data(), numNodes*3, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);! ! // Make output if required! if ( procID == 0 ) if(t >= tNextOut) { /* ... */ }! // Prepere for next step! t += dt;! displ_m1.copyData(displ); displ.copyData(displ_p1);! } // End time loop! if ( procID == 0 ) report.finalizeProgress();! ! MPI_Finalize();!

22  

Page 23: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

Ветка:  Коммит:  

MPI  +  потоки   MPI_threads    7b033e3  

int nodeStart = numNodes/numProcs*procID;! int nodeStop = procID != numProcs - 1 ? numNodes/numProcs*(procID+1) : numNodes;! ! for(int ct = 0; ct < numThreads; ct++) {! eventsStart.push_back(new event);! eventsStop.push_back(new event);! int threadStartNode = nodeStart + (nodeStop - nodeStart)/numThreads*ct;! int threadStopNode = nodeStart + (nodeStop - nodeStart)/numThreads*(ct+1);! if(ct == numThreads-1) threadStopNode = nodeStop;! threads[ct] = std::thread(std::bind(procFunc, threadStartNode, threadStopNode, eventsStart[ct], eventsStop[ct]));! threads[ct].detach();! }! for(auto ev : eventsStop) ev->wait();! ! // Time integration loop! if ( procID == 0 ) report.createNewProgress("Time integration");! while( t < tStop ) { // Time loop! // Update displacements of kinematically loaded nodes! ! MPI_Bcast(displ.data(), numNodes*3, MPI_DOUBLE, 0, MPI_COMM_WORLD);! rezKU.null(); reduceBuf.null();! for(auto ev : eventsStart) ev->set();! for(auto ev : eventsStop) ev->wait();! MPI_Allreduce(reduceBuf.data(), displ_p1.data(), numNodes*3, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);! ! // Make output if required! if ( procID == 0 ) if(t >= tNextOut) { /* ... */}! // Prepere for next step! t += dt; displ_m1.copyData(displ); displ.copyData(displ_p1);! } // End time loop! if ( procID == 0 ) report.finalizeProgress();

23  

Page 24: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

Вместо  выводов  

Один  поток   openMP   threads   MPI   MPI  +  threads  

97   20   21   42   31  

IMHO:  •  При  современном  развитии  вычислительной  техники  разработчику  непростительно  

написание  не  параллельных  программ,  особенно  в  области  расчетов  •  Современные  стандарты  и  их  реализации  позволяют  относительно  быстро  

внедрять  их  в  существующие  или  вновь  создаваемые  проекты  •  Хотя,  возможно  в  ближайшем  будущем  компиляторы  научатся  делать  все  за  нас  :)  

24  

Page 25: Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повышения производительности

Крутько  Евгений  [email protected]  

google.com/+ЕвгенийКрутько  Репозиторий  проекта:  

hnps://github.com/eskrut/directIntegra-onSolver.git  

25