From 78c94aec5107d12943b68d3877f1346675e6c0cb Mon Sep 17 00:00:00 2001 From: Morten Hjorth-Jensen Date: Thu, 14 Mar 2024 15:48:52 +0100 Subject: [PATCH] update week 9 --- doc/pub/week9/html/._week9-bs000.html | 504 +- doc/pub/week9/html/._week9-bs001.html | 515 +- doc/pub/week9/html/._week9-bs002.html | 504 +- doc/pub/week9/html/._week9-bs003.html | 504 +- doc/pub/week9/html/._week9-bs004.html | 504 +- doc/pub/week9/html/._week9-bs005.html | 504 +- doc/pub/week9/html/._week9-bs006.html | 504 +- doc/pub/week9/html/._week9-bs007.html | 504 +- doc/pub/week9/html/._week9-bs008.html | 504 +- doc/pub/week9/html/._week9-bs009.html | 504 +- doc/pub/week9/html/._week9-bs010.html | 504 +- doc/pub/week9/html/._week9-bs011.html | 504 +- doc/pub/week9/html/._week9-bs012.html | 504 +- doc/pub/week9/html/._week9-bs013.html | 511 +- doc/pub/week9/html/._week9-bs014.html | 506 +- doc/pub/week9/html/._week9-bs015.html | 507 +- doc/pub/week9/html/._week9-bs016.html | 508 +- doc/pub/week9/html/._week9-bs017.html | 509 +- doc/pub/week9/html/._week9-bs018.html | 510 +- doc/pub/week9/html/._week9-bs019.html | 521 +- doc/pub/week9/html/._week9-bs020.html | 759 ++- doc/pub/week9/html/._week9-bs021.html | 773 ++- doc/pub/week9/html/._week9-bs022.html | 666 ++- doc/pub/week9/html/._week9-bs023.html | 613 ++- doc/pub/week9/html/._week9-bs024.html | 645 ++- doc/pub/week9/html/._week9-bs025.html | 619 ++- doc/pub/week9/html/._week9-bs026.html | 650 ++- doc/pub/week9/html/._week9-bs027.html | 814 ++- doc/pub/week9/html/._week9-bs028.html | 682 ++- doc/pub/week9/html/._week9-bs029.html | 380 +- doc/pub/week9/html/._week9-bs030.html | 382 +- doc/pub/week9/html/._week9-bs031.html | 374 +- doc/pub/week9/html/._week9-bs032.html | 376 +- doc/pub/week9/html/._week9-bs033.html | 369 +- doc/pub/week9/html/._week9-bs034.html | 400 +- doc/pub/week9/html/._week9-bs035.html | 381 +- doc/pub/week9/html/._week9-bs036.html | 400 +- doc/pub/week9/html/._week9-bs037.html | 389 +- doc/pub/week9/html/._week9-bs038.html | 409 +- doc/pub/week9/html/._week9-bs039.html | 408 +- doc/pub/week9/html/._week9-bs040.html | 439 +- doc/pub/week9/html/._week9-bs041.html | 491 +- doc/pub/week9/html/._week9-bs042.html | 494 +- doc/pub/week9/html/._week9-bs043.html | 449 +- doc/pub/week9/html/._week9-bs044.html | 391 +- doc/pub/week9/html/._week9-bs045.html | 407 +- doc/pub/week9/html/._week9-bs046.html | 396 +- doc/pub/week9/html/._week9-bs047.html | 391 +- doc/pub/week9/html/._week9-bs048.html | 422 +- doc/pub/week9/html/._week9-bs049.html | 412 +- doc/pub/week9/html/._week9-bs050.html | 395 +- doc/pub/week9/html/._week9-bs051.html | 383 +- doc/pub/week9/html/._week9-bs052.html | 375 +- doc/pub/week9/html/._week9-bs053.html | 387 +- doc/pub/week9/html/._week9-bs054.html | 374 +- doc/pub/week9/html/._week9-bs055.html | 388 +- doc/pub/week9/html/._week9-bs056.html | 390 +- doc/pub/week9/html/._week9-bs057.html | 400 +- doc/pub/week9/html/._week9-bs058.html | 374 +- doc/pub/week9/html/._week9-bs059.html | 394 +- doc/pub/week9/html/._week9-bs060.html | 414 +- doc/pub/week9/html/._week9-bs061.html | 387 +- doc/pub/week9/html/._week9-bs062.html | 419 +- doc/pub/week9/html/._week9-bs063.html | 437 +- doc/pub/week9/html/._week9-bs064.html | 450 +- doc/pub/week9/html/._week9-bs065.html | 386 +- doc/pub/week9/html/._week9-bs066.html | 383 +- doc/pub/week9/html/._week9-bs067.html | 367 +- doc/pub/week9/html/._week9-bs068.html | 384 +- doc/pub/week9/html/._week9-bs069.html | 376 +- doc/pub/week9/html/._week9-bs070.html | 402 +- doc/pub/week9/html/._week9-bs071.html | 387 +- doc/pub/week9/html/._week9-bs072.html | 388 +- doc/pub/week9/html/._week9-bs073.html | 374 +- doc/pub/week9/html/._week9-bs074.html | 382 +- doc/pub/week9/html/._week9-bs075.html | 381 +- doc/pub/week9/html/._week9-bs076.html | 406 +- doc/pub/week9/html/._week9-bs077.html | 424 +- doc/pub/week9/html/._week9-bs078.html | 442 +- doc/pub/week9/html/._week9-bs079.html | 373 +- doc/pub/week9/html/._week9-bs080.html | 391 +- doc/pub/week9/html/._week9-bs081.html | 864 +--- doc/pub/week9/html/._week9-bs082.html | 385 +- doc/pub/week9/html/._week9-bs083.html | 426 +- doc/pub/week9/html/._week9-bs084.html | 402 +- doc/pub/week9/html/._week9-bs085.html | 373 +- doc/pub/week9/html/._week9-bs086.html | 414 +- doc/pub/week9/html/._week9-bs087.html | 411 +- doc/pub/week9/html/._week9-bs088.html | 383 +- doc/pub/week9/html/._week9-bs089.html | 416 +- doc/pub/week9/html/._week9-bs090.html | 369 +- doc/pub/week9/html/._week9-bs091.html | 407 +- doc/pub/week9/html/._week9-bs092.html | 405 +- doc/pub/week9/html/._week9-bs093.html | 413 +- doc/pub/week9/html/._week9-bs094.html | 377 +- doc/pub/week9/html/._week9-bs095.html | 405 +- doc/pub/week9/html/._week9-bs096.html | 409 +- doc/pub/week9/html/._week9-bs097.html | 411 +- doc/pub/week9/html/._week9-bs098.html | 412 +- doc/pub/week9/html/._week9-bs099.html | 401 +- doc/pub/week9/html/._week9-bs100.html | 398 +- doc/pub/week9/html/._week9-bs101.html | 412 +- doc/pub/week9/html/._week9-bs102.html | 843 ++- doc/pub/week9/html/._week9-bs103.html | 409 +- doc/pub/week9/html/._week9-bs104.html | 421 +- doc/pub/week9/html/._week9-bs105.html | 412 +- doc/pub/week9/html/._week9-bs106.html | 397 +- doc/pub/week9/html/._week9-bs107.html | 409 +- doc/pub/week9/html/._week9-bs108.html | 404 +- doc/pub/week9/html/._week9-bs109.html | 415 +- doc/pub/week9/html/._week9-bs110.html | 434 +- doc/pub/week9/html/._week9-bs111.html | 390 +- doc/pub/week9/html/._week9-bs112.html | 402 +- doc/pub/week9/html/._week9-bs113.html | 405 +- doc/pub/week9/html/._week9-bs114.html | 402 +- doc/pub/week9/html/._week9-bs115.html | 395 +- doc/pub/week9/html/._week9-bs116.html | 389 +- doc/pub/week9/html/._week9-bs117.html | 415 +- doc/pub/week9/html/._week9-bs118.html | 462 +- doc/pub/week9/html/._week9-bs119.html | 501 +- doc/pub/week9/html/._week9-bs120.html | 751 +++ doc/pub/week9/html/._week9-bs121.html | 732 +++ doc/pub/week9/html/._week9-bs122.html | 735 +++ doc/pub/week9/html/._week9-bs123.html | 750 +++ doc/pub/week9/html/._week9-bs124.html | 820 +++ doc/pub/week9/html/._week9-bs125.html | 715 +++ doc/pub/week9/html/._week9-bs126.html | 707 +++ doc/pub/week9/html/._week9-bs127.html | 768 +++ doc/pub/week9/html/._week9-bs128.html | 733 +++ doc/pub/week9/html/._week9-bs129.html | 731 +++ doc/pub/week9/html/._week9-bs130.html | 759 +++ doc/pub/week9/html/._week9-bs131.html | 730 +++ doc/pub/week9/html/._week9-bs132.html | 754 +++ doc/pub/week9/html/._week9-bs133.html | 727 +++ doc/pub/week9/html/._week9-bs134.html | 732 +++ doc/pub/week9/html/._week9-bs135.html | 689 +++ doc/pub/week9/html/._week9-bs136.html | 700 +++ doc/pub/week9/html/._week9-bs137.html | 727 +++ doc/pub/week9/html/._week9-bs138.html | 726 +++ doc/pub/week9/html/._week9-bs139.html | 764 +++ doc/pub/week9/html/._week9-bs140.html | 782 +++ doc/pub/week9/html/week9-bs.html | 504 +- doc/pub/week9/html/week9-reveal.html | 5222 ++++++++++++++++++- doc/pub/week9/html/week9-solarized.html | 5468 +++++++++++++++++++- doc/pub/week9/html/week9.html | 5468 +++++++++++++++++++- doc/pub/week9/ipynb/ipynb-week9-src.tar.gz | Bin 192 -> 191 bytes doc/pub/week9/ipynb/week9.ipynb | 5017 +++++++++++++++++- doc/pub/week9/pdf/week9-beamer.pdf | Bin 320420 -> 518462 bytes doc/pub/week9/pdf/week9.pdf | Bin 370140 -> 501505 bytes doc/src/week9/programs/automersenne.cpp | 76 + doc/src/week9/programs/mc.py | 54 + doc/src/week9/programs/plot.py | 16 + doc/src/week9/programs/uniformhisto.py | 23 + 153 files changed, 73939 insertions(+), 17368 deletions(-) create mode 100644 doc/pub/week9/html/._week9-bs120.html create mode 100644 doc/pub/week9/html/._week9-bs121.html create mode 100644 doc/pub/week9/html/._week9-bs122.html create mode 100644 doc/pub/week9/html/._week9-bs123.html create mode 100644 doc/pub/week9/html/._week9-bs124.html create mode 100644 doc/pub/week9/html/._week9-bs125.html create mode 100644 doc/pub/week9/html/._week9-bs126.html create mode 100644 doc/pub/week9/html/._week9-bs127.html create mode 100644 doc/pub/week9/html/._week9-bs128.html create mode 100644 doc/pub/week9/html/._week9-bs129.html create mode 100644 doc/pub/week9/html/._week9-bs130.html create mode 100644 doc/pub/week9/html/._week9-bs131.html create mode 100644 doc/pub/week9/html/._week9-bs132.html create mode 100644 doc/pub/week9/html/._week9-bs133.html create mode 100644 doc/pub/week9/html/._week9-bs134.html create mode 100644 doc/pub/week9/html/._week9-bs135.html create mode 100644 doc/pub/week9/html/._week9-bs136.html create mode 100644 doc/pub/week9/html/._week9-bs137.html create mode 100644 doc/pub/week9/html/._week9-bs138.html create mode 100644 doc/pub/week9/html/._week9-bs139.html create mode 100644 doc/pub/week9/html/._week9-bs140.html create mode 100644 doc/src/week9/programs/automersenne.cpp create mode 100644 doc/src/week9/programs/mc.py create mode 100644 doc/src/week9/programs/plot.py create mode 100644 doc/src/week9/programs/uniformhisto.py diff --git a/doc/pub/week9/html/._week9-bs000.html b/doc/pub/week9/html/._week9-bs000.html index 0ee9f022..eb84da37 100644 --- a/doc/pub/week9/html/._week9-bs000.html +++ b/doc/pub/week9/html/._week9-bs000.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -193,7 +689,7 @@

    March 11-15

  • 9
  • 10
  • ...
  • -
  • 22
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs001.html b/doc/pub/week9/html/._week9-bs001.html index 977e8485..78e442fe 100644 --- a/doc/pub/week9/html/._week9-bs001.html +++ b/doc/pub/week9/html/._week9-bs001.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -166,16 +662,7 @@

    Overview of week 11, Mar - - +

    Note, these notes contain additional material om optimization and parallelization. Parts of this material will be discussed this week.

    @@ -193,7 +680,7 @@

    Overview of week 11, Mar
  • 10
  • 11
  • ...
  • -
  • 22
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs002.html b/doc/pub/week9/html/._week9-bs002.html index c96461c9..d9e8847c 100644 --- a/doc/pub/week9/html/._week9-bs002.html +++ b/doc/pub/week9/html/._week9-bs002.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -180,7 +676,7 @@

    Why resampling methods ?

  • 11
  • 12
  • ...
  • -
  • 22
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs003.html b/doc/pub/week9/html/._week9-bs003.html index 426da770..93beacca 100644 --- a/doc/pub/week9/html/._week9-bs003.html +++ b/doc/pub/week9/html/._week9-bs003.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -185,7 +681,7 @@

    Statistical analysis

  • 12
  • 13
  • ...
  • -
  • 22
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs004.html b/doc/pub/week9/html/._week9-bs004.html index 18244a7c..63514813 100644 --- a/doc/pub/week9/html/._week9-bs004.html +++ b/doc/pub/week9/html/._week9-bs004.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -181,7 +677,7 @@

    And why do we use such me
  • 13
  • 14
  • ...
  • -
  • 22
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs005.html b/doc/pub/week9/html/._week9-bs005.html index d6b04049..8c6f395a 100644 --- a/doc/pub/week9/html/._week9-bs005.html +++ b/doc/pub/week9/html/._week9-bs005.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -192,7 +688,7 @@

    Central limit theorem

  • 14
  • 15
  • ...
  • -
  • 22
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs006.html b/doc/pub/week9/html/._week9-bs006.html index c5569c77..b4cebfed 100644 --- a/doc/pub/week9/html/._week9-bs006.html +++ b/doc/pub/week9/html/._week9-bs006.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -179,7 +675,7 @@

    Further remarks

  • 15
  • 16
  • ...
  • -
  • 22
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs007.html b/doc/pub/week9/html/._week9-bs007.html index 4c38912b..ac828571 100644 --- a/doc/pub/week9/html/._week9-bs007.html +++ b/doc/pub/week9/html/._week9-bs007.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -193,7 +689,7 @@

    Running many measurements

    16
  • 17
  • ...
  • -
  • 22
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs008.html b/doc/pub/week9/html/._week9-bs008.html index a20bb699..5eae9dce 100644 --- a/doc/pub/week9/html/._week9-bs008.html +++ b/doc/pub/week9/html/._week9-bs008.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -191,7 +687,7 @@

    Adding more definitions

  • 17
  • 18
  • ...
  • -
  • 22
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs009.html b/doc/pub/week9/html/._week9-bs009.html index 9ecf8b6f..07e5de6b 100644 --- a/doc/pub/week9/html/._week9-bs009.html +++ b/doc/pub/week9/html/._week9-bs009.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -186,7 +682,7 @@

    Further rewriting

  • 18
  • 19
  • ...
  • -
  • 22
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs010.html b/doc/pub/week9/html/._week9-bs010.html index f41eadcc..14a3cd97 100644 --- a/doc/pub/week9/html/._week9-bs010.html +++ b/doc/pub/week9/html/._week9-bs010.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -196,7 +692,7 @@

    The covariance term

  • 19
  • 20
  • ...
  • -
  • 22
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs011.html b/doc/pub/week9/html/._week9-bs011.html index 765472ef..5980ce77 100644 --- a/doc/pub/week9/html/._week9-bs011.html +++ b/doc/pub/week9/html/._week9-bs011.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -193,7 +689,7 @@

    Rewriting the covariance t
  • 20
  • 21
  • ...
  • -
  • 22
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs012.html b/doc/pub/week9/html/._week9-bs012.html index 083ad35c..7610e62a 100644 --- a/doc/pub/week9/html/._week9-bs012.html +++ b/doc/pub/week9/html/._week9-bs012.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -185,6 +681,8 @@

    Introducing the cor
  • 20
  • 21
  • 22
  • +
  • ...
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs013.html b/doc/pub/week9/html/._week9-bs013.html index b3926051..39f540a0 100644 --- a/doc/pub/week9/html/._week9-bs013.html +++ b/doc/pub/week9/html/._week9-bs013.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -153,9 +649,9 @@

    Resampling methods: Blocking

    The blocking method was made popular by Flyvbjerg and Pedersen (1989) -and has become one of the standard ways to estimate -\( V(\widehat{\theta}) \) for exactly one \( \widehat{\theta} \), namely -\( \widehat{\theta} = \overline{X} \). +and has become one of the standard ways to estimate the variance +\( \mathrm{var}(\widehat{\theta}) \) for exactly one estimator \( \widehat{\theta} \), namely +\( \widehat{\theta} = \overline{X} \), the mean value.

    Assume \( n = 2^d \) for some integer \( d>1 \) and \( X_1,X_2,\cdots, X_n \) is a stationary time series to begin with. @@ -191,6 +687,9 @@

    Resampling methods: Blocking
  • 20
  • 21
  • 22
  • +
  • 23
  • +
  • ...
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs014.html b/doc/pub/week9/html/._week9-bs014.html index 17f75c4d..fa7b50aa 100644 --- a/doc/pub/week9/html/._week9-bs014.html +++ b/doc/pub/week9/html/._week9-bs014.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -180,6 +676,10 @@

    Why blocking?

  • 20
  • 21
  • 22
  • +
  • 23
  • +
  • 24
  • +
  • ...
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs015.html b/doc/pub/week9/html/._week9-bs015.html index 58375c48..72673cc9 100644 --- a/doc/pub/week9/html/._week9-bs015.html +++ b/doc/pub/week9/html/._week9-bs015.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -190,6 +686,11 @@

    Blocking Transformations

  • 20
  • 21
  • 22
  • +
  • 23
  • +
  • 24
  • +
  • 25
  • +
  • ...
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs016.html b/doc/pub/week9/html/._week9-bs016.html index 2e67d0cc..74235b3b 100644 --- a/doc/pub/week9/html/._week9-bs016.html +++ b/doc/pub/week9/html/._week9-bs016.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -188,6 +684,12 @@

    Blocking transformations

  • 20
  • 21
  • 22
  • +
  • 23
  • +
  • 24
  • +
  • 25
  • +
  • 26
  • +
  • ...
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs017.html b/doc/pub/week9/html/._week9-bs017.html index 9b2ac236..628dc398 100644 --- a/doc/pub/week9/html/._week9-bs017.html +++ b/doc/pub/week9/html/._week9-bs017.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -191,6 +687,13 @@

    Blocking Transformations

  • 20
  • 21
  • 22
  • +
  • 23
  • +
  • 24
  • +
  • 25
  • +
  • 26
  • +
  • 27
  • +
  • ...
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs018.html b/doc/pub/week9/html/._week9-bs018.html index db3ecf38..ce4aec3d 100644 --- a/doc/pub/week9/html/._week9-bs018.html +++ b/doc/pub/week9/html/._week9-bs018.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -187,6 +683,14 @@

    Blocking Transfor
  • 20
  • 21
  • 22
  • +
  • 23
  • +
  • 24
  • +
  • 25
  • +
  • 26
  • +
  • 27
  • +
  • 28
  • +
  • ...
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs019.html b/doc/pub/week9/html/._week9-bs019.html index 0366c982..0fd8978e 100644 --- a/doc/pub/week9/html/._week9-bs019.html +++ b/doc/pub/week9/html/._week9-bs019.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -168,16 +664,6 @@

    Blocking Tran \end{align} $$ -

    Flyvbjerg and Petersen demonstrated that the sequence -\( \{e_k\}_{k=0}^{d-1} \) is decreasing, and conjecture that the term -\( e_k \) can be made as small as we would like by making \( k \) (and hence -\( d \)) sufficiently large. The sequence is decreasing (Master of Science thesis by Marius Jonsson, UiO 2018). -It means we can apply blocking transformations until -\( e_k \) is sufficiently small, and then estimate \( \mathrm{var}(\overline{X}) \) by -\( \widehat{\sigma}^2_k/n_k \). -

    - -

    For an elegant solution and proof of the blocking method, see the recent article of Marius Jonsson (former MSc student of the Computational Physics group).

    @@ -196,6 +682,15 @@

    Blocking Tran
  • 20
  • 21
  • 22
  • +
  • 23
  • +
  • 24
  • +
  • 25
  • +
  • 26
  • +
  • 27
  • +
  • 28
  • +
  • 29
  • +
  • ...
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs020.html b/doc/pub/week9/html/._week9-bs020.html index 29d40bbe..5dc169b1 100644 --- a/doc/pub/week9/html/._week9-bs020.html +++ b/doc/pub/week9/html/._week9-bs020.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -150,245 +646,18 @@

     

     

     

    -

    Example code form last week

    - - -
    -
    -
    -
    -
    -
    # 2-electron VMC code for 2dim quantum dot with importance sampling
    -# Using gaussian rng for new positions and Metropolis- Hastings 
    -# Added energy minimization
    -from math import exp, sqrt
    -from random import random, seed, normalvariate
    -import numpy as np
    -import matplotlib.pyplot as plt
    -from mpl_toolkits.mplot3d import Axes3D
    -from matplotlib import cm
    -from matplotlib.ticker import LinearLocator, FormatStrFormatter
    -from scipy.optimize import minimize
    -import sys
    -import os
    -
    -# Where to save data files
    -PROJECT_ROOT_DIR = "Results"
    -DATA_ID = "Results/EnergyMin"
    +

    More on the blocking method

    -if not os.path.exists(PROJECT_ROOT_DIR): - os.mkdir(PROJECT_ROOT_DIR) - -if not os.path.exists(DATA_ID): - os.makedirs(DATA_ID) - -def data_path(dat_id): - return os.path.join(DATA_ID, dat_id) - -outfile = open(data_path("Energies.dat"),'w') - - -# Trial wave function for the 2-electron quantum dot in two dims -def WaveFunction(r,alpha,beta): - r1 = r[0,0]**2 + r[0,1]**2 - r2 = r[1,0]**2 + r[1,1]**2 - r12 = sqrt((r[0,0]-r[1,0])**2 + (r[0,1]-r[1,1])**2) - deno = r12/(1+beta*r12) - return exp(-0.5*alpha*(r1+r2)+deno) - -# Local energy for the 2-electron quantum dot in two dims, using analytical local energy -def LocalEnergy(r,alpha,beta): - - r1 = (r[0,0]**2 + r[0,1]**2) - r2 = (r[1,0]**2 + r[1,1]**2) - r12 = sqrt((r[0,0]-r[1,0])**2 + (r[0,1]-r[1,1])**2) - deno = 1.0/(1+beta*r12) - deno2 = deno*deno - return 0.5*(1-alpha*alpha)*(r1 + r2) +2.0*alpha + 1.0/r12+deno2*(alpha*r12-deno2+2*beta*deno-1.0/r12) - -# Derivate of wave function ansatz as function of variational parameters -def DerivativeWFansatz(r,alpha,beta): - - WfDer = np.zeros((2), np.double) - r1 = (r[0,0]**2 + r[0,1]**2) - r2 = (r[1,0]**2 + r[1,1]**2) - r12 = sqrt((r[0,0]-r[1,0])**2 + (r[0,1]-r[1,1])**2) - deno = 1.0/(1+beta*r12) - deno2 = deno*deno - WfDer[0] = -0.5*(r1+r2) - WfDer[1] = -r12*r12*deno2 - return WfDer - -# Setting up the quantum force for the two-electron quantum dot, recall that it is a vector -def QuantumForce(r,alpha,beta): - - qforce = np.zeros((NumberParticles,Dimension), np.double) - r12 = sqrt((r[0,0]-r[1,0])**2 + (r[0,1]-r[1,1])**2) - deno = 1.0/(1+beta*r12) - qforce[0,:] = -2*r[0,:]*alpha*(r[0,:]-r[1,:])*deno*deno/r12 - qforce[1,:] = -2*r[1,:]*alpha*(r[1,:]-r[0,:])*deno*deno/r12 - return qforce - - -# Computing the derivative of the energy and the energy -def EnergyDerivative(x0): - - - # Parameters in the Fokker-Planck simulation of the quantum force - D = 0.5 - TimeStep = 0.05 - # positions - PositionOld = np.zeros((NumberParticles,Dimension), np.double) - PositionNew = np.zeros((NumberParticles,Dimension), np.double) - # Quantum force - QuantumForceOld = np.zeros((NumberParticles,Dimension), np.double) - QuantumForceNew = np.zeros((NumberParticles,Dimension), np.double) - - energy = 0.0 - DeltaE = 0.0 - alpha = x0[0] - beta = x0[1] - EnergyDer = 0.0 - DeltaPsi = 0.0 - DerivativePsiE = 0.0 - #Initial position - for i in range(NumberParticles): - for j in range(Dimension): - PositionOld[i,j] = normalvariate(0.0,1.0)*sqrt(TimeStep) - wfold = WaveFunction(PositionOld,alpha,beta) - QuantumForceOld = QuantumForce(PositionOld,alpha, beta) - - #Loop over MC MCcycles - for MCcycle in range(NumberMCcycles): - #Trial position moving one particle at the time - for i in range(NumberParticles): - for j in range(Dimension): - PositionNew[i,j] = PositionOld[i,j]+normalvariate(0.0,1.0)*sqrt(TimeStep)+\ - QuantumForceOld[i,j]*TimeStep*D - wfnew = WaveFunction(PositionNew,alpha,beta) - QuantumForceNew = QuantumForce(PositionNew,alpha, beta) - GreensFunction = 0.0 - for j in range(Dimension): - GreensFunction += 0.5*(QuantumForceOld[i,j]+QuantumForceNew[i,j])*\ - (D*TimeStep*0.5*(QuantumForceOld[i,j]-QuantumForceNew[i,j])-\ - PositionNew[i,j]+PositionOld[i,j]) - - GreensFunction = exp(GreensFunction) - ProbabilityRatio = GreensFunction*wfnew**2/wfold**2 - #Metropolis-Hastings test to see whether we accept the move - if random() <= ProbabilityRatio: - for j in range(Dimension): - PositionOld[i,j] = PositionNew[i,j] - QuantumForceOld[i,j] = QuantumForceNew[i,j] - wfold = wfnew - DeltaE = LocalEnergy(PositionOld,alpha,beta) - DerPsi = DerivativeWFansatz(PositionOld,alpha,beta) - DeltaPsi += DerPsi - energy += DeltaE - DerivativePsiE += DerPsi*DeltaE - - # We calculate mean values - energy /= NumberMCcycles - DerivativePsiE /= NumberMCcycles - DeltaPsi /= NumberMCcycles - EnergyDer = 2*(DerivativePsiE-DeltaPsi*energy) - return EnergyDer - - -# Computing the expectation value of the local energy -def Energy(x0): - # Parameters in the Fokker-Planck simulation of the quantum force - D = 0.5 - TimeStep = 0.05 - # positions - PositionOld = np.zeros((NumberParticles,Dimension), np.double) - PositionNew = np.zeros((NumberParticles,Dimension), np.double) - # Quantum force - QuantumForceOld = np.zeros((NumberParticles,Dimension), np.double) - QuantumForceNew = np.zeros((NumberParticles,Dimension), np.double) - - energy = 0.0 - DeltaE = 0.0 - alpha = x0[0] - beta = x0[1] - #Initial position - for i in range(NumberParticles): - for j in range(Dimension): - PositionOld[i,j] = normalvariate(0.0,1.0)*sqrt(TimeStep) - wfold = WaveFunction(PositionOld,alpha,beta) - QuantumForceOld = QuantumForce(PositionOld,alpha, beta) - - #Loop over MC MCcycles - for MCcycle in range(NumberMCcycles): - #Trial position moving one particle at the time - for i in range(NumberParticles): - for j in range(Dimension): - PositionNew[i,j] = PositionOld[i,j]+normalvariate(0.0,1.0)*sqrt(TimeStep)+\ - QuantumForceOld[i,j]*TimeStep*D - wfnew = WaveFunction(PositionNew,alpha,beta) - QuantumForceNew = QuantumForce(PositionNew,alpha, beta) - GreensFunction = 0.0 - for j in range(Dimension): - GreensFunction += 0.5*(QuantumForceOld[i,j]+QuantumForceNew[i,j])*\ - (D*TimeStep*0.5*(QuantumForceOld[i,j]-QuantumForceNew[i,j])-\ - PositionNew[i,j]+PositionOld[i,j]) - - GreensFunction = exp(GreensFunction) - ProbabilityRatio = GreensFunction*wfnew**2/wfold**2 - #Metropolis-Hastings test to see whether we accept the move - if random() <= ProbabilityRatio: - for j in range(Dimension): - PositionOld[i,j] = PositionNew[i,j] - QuantumForceOld[i,j] = QuantumForceNew[i,j] - wfold = wfnew - DeltaE = LocalEnergy(PositionOld,alpha,beta) - energy += DeltaE - if Printout: - outfile.write('%f\n' %(energy/(MCcycle+1.0))) - # We calculate mean values - energy /= NumberMCcycles - return energy - -#Here starts the main program with variable declarations -NumberParticles = 2 -Dimension = 2 -# seed for rng generator -seed() -# Monte Carlo cycles for parameter optimization -Printout = False -NumberMCcycles= 10000 -# guess for variational parameters -x0 = np.array([0.9,0.2]) -# Using Broydens method to find optimal parameters -res = minimize(Energy, x0, method='BFGS', jac=EnergyDerivative, options={'gtol': 1e-4,'disp': True}) -x0 = res.x -# Compute the energy again with the optimal parameters and increased number of Monte Cycles -NumberMCcycles= 2**19 -Printout = True -FinalEnergy = Energy(x0) -EResult = np.array([FinalEnergy,FinalEnergy]) -outfile.close() -#nice printout with Pandas -import pandas as pd -from pandas import DataFrame -data ={'Optimal Parameters':x0, 'Final Energy':EResult} -frame = pd.DataFrame(data) -print(frame) -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    +

    Flyvbjerg and Petersen demonstrated that the sequence +\( \{e_k\}_{k=0}^{d-1} \) is decreasing, and conjecture that the term +\( e_k \) can be made as small as we would like by making \( k \) (and hence +\( d \)) sufficiently large. The sequence is decreasing. +It means we can apply blocking transformations until +\( e_k \) is sufficiently small, and then estimate \( \mathrm{var}(\overline{X}) \) by +\( \widehat{\sigma}^2_k/n_k \). +

    +

    For an elegant solution and proof of the blocking method, see the recent article of Marius Jonsson (former MSc student of the Computational Physics group).

    @@ -406,6 +675,16 @@

    Example code form last week
  • 20
  • 21
  • 22
  • +
  • 23
  • +
  • 24
  • +
  • 25
  • +
  • 26
  • +
  • 27
  • +
  • 28
  • +
  • 29
  • +
  • 30
  • +
  • ...
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs021.html b/doc/pub/week9/html/._week9-bs021.html index a017baf4..12543a6e 100644 --- a/doc/pub/week9/html/._week9-bs021.html +++ b/doc/pub/week9/html/._week9-bs021.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -150,13 +646,7 @@

     

     

     

    -

    Resampling analysis

    - -

    The next step is then to use the above data sets and perform a -resampling analysis using the blocking method -The blocking code, based on the article of Marius Jonsson is given here -

    - +

    Example code form last week

    @@ -164,60 +654,221 @@

    Resampling analysis

    -
    # Common imports
    +  
    # 2-electron VMC code for 2dim quantum dot with importance sampling
    +# Using gaussian rng for new positions and Metropolis- Hastings 
    +# Added energy minimization
    +from math import exp, sqrt
    +from random import random, seed, normalvariate
    +import numpy as np
    +import matplotlib.pyplot as plt
    +from mpl_toolkits.mplot3d import Axes3D
    +from matplotlib import cm
    +from matplotlib.ticker import LinearLocator, FormatStrFormatter
    +from scipy.optimize import minimize
    +import sys
     import os
     
    -# Where to save the figures and data files
    +# Where to save data files
    +PROJECT_ROOT_DIR = "Results"
     DATA_ID = "Results/EnergyMin"
     
    +if not os.path.exists(PROJECT_ROOT_DIR):
    +    os.mkdir(PROJECT_ROOT_DIR)
    +
    +if not os.path.exists(DATA_ID):
    +    os.makedirs(DATA_ID)
    +
     def data_path(dat_id):
         return os.path.join(DATA_ID, dat_id)
     
    -infile = open(data_path("Energies.dat"),'r')
    -
    -from numpy import log2, zeros, mean, var, sum, loadtxt, arange, array, cumsum, dot, transpose, diagonal, sqrt
    -from numpy.linalg import inv
    -
    -def block(x):
    -    # preliminaries
    -    n = len(x)
    -    d = int(log2(n))
    -    s, gamma = zeros(d), zeros(d)
    -    mu = mean(x)
    -
    -    # estimate the auto-covariance and variances 
    -    # for each blocking transformation
    -    for i in arange(0,d):
    -        n = len(x)
    -        # estimate autocovariance of x
    -        gamma[i] = (n)**(-1)*sum( (x[0:(n-1)]-mu)*(x[1:n]-mu) )
    -        # estimate variance of x
    -        s[i] = var(x)
    -        # perform blocking transformation
    -        x = 0.5*(x[0::2] + x[1::2])
    -   
    -    # generate the test observator M_k from the theorem
    -    M = (cumsum( ((gamma/s)**2*2**arange(1,d+1)[::-1])[::-1] )  )[::-1]
    -
    -    # we need a list of magic numbers
    -    q =array([6.634897,9.210340, 11.344867, 13.276704, 15.086272, 16.811894, 18.475307, 20.090235, 21.665994, 23.209251, 24.724970, 26.216967, 27.688250, 29.141238, 30.577914, 31.999927, 33.408664, 34.805306, 36.190869, 37.566235, 38.932173, 40.289360, 41.638398, 42.979820, 44.314105, 45.641683, 46.962942, 48.278236, 49.587884, 50.892181])
    -
    -    # use magic to determine when we should have stopped blocking
    -    for k in arange(0,d):
    -        if(M[k] < q[k]):
    -            break
    -    if (k >= d-1):
    -        print("Warning: Use more data")
    -    return mu, s[k]/2**(d-k)
    -
    -
    -x = loadtxt(infile)
    -(mean, var) = block(x) 
    -std = sqrt(var)
    +outfile = open(data_path("Energies.dat"),'w')
    +
    +
    +# Trial wave function for the 2-electron quantum dot in two dims
    +def WaveFunction(r,alpha,beta):
    +    r1 = r[0,0]**2 + r[0,1]**2
    +    r2 = r[1,0]**2 + r[1,1]**2
    +    r12 = sqrt((r[0,0]-r[1,0])**2 + (r[0,1]-r[1,1])**2)
    +    deno = r12/(1+beta*r12)
    +    return exp(-0.5*alpha*(r1+r2)+deno)
    +
    +# Local energy  for the 2-electron quantum dot in two dims, using analytical local energy
    +def LocalEnergy(r,alpha,beta):
    +    
    +    r1 = (r[0,0]**2 + r[0,1]**2)
    +    r2 = (r[1,0]**2 + r[1,1]**2)
    +    r12 = sqrt((r[0,0]-r[1,0])**2 + (r[0,1]-r[1,1])**2)
    +    deno = 1.0/(1+beta*r12)
    +    deno2 = deno*deno
    +    return 0.5*(1-alpha*alpha)*(r1 + r2) +2.0*alpha + 1.0/r12+deno2*(alpha*r12-deno2+2*beta*deno-1.0/r12)
    +
    +# Derivate of wave function ansatz as function of variational parameters
    +def DerivativeWFansatz(r,alpha,beta):
    +    
    +    WfDer  = np.zeros((2), np.double)
    +    r1 = (r[0,0]**2 + r[0,1]**2)
    +    r2 = (r[1,0]**2 + r[1,1]**2)
    +    r12 = sqrt((r[0,0]-r[1,0])**2 + (r[0,1]-r[1,1])**2)
    +    deno = 1.0/(1+beta*r12)
    +    deno2 = deno*deno
    +    WfDer[0] = -0.5*(r1+r2)
    +    WfDer[1] = -r12*r12*deno2
    +    return  WfDer
    +
    +# Setting up the quantum force for the two-electron quantum dot, recall that it is a vector
    +def QuantumForce(r,alpha,beta):
    +
    +    qforce = np.zeros((NumberParticles,Dimension), np.double)
    +    r12 = sqrt((r[0,0]-r[1,0])**2 + (r[0,1]-r[1,1])**2)
    +    deno = 1.0/(1+beta*r12)
    +    qforce[0,:] = -2*r[0,:]*alpha*(r[0,:]-r[1,:])*deno*deno/r12
    +    qforce[1,:] = -2*r[1,:]*alpha*(r[1,:]-r[0,:])*deno*deno/r12
    +    return qforce
    +    
    +
    +# Computing the derivative of the energy and the energy 
    +def EnergyDerivative(x0):
    +
    +    
    +    # Parameters in the Fokker-Planck simulation of the quantum force
    +    D = 0.5
    +    TimeStep = 0.05
    +    # positions
    +    PositionOld = np.zeros((NumberParticles,Dimension), np.double)
    +    PositionNew = np.zeros((NumberParticles,Dimension), np.double)
    +    # Quantum force
    +    QuantumForceOld = np.zeros((NumberParticles,Dimension), np.double)
    +    QuantumForceNew = np.zeros((NumberParticles,Dimension), np.double)
    +
    +    energy = 0.0
    +    DeltaE = 0.0
    +    alpha = x0[0]
    +    beta = x0[1]
    +    EnergyDer = 0.0
    +    DeltaPsi = 0.0
    +    DerivativePsiE = 0.0 
    +    #Initial position
    +    for i in range(NumberParticles):
    +        for j in range(Dimension):
    +            PositionOld[i,j] = normalvariate(0.0,1.0)*sqrt(TimeStep)
    +    wfold = WaveFunction(PositionOld,alpha,beta)
    +    QuantumForceOld = QuantumForce(PositionOld,alpha, beta)
    +
    +    #Loop over MC MCcycles
    +    for MCcycle in range(NumberMCcycles):
    +        #Trial position moving one particle at the time
    +        for i in range(NumberParticles):
    +            for j in range(Dimension):
    +                PositionNew[i,j] = PositionOld[i,j]+normalvariate(0.0,1.0)*sqrt(TimeStep)+\
    +                                       QuantumForceOld[i,j]*TimeStep*D
    +            wfnew = WaveFunction(PositionNew,alpha,beta)
    +            QuantumForceNew = QuantumForce(PositionNew,alpha, beta)
    +            GreensFunction = 0.0
    +            for j in range(Dimension):
    +                GreensFunction += 0.5*(QuantumForceOld[i,j]+QuantumForceNew[i,j])*\
    +	                              (D*TimeStep*0.5*(QuantumForceOld[i,j]-QuantumForceNew[i,j])-\
    +                                      PositionNew[i,j]+PositionOld[i,j])
    +      
    +            GreensFunction = exp(GreensFunction)
    +            ProbabilityRatio = GreensFunction*wfnew**2/wfold**2
    +            #Metropolis-Hastings test to see whether we accept the move
    +            if random() <= ProbabilityRatio:
    +                for j in range(Dimension):
    +                    PositionOld[i,j] = PositionNew[i,j]
    +                    QuantumForceOld[i,j] = QuantumForceNew[i,j]
    +                wfold = wfnew
    +        DeltaE = LocalEnergy(PositionOld,alpha,beta)
    +        DerPsi = DerivativeWFansatz(PositionOld,alpha,beta)
    +        DeltaPsi += DerPsi
    +        energy += DeltaE
    +        DerivativePsiE += DerPsi*DeltaE
    +            
    +    # We calculate mean values
    +    energy /= NumberMCcycles
    +    DerivativePsiE /= NumberMCcycles
    +    DeltaPsi /= NumberMCcycles
    +    EnergyDer  = 2*(DerivativePsiE-DeltaPsi*energy)
    +    return EnergyDer
    +
    +
    +# Computing the expectation value of the local energy 
    +def Energy(x0):
    +    # Parameters in the Fokker-Planck simulation of the quantum force
    +    D = 0.5
    +    TimeStep = 0.05
    +    # positions
    +    PositionOld = np.zeros((NumberParticles,Dimension), np.double)
    +    PositionNew = np.zeros((NumberParticles,Dimension), np.double)
    +    # Quantum force
    +    QuantumForceOld = np.zeros((NumberParticles,Dimension), np.double)
    +    QuantumForceNew = np.zeros((NumberParticles,Dimension), np.double)
    +
    +    energy = 0.0
    +    DeltaE = 0.0
    +    alpha = x0[0]
    +    beta = x0[1]
    +    #Initial position
    +    for i in range(NumberParticles):
    +        for j in range(Dimension):
    +            PositionOld[i,j] = normalvariate(0.0,1.0)*sqrt(TimeStep)
    +    wfold = WaveFunction(PositionOld,alpha,beta)
    +    QuantumForceOld = QuantumForce(PositionOld,alpha, beta)
    +
    +    #Loop over MC MCcycles
    +    for MCcycle in range(NumberMCcycles):
    +        #Trial position moving one particle at the time
    +        for i in range(NumberParticles):
    +            for j in range(Dimension):
    +                PositionNew[i,j] = PositionOld[i,j]+normalvariate(0.0,1.0)*sqrt(TimeStep)+\
    +                                       QuantumForceOld[i,j]*TimeStep*D
    +            wfnew = WaveFunction(PositionNew,alpha,beta)
    +            QuantumForceNew = QuantumForce(PositionNew,alpha, beta)
    +            GreensFunction = 0.0
    +            for j in range(Dimension):
    +                GreensFunction += 0.5*(QuantumForceOld[i,j]+QuantumForceNew[i,j])*\
    +	                              (D*TimeStep*0.5*(QuantumForceOld[i,j]-QuantumForceNew[i,j])-\
    +                                      PositionNew[i,j]+PositionOld[i,j])
    +      
    +            GreensFunction = exp(GreensFunction)
    +            ProbabilityRatio = GreensFunction*wfnew**2/wfold**2
    +            #Metropolis-Hastings test to see whether we accept the move
    +            if random() <= ProbabilityRatio:
    +                for j in range(Dimension):
    +                    PositionOld[i,j] = PositionNew[i,j]
    +                    QuantumForceOld[i,j] = QuantumForceNew[i,j]
    +                wfold = wfnew
    +        DeltaE = LocalEnergy(PositionOld,alpha,beta)
    +        energy += DeltaE
    +        if Printout: 
    +           outfile.write('%f\n' %(energy/(MCcycle+1.0)))            
    +    # We calculate mean values
    +    energy /= NumberMCcycles
    +    return energy
    +
    +#Here starts the main program with variable declarations
    +NumberParticles = 2
    +Dimension = 2
    +# seed for rng generator 
    +seed()
    +# Monte Carlo cycles for parameter optimization
    +Printout = False
    +NumberMCcycles= 10000
    +# guess for variational parameters
    +x0 = np.array([0.9,0.2])
    +# Using Broydens method to find optimal parameters
    +res = minimize(Energy, x0, method='BFGS', jac=EnergyDerivative, options={'gtol': 1e-4,'disp': True})
    +x0 = res.x
    +# Compute the energy again with the optimal parameters and increased number of Monte Cycles
    +NumberMCcycles= 2**19
    +Printout = True
    +FinalEnergy = Energy(x0)
    +EResult = np.array([FinalEnergy,FinalEnergy])
    +outfile.close()
    +#nice printout with Pandas
     import pandas as pd
     from pandas import DataFrame
    -data ={'Mean':[mean], 'STDev':[std]}
    -frame = pd.DataFrame(data,index=['Values'])
    +data ={'Optimal Parameters':x0, 'Final Energy':EResult}
    +frame = pd.DataFrame(data)
     print(frame)
     
    @@ -250,6 +901,18 @@

    Resampling analysis

  • 20
  • 21
  • 22
  • +
  • 23
  • +
  • 24
  • +
  • 25
  • +
  • 26
  • +
  • 27
  • +
  • 28
  • +
  • 29
  • +
  • 30
  • +
  • 31
  • +
  • ...
  • +
  • 141
  • +
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs022.html b/doc/pub/week9/html/._week9-bs022.html index bc5be66d..e49fae5f 100644 --- a/doc/pub/week9/html/._week9-bs022.html +++ b/doc/pub/week9/html/._week9-bs022.html @@ -47,6 +47,7 @@ None, 'and-why-do-we-use-such-methods'), ('Central limit theorem', 2, None, 'central-limit-theorem'), + ('Further remarks', 2, None, 'further-remarks'), ('Running many measurements', 2, None, @@ -62,62 +63,404 @@ 2, None, 'introducing-the-correlation-function'), - ('Statistics, wrapping up from last week', + ('Resampling methods: Blocking', 2, None, - 'statistics-wrapping-up-from-last-week'), - ('Statistics, final expression', + 'resampling-methods-blocking'), + ('Why blocking?', 2, None, 'why-blocking'), + ('Blocking Transformations', 2, None, 'blocking-transformations'), + ('Blocking transformations', 2, None, 'blocking-transformations'), + ('Blocking Transformations', 2, None, 'blocking-transformations'), + ('Blocking Transformations, getting there', 2, None, - 'statistics-final-expression'), - ('Statistics, effective number of correlations', + 'blocking-transformations-getting-there'), + ('Blocking Transformations, final expressions', 2, None, - 'statistics-effective-number-of-correlations'), - ('Can we understand this? Time Auto-correlation Function', + 'blocking-transformations-final-expressions'), + ('More on the blocking method', 2, None, - 'can-we-understand-this-time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'more-on-the-blocking-method'), + ('Example code form last week', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'example-code-form-last-week'), + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'optimization-and-profiling'), + ('Optimization and debugging', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', 2, None, - 'time-auto-correlation-function'), - ('Correlation Time', 2, None, 'correlation-time'), - ('Resampling methods: Blocking', + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', 2, None, - 'resampling-methods-blocking'), - ('Blocking Transformations', 2, None, 'blocking-transformations'), - ('Blocking Transformations', 2, None, 'blocking-transformations'), - ('Blocking Transformations, getting there', + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', 2, None, - 'blocking-transformations-getting-there'), - ('Blocking Transformations, final expressions', + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', 2, None, - 'blocking-transformations-final-expressions'), - ('Example code form last week', + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', 2, None, - 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -157,29 +500,141 @@
  • Statistical analysis
  • And why do we use such methods?
  • Central limit theorem
  • -
  • Running many measurements
  • -
  • Adding more definitions
  • -
  • Further rewriting
  • -
  • The covariance term
  • -
  • Rewriting the covariance term
  • -
  • Introducing the correlation function
  • -
  • Statistics, wrapping up from last week
  • -
  • Statistics, final expression
  • -
  • Statistics, effective number of correlations
  • -
  • Can we understand this? Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Correlation Time
  • -
  • Resampling methods: Blocking
  • -
  • Blocking Transformations
  • -
  • Blocking Transformations
  • -
  • Blocking Transformations, getting there
  • -
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • Further remarks
  • +
  • Running many measurements
  • +
  • Adding more definitions
  • +
  • Further rewriting
  • +
  • The covariance term
  • +
  • Rewriting the covariance term
  • +
  • Introducing the correlation function
  • +
  • Resampling methods: Blocking
  • +
  • Why blocking?
  • +
  • Blocking Transformations
  • +
  • Blocking transformations
  • +
  • Blocking Transformations
  • +
  • Blocking Transformations, getting there
  • +
  • Blocking Transformations, final expressions
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -191,28 +646,90 @@

     

     

     

    -

    Resampling methods: Blocking

    +

    Resampling analysis

    -

    The blocking method was made popular by Flyvbjerg and Pedersen (1989) -and has become one of the standard ways to estimate -\( V(\widehat{\theta}) \) for exactly one \( \widehat{\theta} \), namely -\( \widehat{\theta} = \overline{X} \). +

    The next step is then to use the above data sets and perform a +resampling analysis using the blocking method +The blocking code, based on the article of Marius Jonsson is given here

    -

    Assume \( n = 2^d \) for some integer \( d>1 \) and \( X_1,X_2,\cdots, X_n \) is a stationary time series to begin with. -Moreover, assume that the time series is asymptotically uncorrelated. We switch to vector notation by arranging \( X_1,X_2,\cdots,X_n \) in an \( n \)-tuple. Define: -

    -$$ -\begin{align*} -\hat{X} = (X_1,X_2,\cdots,X_n). -\end{align*} -$$ - -

    The strength of the blocking method is when the number of -observations, \( n \) is large. For large \( n \), the complexity of dependent -bootstrapping scales poorly, but the blocking method does not, -moreover, it becomes more accurate the larger \( n \) is. -

    + + +
    +
    +
    +
    +
    +
    # Common imports
    +import os
    +
    +# Where to save the figures and data files
    +DATA_ID = "Results/EnergyMin"
    +
    +def data_path(dat_id):
    +    return os.path.join(DATA_ID, dat_id)
    +
    +infile = open(data_path("Energies.dat"),'r')
    +
    +from numpy import log2, zeros, mean, var, sum, loadtxt, arange, array, cumsum, dot, transpose, diagonal, sqrt
    +from numpy.linalg import inv
    +
    +def block(x):
    +    # preliminaries
    +    n = len(x)
    +    d = int(log2(n))
    +    s, gamma = zeros(d), zeros(d)
    +    mu = mean(x)
    +
    +    # estimate the auto-covariance and variances 
    +    # for each blocking transformation
    +    for i in arange(0,d):
    +        n = len(x)
    +        # estimate autocovariance of x
    +        gamma[i] = (n)**(-1)*sum( (x[0:(n-1)]-mu)*(x[1:n]-mu) )
    +        # estimate variance of x
    +        s[i] = var(x)
    +        # perform blocking transformation
    +        x = 0.5*(x[0::2] + x[1::2])
    +   
    +    # generate the test observator M_k from the theorem
    +    M = (cumsum( ((gamma/s)**2*2**arange(1,d+1)[::-1])[::-1] )  )[::-1]
    +
    +    # we need a list of magic numbers
    +    q =array([6.634897,9.210340, 11.344867, 13.276704, 15.086272, 16.811894, 18.475307, 20.090235, 21.665994, 23.209251, 24.724970, 26.216967, 27.688250, 29.141238, 30.577914, 31.999927, 33.408664, 34.805306, 36.190869, 37.566235, 38.932173, 40.289360, 41.638398, 42.979820, 44.314105, 45.641683, 46.962942, 48.278236, 49.587884, 50.892181])
    +
    +    # use magic to determine when we should have stopped blocking
    +    for k in arange(0,d):
    +        if(M[k] < q[k]):
    +            break
    +    if (k >= d-1):
    +        print("Warning: Use more data")
    +    return mu, s[k]/2**(d-k)
    +
    +
    +x = loadtxt(infile)
    +(mean, var) = block(x) 
    +std = sqrt(var)
    +import pandas as pd
    +from pandas import DataFrame
    +data ={'Mean':[mean], 'STDev':[std]}
    +frame = pd.DataFrame(data,index=['Values'])
    +print(frame)
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    @@ -235,6 +752,11 @@

    Resampling methods: Blocking
  • 27
  • 28
  • 29
  • +
  • 30
  • +
  • 31
  • +
  • 32
  • +
  • ...
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs023.html b/doc/pub/week9/html/._week9-bs023.html index 63b8cb51..3a01dcc7 100644 --- a/doc/pub/week9/html/._week9-bs023.html +++ b/doc/pub/week9/html/._week9-bs023.html @@ -47,6 +47,7 @@ None, 'and-why-do-we-use-such-methods'), ('Central limit theorem', 2, None, 'central-limit-theorem'), + ('Further remarks', 2, None, 'further-remarks'), ('Running many measurements', 2, None, @@ -62,62 +63,404 @@ 2, None, 'introducing-the-correlation-function'), - ('Statistics, wrapping up from last week', + ('Resampling methods: Blocking', 2, None, - 'statistics-wrapping-up-from-last-week'), - ('Statistics, final expression', + 'resampling-methods-blocking'), + ('Why blocking?', 2, None, 'why-blocking'), + ('Blocking Transformations', 2, None, 'blocking-transformations'), + ('Blocking transformations', 2, None, 'blocking-transformations'), + ('Blocking Transformations', 2, None, 'blocking-transformations'), + ('Blocking Transformations, getting there', 2, None, - 'statistics-final-expression'), - ('Statistics, effective number of correlations', + 'blocking-transformations-getting-there'), + ('Blocking Transformations, final expressions', 2, None, - 'statistics-effective-number-of-correlations'), - ('Can we understand this? Time Auto-correlation Function', + 'blocking-transformations-final-expressions'), + ('More on the blocking method', 2, None, - 'can-we-understand-this-time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'more-on-the-blocking-method'), + ('Example code form last week', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'example-code-form-last-week'), + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'optimization-and-profiling'), + ('Optimization and debugging', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', 2, None, - 'time-auto-correlation-function'), - ('Correlation Time', 2, None, 'correlation-time'), - ('Resampling methods: Blocking', + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', 2, None, - 'resampling-methods-blocking'), - ('Blocking Transformations', 2, None, 'blocking-transformations'), - ('Blocking Transformations', 2, None, 'blocking-transformations'), - ('Blocking Transformations, getting there', + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', 2, None, - 'blocking-transformations-getting-there'), - ('Blocking Transformations, final expressions', + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', 2, None, - 'blocking-transformations-final-expressions'), - ('Example code form last week', + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', 2, None, - 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -157,29 +500,141 @@
  • Statistical analysis
  • And why do we use such methods?
  • Central limit theorem
  • -
  • Running many measurements
  • -
  • Adding more definitions
  • -
  • Further rewriting
  • -
  • The covariance term
  • -
  • Rewriting the covariance term
  • -
  • Introducing the correlation function
  • -
  • Statistics, wrapping up from last week
  • -
  • Statistics, final expression
  • -
  • Statistics, effective number of correlations
  • -
  • Can we understand this? Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Correlation Time
  • -
  • Resampling methods: Blocking
  • -
  • Blocking Transformations
  • -
  • Blocking Transformations
  • -
  • Blocking Transformations, getting there
  • -
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • Further remarks
  • +
  • Running many measurements
  • +
  • Adding more definitions
  • +
  • Further rewriting
  • +
  • The covariance term
  • +
  • Rewriting the covariance term
  • +
  • Introducing the correlation function
  • +
  • Resampling methods: Blocking
  • +
  • Why blocking?
  • +
  • Blocking Transformations
  • +
  • Blocking transformations
  • +
  • Blocking Transformations
  • +
  • Blocking Transformations, getting there
  • +
  • Blocking Transformations, final expressions
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -191,41 +646,17 @@

     

     

     

    -

    Blocking Transformations

    -

    We now define -blocking transformations. The idea is to take the mean of subsequent -pair of elements from \( \vec{X} \) and form a new vector -\( \vec{X}_1 \). Continuing in the same way by taking the mean of -subsequent pairs of elements of \( \vec{X}_1 \) we obtain \( \vec{X}_2 \), and -so on. -Define \( \vec{X}_i \) recursively by: -

    - -$$ -\begin{align} -(\vec{X}_0)_k &\equiv (\vec{X})_k \nonumber \\ -(\vec{X}_{i+1})_k &\equiv \frac{1}{2}\Big( (\vec{X}_i)_{2k-1} + -(\vec{X}_i)_{2k} \Big) \qquad \text{for all} \qquad 1 \leq i \leq d-1 -\tag{5} -\end{align} -$$ - -

    The quantity \( \vec{X}_k \) is -subject to \( k \) blocking transformations. We now have \( d \) vectors -\( \vec{X}_0, \vec{X}_1,\cdots,\vec X_{d-1} \) containing the subsequent -averages of observations. It turns out that if the components of -\( \vec{X} \) is a stationary time series, then the components of -\( \vec{X}_i \) is a stationary time series for all \( 0 \leq i \leq d-1 \) -

    - -

    We can then compute the autocovariance, the variance, sample mean, and -number of observations for each \( i \). -Let \( \gamma_i, \sigma_i^2, -\overline{X}_i \) denote the autocovariance, variance and average of the -elements of \( \vec{X}_i \) and let \( n_i \) be the number of elements of -\( \vec{X}_i \). It follows by induction that \( n_i = n/2^i \). -

    - +

    Content

    +
      +
    • Simple compiler options
    • +
    • Tools to benchmark your code
    • +
    • Machine architectures
    • +
    • What is vectorization?
    • +
    • How to measure code performance
    • +
    • Parallelization with OpenMP
    • +
    • Parallelization with MPI
    • +
    • Vectorization and parallelization, examples
    • +

    diff --git a/doc/pub/week9/html/._week9-bs024.html b/doc/pub/week9/html/._week9-bs024.html index 4381840f..2d43eb0d 100644 --- a/doc/pub/week9/html/._week9-bs024.html +++ b/doc/pub/week9/html/._week9-bs024.html @@ -47,6 +47,7 @@ None, 'and-why-do-we-use-such-methods'), ('Central limit theorem', 2, None, 'central-limit-theorem'), + ('Further remarks', 2, None, 'further-remarks'), ('Running many measurements', 2, None, @@ -62,62 +63,404 @@ 2, None, 'introducing-the-correlation-function'), - ('Statistics, wrapping up from last week', + ('Resampling methods: Blocking', 2, None, - 'statistics-wrapping-up-from-last-week'), - ('Statistics, final expression', + 'resampling-methods-blocking'), + ('Why blocking?', 2, None, 'why-blocking'), + ('Blocking Transformations', 2, None, 'blocking-transformations'), + ('Blocking transformations', 2, None, 'blocking-transformations'), + ('Blocking Transformations', 2, None, 'blocking-transformations'), + ('Blocking Transformations, getting there', 2, None, - 'statistics-final-expression'), - ('Statistics, effective number of correlations', + 'blocking-transformations-getting-there'), + ('Blocking Transformations, final expressions', 2, None, - 'statistics-effective-number-of-correlations'), - ('Can we understand this? Time Auto-correlation Function', + 'blocking-transformations-final-expressions'), + ('More on the blocking method', 2, None, - 'can-we-understand-this-time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'more-on-the-blocking-method'), + ('Example code form last week', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'example-code-form-last-week'), + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'optimization-and-profiling'), + ('Optimization and debugging', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', 2, None, - 'time-auto-correlation-function'), - ('Correlation Time', 2, None, 'correlation-time'), - ('Resampling methods: Blocking', + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', 2, None, - 'resampling-methods-blocking'), - ('Blocking Transformations', 2, None, 'blocking-transformations'), - ('Blocking Transformations', 2, None, 'blocking-transformations'), - ('Blocking Transformations, getting there', + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', 2, None, - 'blocking-transformations-getting-there'), - ('Blocking Transformations, final expressions', + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', 2, None, - 'blocking-transformations-final-expressions'), - ('Example code form last week', + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', 2, None, - 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -157,29 +500,141 @@
  • Statistical analysis
  • And why do we use such methods?
  • Central limit theorem
  • -
  • Running many measurements
  • -
  • Adding more definitions
  • -
  • Further rewriting
  • -
  • The covariance term
  • -
  • Rewriting the covariance term
  • -
  • Introducing the correlation function
  • -
  • Statistics, wrapping up from last week
  • -
  • Statistics, final expression
  • -
  • Statistics, effective number of correlations
  • -
  • Can we understand this? Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Correlation Time
  • -
  • Resampling methods: Blocking
  • -
  • Blocking Transformations
  • -
  • Blocking Transformations
  • -
  • Blocking Transformations, getting there
  • -
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • Further remarks
  • +
  • Running many measurements
  • +
  • Adding more definitions
  • +
  • Further rewriting
  • +
  • The covariance term
  • +
  • Rewriting the covariance term
  • +
  • Introducing the correlation function
  • +
  • Resampling methods: Blocking
  • +
  • Why blocking?
  • +
  • Blocking Transformations
  • +
  • Blocking transformations
  • +
  • Blocking Transformations
  • +
  • Blocking Transformations, getting there
  • +
  • Blocking Transformations, final expressions
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -191,25 +646,70 @@

     

     

     

    -

    Blocking Transformations

    +

    Optimization and profiling

    +
    +
    + + +

    Till now we have not paid much attention to speed and possible optimization possibilities +inherent in the various compilers. We have compiled and linked as +

    + + +
    +
    +
    +
    +
    +
    c++  -c  mycode.cpp
    +c++  -o  mycode.exe  mycode.o
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    Using the -definition of the blocking transformation and the distributive -property of the covariance, it is clear that since \( h =|i-j| \) -we can define +

    For Fortran replace with for example gfortran or ifort. +This is what we call a flat compiler option and should be used when we develop the code. +It produces normally a very large and slow code when translated to machine instructions. +We use this option for debugging and for establishing the correct program output because +every operation is done precisely as the user specified it.

    -$$ -\begin{align} -\gamma_{k+1}(h) &= cov\left( ({X}_{k+1})_{i}, ({X}_{k+1})_{j} \right) \nonumber \\ -&= \frac{1}{4}cov\left( ({X}_{k})_{2i-1} + ({X}_{k})_{2i}, ({X}_{k})_{2j-1} + ({X}_{k})_{2j} \right) \nonumber \\ -&= \frac{1}{2}\gamma_{k}(2h) + \frac{1}{2}\gamma_k(2h+1) \hspace{0.1cm} \mathrm{h = 0} -\tag{6}\\ -&=\frac{1}{4}\gamma_k(2h-1) + \frac{1}{2}\gamma_k(2h) + \frac{1}{4}\gamma_k(2h+1) \quad \mathrm{else} -\tag{7} -\end{align} -$$ -

    The quantity \( \hat{X} \) is asymptotic uncorrelated by assumption, \( \hat{X}_k \) is also asymptotic uncorrelated. Let's turn our attention to the variance of the sample mean \( V(\overline{X}) \).

    +

    It is instructive to look up the compiler manual for further instructions by writing

    + + +
    +
    +
    +
    +
    +
    man c++
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    @@ -230,6 +730,13 @@

    Blocking Transformations

  • 27
  • 28
  • 29
  • +
  • 30
  • +
  • 31
  • +
  • 32
  • +
  • 33
  • +
  • 34
  • +
  • ...
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs025.html b/doc/pub/week9/html/._week9-bs025.html index 72e4f7f7..1121a3ae 100644 --- a/doc/pub/week9/html/._week9-bs025.html +++ b/doc/pub/week9/html/._week9-bs025.html @@ -47,6 +47,7 @@ None, 'and-why-do-we-use-such-methods'), ('Central limit theorem', 2, None, 'central-limit-theorem'), + ('Further remarks', 2, None, 'further-remarks'), ('Running many measurements', 2, None, @@ -62,62 +63,404 @@ 2, None, 'introducing-the-correlation-function'), - ('Statistics, wrapping up from last week', + ('Resampling methods: Blocking', 2, None, - 'statistics-wrapping-up-from-last-week'), - ('Statistics, final expression', + 'resampling-methods-blocking'), + ('Why blocking?', 2, None, 'why-blocking'), + ('Blocking Transformations', 2, None, 'blocking-transformations'), + ('Blocking transformations', 2, None, 'blocking-transformations'), + ('Blocking Transformations', 2, None, 'blocking-transformations'), + ('Blocking Transformations, getting there', 2, None, - 'statistics-final-expression'), - ('Statistics, effective number of correlations', + 'blocking-transformations-getting-there'), + ('Blocking Transformations, final expressions', 2, None, - 'statistics-effective-number-of-correlations'), - ('Can we understand this? Time Auto-correlation Function', + 'blocking-transformations-final-expressions'), + ('More on the blocking method', 2, None, - 'can-we-understand-this-time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'more-on-the-blocking-method'), + ('Example code form last week', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'example-code-form-last-week'), + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'optimization-and-profiling'), + ('Optimization and debugging', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', 2, None, - 'time-auto-correlation-function'), - ('Correlation Time', 2, None, 'correlation-time'), - ('Resampling methods: Blocking', + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', 2, None, - 'resampling-methods-blocking'), - ('Blocking Transformations', 2, None, 'blocking-transformations'), - ('Blocking Transformations', 2, None, 'blocking-transformations'), - ('Blocking Transformations, getting there', + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', 2, None, - 'blocking-transformations-getting-there'), - ('Blocking Transformations, final expressions', + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', 2, None, - 'blocking-transformations-final-expressions'), - ('Example code form last week', + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', 2, None, - 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -157,29 +500,141 @@
  • Statistical analysis
  • And why do we use such methods?
  • Central limit theorem
  • -
  • Running many measurements
  • -
  • Adding more definitions
  • -
  • Further rewriting
  • -
  • The covariance term
  • -
  • Rewriting the covariance term
  • -
  • Introducing the correlation function
  • -
  • Statistics, wrapping up from last week
  • -
  • Statistics, final expression
  • -
  • Statistics, effective number of correlations
  • -
  • Can we understand this? Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Correlation Time
  • -
  • Resampling methods: Blocking
  • -
  • Blocking Transformations
  • -
  • Blocking Transformations
  • -
  • Blocking Transformations, getting there
  • -
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • Further remarks
  • +
  • Running many measurements
  • +
  • Adding more definitions
  • +
  • Further rewriting
  • +
  • The covariance term
  • +
  • Rewriting the covariance term
  • +
  • Introducing the correlation function
  • +
  • Resampling methods: Blocking
  • +
  • Why blocking?
  • +
  • Blocking Transformations
  • +
  • Blocking transformations
  • +
  • Blocking Transformations
  • +
  • Blocking Transformations, getting there
  • +
  • Blocking Transformations, final expressions
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -191,24 +646,42 @@

     

     

     

    -

    Blocking Transformations, getting there

    -

    We have

    -$$ -\begin{align} -V(\overline{X}_k) = \frac{\sigma_k^2}{n_k} + \underbrace{\frac{2}{n_k} \sum_{h=1}^{n_k-1}\left( 1 - \frac{h}{n_k} \right)\gamma_k(h)}_{\equiv e_k} = \frac{\sigma^2_k}{n_k} + e_k \quad \text{if} \quad \gamma_k(0) = \sigma_k^2. -\tag{8} -\end{align} -$$ +

    More on optimization

    +
    +
    + +

    We have additional compiler options for optimization. These may include procedure inlining where +performance may be improved, moving constants inside loops outside the loop, +identify potential parallelism, include automatic vectorization or replace a division with a reciprocal +and a multiplication if this speeds up the code. +

    -

    The term \( e_k \) is called the truncation error:

    -$$ -\begin{equation} -e_k = \frac{2}{n_k} \sum_{h=1}^{n_k-1}\left( 1 - \frac{h}{n_k} \right)\gamma_k(h). -\tag{9} -\end{equation} -$$ + +
    +
    +
    +
    +
    +
    c++  -O3 -c  mycode.cpp
    +c++  -O3 -o  mycode.exe  mycode.o
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    We can show that \( V(\overline{X}_i) = V(\overline{X}_j) \) for all \( 0 \leq i \leq d-1 \) and \( 0 \leq j \leq d-1 \).

    +

    This (other options are -O2 or -Ofast) is the recommended option.

    +
    +

    @@ -228,6 +701,14 @@

    Blocking Transfor
  • 27
  • 28
  • 29
  • +
  • 30
  • +
  • 31
  • +
  • 32
  • +
  • 33
  • +
  • 34
  • +
  • 35
  • +
  • ...
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs026.html b/doc/pub/week9/html/._week9-bs026.html index ba2f41f2..bed0c48d 100644 --- a/doc/pub/week9/html/._week9-bs026.html +++ b/doc/pub/week9/html/._week9-bs026.html @@ -47,6 +47,7 @@ None, 'and-why-do-we-use-such-methods'), ('Central limit theorem', 2, None, 'central-limit-theorem'), + ('Further remarks', 2, None, 'further-remarks'), ('Running many measurements', 2, None, @@ -62,62 +63,404 @@ 2, None, 'introducing-the-correlation-function'), - ('Statistics, wrapping up from last week', + ('Resampling methods: Blocking', 2, None, - 'statistics-wrapping-up-from-last-week'), - ('Statistics, final expression', + 'resampling-methods-blocking'), + ('Why blocking?', 2, None, 'why-blocking'), + ('Blocking Transformations', 2, None, 'blocking-transformations'), + ('Blocking transformations', 2, None, 'blocking-transformations'), + ('Blocking Transformations', 2, None, 'blocking-transformations'), + ('Blocking Transformations, getting there', 2, None, - 'statistics-final-expression'), - ('Statistics, effective number of correlations', + 'blocking-transformations-getting-there'), + ('Blocking Transformations, final expressions', 2, None, - 'statistics-effective-number-of-correlations'), - ('Can we understand this? Time Auto-correlation Function', + 'blocking-transformations-final-expressions'), + ('More on the blocking method', 2, None, - 'can-we-understand-this-time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'more-on-the-blocking-method'), + ('Example code form last week', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'example-code-form-last-week'), + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'optimization-and-profiling'), + ('Optimization and debugging', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', 2, None, - 'time-auto-correlation-function'), - ('Correlation Time', 2, None, 'correlation-time'), - ('Resampling methods: Blocking', + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', 2, None, - 'resampling-methods-blocking'), - ('Blocking Transformations', 2, None, 'blocking-transformations'), - ('Blocking Transformations', 2, None, 'blocking-transformations'), - ('Blocking Transformations, getting there', + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', 2, None, - 'blocking-transformations-getting-there'), - ('Blocking Transformations, final expressions', + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', 2, None, - 'blocking-transformations-final-expressions'), - ('Example code form last week', + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', 2, None, - 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -157,29 +500,141 @@
  • Statistical analysis
  • And why do we use such methods?
  • Central limit theorem
  • -
  • Running many measurements
  • -
  • Adding more definitions
  • -
  • Further rewriting
  • -
  • The covariance term
  • -
  • Rewriting the covariance term
  • -
  • Introducing the correlation function
  • -
  • Statistics, wrapping up from last week
  • -
  • Statistics, final expression
  • -
  • Statistics, effective number of correlations
  • -
  • Can we understand this? Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Correlation Time
  • -
  • Resampling methods: Blocking
  • -
  • Blocking Transformations
  • -
  • Blocking Transformations
  • -
  • Blocking Transformations, getting there
  • -
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • Further remarks
  • +
  • Running many measurements
  • +
  • Adding more definitions
  • +
  • Further rewriting
  • +
  • The covariance term
  • +
  • Rewriting the covariance term
  • +
  • Introducing the correlation function
  • +
  • Resampling methods: Blocking
  • +
  • Why blocking?
  • +
  • Blocking Transformations
  • +
  • Blocking transformations
  • +
  • Blocking Transformations
  • +
  • Blocking Transformations, getting there
  • +
  • Blocking Transformations, final expressions
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -191,34 +646,68 @@

     

     

     

    -

    Blocking Transformations, final expressions

    +

    Optimization and profiling

    +
    +
    + +

    It is also useful to profile your program under the development stage. +You would then compile with +

    -

    We can then wrap up

    -$$ -\begin{align} -n_{j+1} \overline{X}_{j+1} &= \sum_{i=1}^{n_{j+1}} (\hat{X}_{j+1})_i = \frac{1}{2}\sum_{i=1}^{n_{j}/2} (\hat{X}_{j})_{2i-1} + (\hat{X}_{j})_{2i} \nonumber \\ -&= \frac{1}{2}\left[ (\hat{X}_j)_1 + (\hat{X}_j)_2 + \cdots + (\hat{X}_j)_{n_j} \right] = \underbrace{\frac{n_j}{2}}_{=n_{j+1}} \overline{X}_j = n_{j+1}\overline{X}_j. -\tag{10} -\end{align} -$$ + +
    +
    +
    +
    +
    +
    c++  -pg -O3 -c  mycode.cpp
    +c++  -pg -O3 -o  mycode.exe  mycode.o
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    After you have run the code you can obtain the profiling information via

    -

    By repeated use of this equation we get \( V(\overline{X}_i) = V(\overline{X}_0) = V(\overline{X}) \) for all \( 0 \leq i \leq d-1 \). This has the consequence that

    -$$ -\begin{align} -V(\overline{X}) = \frac{\sigma_k^2}{n_k} + e_k \qquad \text{for all} \qquad 0 \leq k \leq d-1. \tag{11} -\end{align} -$$ + +
    +
    +
    +
    +
    +
    gprof mycode.exe >  ProfileOutput
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    Flyvbjerg and Petersen demonstrated that the sequence -\( \{e_k\}_{k=0}^{d-1} \) is decreasing, and conjecture that the term -\( e_k \) can be made as small as we would like by making \( k \) (and hence -\( d \)) sufficiently large. The sequence is decreasing (Master of Science thesis by Marius Jonsson, UiO 2018). -It means we can apply blocking transformations until -\( e_k \) is sufficiently small, and then estimate \( V(\overline{X}) \) by -\( \widehat{\sigma}^2_k/n_k \). +

    When you have profiled properly your code, you must take out this option as it +slows down performance. +For memory tests use valgrind. An excellent environment for all these aspects, and much more, is Qt creator.

    +
    +
    -

    For an elegant solution and proof of the blocking method, see the recent article of Marius Jonsson (former MSc student of the Computational Physics group).

    @@ -237,6 +726,15 @@

    Blocking Tran
  • 27
  • 28
  • 29
  • +
  • 30
  • +
  • 31
  • +
  • 32
  • +
  • 33
  • +
  • 34
  • +
  • 35
  • +
  • 36
  • +
  • ...
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs027.html b/doc/pub/week9/html/._week9-bs027.html index 9533d45f..19c3cbe9 100644 --- a/doc/pub/week9/html/._week9-bs027.html +++ b/doc/pub/week9/html/._week9-bs027.html @@ -47,6 +47,7 @@ None, 'and-why-do-we-use-such-methods'), ('Central limit theorem', 2, None, 'central-limit-theorem'), + ('Further remarks', 2, None, 'further-remarks'), ('Running many measurements', 2, None, @@ -62,62 +63,404 @@ 2, None, 'introducing-the-correlation-function'), - ('Statistics, wrapping up from last week', + ('Resampling methods: Blocking', 2, None, - 'statistics-wrapping-up-from-last-week'), - ('Statistics, final expression', + 'resampling-methods-blocking'), + ('Why blocking?', 2, None, 'why-blocking'), + ('Blocking Transformations', 2, None, 'blocking-transformations'), + ('Blocking transformations', 2, None, 'blocking-transformations'), + ('Blocking Transformations', 2, None, 'blocking-transformations'), + ('Blocking Transformations, getting there', 2, None, - 'statistics-final-expression'), - ('Statistics, effective number of correlations', + 'blocking-transformations-getting-there'), + ('Blocking Transformations, final expressions', 2, None, - 'statistics-effective-number-of-correlations'), - ('Can we understand this? Time Auto-correlation Function', + 'blocking-transformations-final-expressions'), + ('More on the blocking method', 2, None, - 'can-we-understand-this-time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'more-on-the-blocking-method'), + ('Example code form last week', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'example-code-form-last-week'), + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'optimization-and-profiling'), + ('Optimization and debugging', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', 2, None, - 'time-auto-correlation-function'), - ('Correlation Time', 2, None, 'correlation-time'), - ('Resampling methods: Blocking', + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', 2, None, - 'resampling-methods-blocking'), - ('Blocking Transformations', 2, None, 'blocking-transformations'), - ('Blocking Transformations', 2, None, 'blocking-transformations'), - ('Blocking Transformations, getting there', + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', 2, None, - 'blocking-transformations-getting-there'), - ('Blocking Transformations, final expressions', + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', 2, None, - 'blocking-transformations-final-expressions'), - ('Example code form last week', + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', 2, None, - 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -157,29 +500,141 @@
  • Statistical analysis
  • And why do we use such methods?
  • Central limit theorem
  • -
  • Running many measurements
  • -
  • Adding more definitions
  • -
  • Further rewriting
  • -
  • The covariance term
  • -
  • Rewriting the covariance term
  • -
  • Introducing the correlation function
  • -
  • Statistics, wrapping up from last week
  • -
  • Statistics, final expression
  • -
  • Statistics, effective number of correlations
  • -
  • Can we understand this? Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Correlation Time
  • -
  • Resampling methods: Blocking
  • -
  • Blocking Transformations
  • -
  • Blocking Transformations
  • -
  • Blocking Transformations, getting there
  • -
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • Further remarks
  • +
  • Running many measurements
  • +
  • Adding more definitions
  • +
  • Further rewriting
  • +
  • The covariance term
  • +
  • Rewriting the covariance term
  • +
  • Introducing the correlation function
  • +
  • Resampling methods: Blocking
  • +
  • Why blocking?
  • +
  • Blocking Transformations
  • +
  • Blocking transformations
  • +
  • Blocking Transformations
  • +
  • Blocking Transformations, getting there
  • +
  • Blocking Transformations, final expressions
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -191,230 +646,22 @@

     

     

     

    -

    Example code form last week

    - - +

    Optimization and debugging

    +
    +
    + +

    Adding debugging options is a very useful alternative under the development stage of a program. +You would then compile with +

    + +
    -
    # 2-electron VMC code for 2dim quantum dot with importance sampling
    -# Using gaussian rng for new positions and Metropolis- Hastings 
    -# Added energy minimization
    -from math import exp, sqrt
    -from random import random, seed, normalvariate
    -import numpy as np
    -import matplotlib.pyplot as plt
    -from mpl_toolkits.mplot3d import Axes3D
    -from matplotlib import cm
    -from matplotlib.ticker import LinearLocator, FormatStrFormatter
    -from scipy.optimize import minimize
    -import sys
    -import os
    -
    -# Where to save data files
    -PROJECT_ROOT_DIR = "Results"
    -DATA_ID = "Results/EnergyMin"
    -
    -if not os.path.exists(PROJECT_ROOT_DIR):
    -    os.mkdir(PROJECT_ROOT_DIR)
    -
    -if not os.path.exists(DATA_ID):
    -    os.makedirs(DATA_ID)
    -
    -def data_path(dat_id):
    -    return os.path.join(DATA_ID, dat_id)
    -
    -outfile = open(data_path("Energies.dat"),'w')
    -
    -
    -# Trial wave function for the 2-electron quantum dot in two dims
    -def WaveFunction(r,alpha,beta):
    -    r1 = r[0,0]**2 + r[0,1]**2
    -    r2 = r[1,0]**2 + r[1,1]**2
    -    r12 = sqrt((r[0,0]-r[1,0])**2 + (r[0,1]-r[1,1])**2)
    -    deno = r12/(1+beta*r12)
    -    return exp(-0.5*alpha*(r1+r2)+deno)
    -
    -# Local energy  for the 2-electron quantum dot in two dims, using analytical local energy
    -def LocalEnergy(r,alpha,beta):
    -    
    -    r1 = (r[0,0]**2 + r[0,1]**2)
    -    r2 = (r[1,0]**2 + r[1,1]**2)
    -    r12 = sqrt((r[0,0]-r[1,0])**2 + (r[0,1]-r[1,1])**2)
    -    deno = 1.0/(1+beta*r12)
    -    deno2 = deno*deno
    -    return 0.5*(1-alpha*alpha)*(r1 + r2) +2.0*alpha + 1.0/r12+deno2*(alpha*r12-deno2+2*beta*deno-1.0/r12)
    -
    -# Derivate of wave function ansatz as function of variational parameters
    -def DerivativeWFansatz(r,alpha,beta):
    -    
    -    WfDer  = np.zeros((2), np.double)
    -    r1 = (r[0,0]**2 + r[0,1]**2)
    -    r2 = (r[1,0]**2 + r[1,1]**2)
    -    r12 = sqrt((r[0,0]-r[1,0])**2 + (r[0,1]-r[1,1])**2)
    -    deno = 1.0/(1+beta*r12)
    -    deno2 = deno*deno
    -    WfDer[0] = -0.5*(r1+r2)
    -    WfDer[1] = -r12*r12*deno2
    -    return  WfDer
    -
    -# Setting up the quantum force for the two-electron quantum dot, recall that it is a vector
    -def QuantumForce(r,alpha,beta):
    -
    -    qforce = np.zeros((NumberParticles,Dimension), np.double)
    -    r12 = sqrt((r[0,0]-r[1,0])**2 + (r[0,1]-r[1,1])**2)
    -    deno = 1.0/(1+beta*r12)
    -    qforce[0,:] = -2*r[0,:]*alpha*(r[0,:]-r[1,:])*deno*deno/r12
    -    qforce[1,:] = -2*r[1,:]*alpha*(r[1,:]-r[0,:])*deno*deno/r12
    -    return qforce
    -    
    -
    -# Computing the derivative of the energy and the energy 
    -def EnergyDerivative(x0):
    -
    -    
    -    # Parameters in the Fokker-Planck simulation of the quantum force
    -    D = 0.5
    -    TimeStep = 0.05
    -    # positions
    -    PositionOld = np.zeros((NumberParticles,Dimension), np.double)
    -    PositionNew = np.zeros((NumberParticles,Dimension), np.double)
    -    # Quantum force
    -    QuantumForceOld = np.zeros((NumberParticles,Dimension), np.double)
    -    QuantumForceNew = np.zeros((NumberParticles,Dimension), np.double)
    -
    -    energy = 0.0
    -    DeltaE = 0.0
    -    alpha = x0[0]
    -    beta = x0[1]
    -    EnergyDer = 0.0
    -    DeltaPsi = 0.0
    -    DerivativePsiE = 0.0 
    -    #Initial position
    -    for i in range(NumberParticles):
    -        for j in range(Dimension):
    -            PositionOld[i,j] = normalvariate(0.0,1.0)*sqrt(TimeStep)
    -    wfold = WaveFunction(PositionOld,alpha,beta)
    -    QuantumForceOld = QuantumForce(PositionOld,alpha, beta)
    -
    -    #Loop over MC MCcycles
    -    for MCcycle in range(NumberMCcycles):
    -        #Trial position moving one particle at the time
    -        for i in range(NumberParticles):
    -            for j in range(Dimension):
    -                PositionNew[i,j] = PositionOld[i,j]+normalvariate(0.0,1.0)*sqrt(TimeStep)+\
    -                                       QuantumForceOld[i,j]*TimeStep*D
    -            wfnew = WaveFunction(PositionNew,alpha,beta)
    -            QuantumForceNew = QuantumForce(PositionNew,alpha, beta)
    -            GreensFunction = 0.0
    -            for j in range(Dimension):
    -                GreensFunction += 0.5*(QuantumForceOld[i,j]+QuantumForceNew[i,j])*\
    -	                              (D*TimeStep*0.5*(QuantumForceOld[i,j]-QuantumForceNew[i,j])-\
    -                                      PositionNew[i,j]+PositionOld[i,j])
    -      
    -            GreensFunction = exp(GreensFunction)
    -            ProbabilityRatio = GreensFunction*wfnew**2/wfold**2
    -            #Metropolis-Hastings test to see whether we accept the move
    -            if random() <= ProbabilityRatio:
    -                for j in range(Dimension):
    -                    PositionOld[i,j] = PositionNew[i,j]
    -                    QuantumForceOld[i,j] = QuantumForceNew[i,j]
    -                wfold = wfnew
    -        DeltaE = LocalEnergy(PositionOld,alpha,beta)
    -        DerPsi = DerivativeWFansatz(PositionOld,alpha,beta)
    -        DeltaPsi += DerPsi
    -        energy += DeltaE
    -        DerivativePsiE += DerPsi*DeltaE
    -            
    -    # We calculate mean values
    -    energy /= NumberMCcycles
    -    DerivativePsiE /= NumberMCcycles
    -    DeltaPsi /= NumberMCcycles
    -    EnergyDer  = 2*(DerivativePsiE-DeltaPsi*energy)
    -    return EnergyDer
    -
    -
    -# Computing the expectation value of the local energy 
    -def Energy(x0):
    -    # Parameters in the Fokker-Planck simulation of the quantum force
    -    D = 0.5
    -    TimeStep = 0.05
    -    # positions
    -    PositionOld = np.zeros((NumberParticles,Dimension), np.double)
    -    PositionNew = np.zeros((NumberParticles,Dimension), np.double)
    -    # Quantum force
    -    QuantumForceOld = np.zeros((NumberParticles,Dimension), np.double)
    -    QuantumForceNew = np.zeros((NumberParticles,Dimension), np.double)
    -
    -    energy = 0.0
    -    DeltaE = 0.0
    -    alpha = x0[0]
    -    beta = x0[1]
    -    #Initial position
    -    for i in range(NumberParticles):
    -        for j in range(Dimension):
    -            PositionOld[i,j] = normalvariate(0.0,1.0)*sqrt(TimeStep)
    -    wfold = WaveFunction(PositionOld,alpha,beta)
    -    QuantumForceOld = QuantumForce(PositionOld,alpha, beta)
    -
    -    #Loop over MC MCcycles
    -    for MCcycle in range(NumberMCcycles):
    -        #Trial position moving one particle at the time
    -        for i in range(NumberParticles):
    -            for j in range(Dimension):
    -                PositionNew[i,j] = PositionOld[i,j]+normalvariate(0.0,1.0)*sqrt(TimeStep)+\
    -                                       QuantumForceOld[i,j]*TimeStep*D
    -            wfnew = WaveFunction(PositionNew,alpha,beta)
    -            QuantumForceNew = QuantumForce(PositionNew,alpha, beta)
    -            GreensFunction = 0.0
    -            for j in range(Dimension):
    -                GreensFunction += 0.5*(QuantumForceOld[i,j]+QuantumForceNew[i,j])*\
    -	                              (D*TimeStep*0.5*(QuantumForceOld[i,j]-QuantumForceNew[i,j])-\
    -                                      PositionNew[i,j]+PositionOld[i,j])
    -      
    -            GreensFunction = exp(GreensFunction)
    -            ProbabilityRatio = GreensFunction*wfnew**2/wfold**2
    -            #Metropolis-Hastings test to see whether we accept the move
    -            if random() <= ProbabilityRatio:
    -                for j in range(Dimension):
    -                    PositionOld[i,j] = PositionNew[i,j]
    -                    QuantumForceOld[i,j] = QuantumForceNew[i,j]
    -                wfold = wfnew
    -        DeltaE = LocalEnergy(PositionOld,alpha,beta)
    -        energy += DeltaE
    -        if Printout: 
    -           outfile.write('%f\n' %(energy/(MCcycle+1.0)))            
    -    # We calculate mean values
    -    energy /= NumberMCcycles
    -    return energy
    -
    -#Here starts the main program with variable declarations
    -NumberParticles = 2
    -Dimension = 2
    -# seed for rng generator 
    -seed()
    -# Monte Carlo cycles for parameter optimization
    -Printout = False
    -NumberMCcycles= 10000
    -# guess for variational parameters
    -x0 = np.array([0.9,0.2])
    -# Using Broydens method to find optimal parameters
    -res = minimize(Energy, x0, method='BFGS', jac=EnergyDerivative, options={'gtol': 1e-4,'disp': True})
    -x0 = res.x
    -# Compute the energy again with the optimal parameters and increased number of Monte Cycles
    -NumberMCcycles= 2**19
    -Printout = True
    -FinalEnergy = Energy(x0)
    -EResult = np.array([FinalEnergy,FinalEnergy])
    -outfile.close()
    -#nice printout with Pandas
    -import pandas as pd
    -from pandas import DataFrame
    -data ={'Optimal Parameters':x0, 'Final Energy':EResult}
    -frame = pd.DataFrame(data)
    -print(frame)
    +  
    c++  -g -O0 -c  mycode.cpp
    +c++  -g -O0 -o  mycode.exe  mycode.o
     
    @@ -430,6 +677,19 @@

    Example code form last week

    +

    This option generates debugging information allowing you to trace for example if an array is properly allocated. Some compilers work best with the no optimization option -O0.

    +
    +
    + +
    +
    + +

    Depending on the compiler, one can add flags which generate code that catches integer overflow errors. +The flag -ftrapv does this for the CLANG compiler on OS X operating systems. +

    +
    +
    +

    @@ -447,6 +707,16 @@

    Example code form last week
  • 27
  • 28
  • 29
  • +
  • 30
  • +
  • 31
  • +
  • 32
  • +
  • 33
  • +
  • 34
  • +
  • 35
  • +
  • 36
  • +
  • 37
  • +
  • ...
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs028.html b/doc/pub/week9/html/._week9-bs028.html index ff6b5365..c6698375 100644 --- a/doc/pub/week9/html/._week9-bs028.html +++ b/doc/pub/week9/html/._week9-bs028.html @@ -47,6 +47,7 @@ None, 'and-why-do-we-use-such-methods'), ('Central limit theorem', 2, None, 'central-limit-theorem'), + ('Further remarks', 2, None, 'further-remarks'), ('Running many measurements', 2, None, @@ -62,62 +63,404 @@ 2, None, 'introducing-the-correlation-function'), - ('Statistics, wrapping up from last week', + ('Resampling methods: Blocking', 2, None, - 'statistics-wrapping-up-from-last-week'), - ('Statistics, final expression', + 'resampling-methods-blocking'), + ('Why blocking?', 2, None, 'why-blocking'), + ('Blocking Transformations', 2, None, 'blocking-transformations'), + ('Blocking transformations', 2, None, 'blocking-transformations'), + ('Blocking Transformations', 2, None, 'blocking-transformations'), + ('Blocking Transformations, getting there', 2, None, - 'statistics-final-expression'), - ('Statistics, effective number of correlations', + 'blocking-transformations-getting-there'), + ('Blocking Transformations, final expressions', 2, None, - 'statistics-effective-number-of-correlations'), - ('Can we understand this? Time Auto-correlation Function', + 'blocking-transformations-final-expressions'), + ('More on the blocking method', 2, None, - 'can-we-understand-this-time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'more-on-the-blocking-method'), + ('Example code form last week', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'example-code-form-last-week'), + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'optimization-and-profiling'), + ('Optimization and debugging', 2, None, - 'time-auto-correlation-function'), - ('Time Auto-correlation Function', + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', 2, None, - 'time-auto-correlation-function'), - ('Correlation Time', 2, None, 'correlation-time'), - ('Resampling methods: Blocking', + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', 2, None, - 'resampling-methods-blocking'), - ('Blocking Transformations', 2, None, 'blocking-transformations'), - ('Blocking Transformations', 2, None, 'blocking-transformations'), - ('Blocking Transformations, getting there', + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', 2, None, - 'blocking-transformations-getting-there'), - ('Blocking Transformations, final expressions', + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', 2, None, - 'blocking-transformations-final-expressions'), - ('Example code form last week', + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', 2, None, - 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -157,29 +500,141 @@
  • Statistical analysis
  • And why do we use such methods?
  • Central limit theorem
  • -
  • Running many measurements
  • -
  • Adding more definitions
  • -
  • Further rewriting
  • -
  • The covariance term
  • -
  • Rewriting the covariance term
  • -
  • Introducing the correlation function
  • -
  • Statistics, wrapping up from last week
  • -
  • Statistics, final expression
  • -
  • Statistics, effective number of correlations
  • -
  • Can we understand this? Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Time Auto-correlation Function
  • -
  • Correlation Time
  • -
  • Resampling methods: Blocking
  • -
  • Blocking Transformations
  • -
  • Blocking Transformations
  • -
  • Blocking Transformations, getting there
  • -
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • Further remarks
  • +
  • Running many measurements
  • +
  • Adding more definitions
  • +
  • Further rewriting
  • +
  • The covariance term
  • +
  • Rewriting the covariance term
  • +
  • Introducing the correlation function
  • +
  • Resampling methods: Blocking
  • +
  • Why blocking?
  • +
  • Blocking Transformations
  • +
  • Blocking transformations
  • +
  • Blocking Transformations
  • +
  • Blocking Transformations, getting there
  • +
  • Blocking Transformations, final expressions
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -191,75 +646,56 @@

     

     

     

    -

    Resampling analysis

    - -

    The next step is then to use the above data sets and perform a -resampling analysis using the blocking method -The blocking code, based on the article of Marius Jonsson is given here -

    - +

    Other hints

    +
    +
    + +

    In general, irrespective of compiler options, it is useful to

    +
      +
    • avoid if tests or call to functions inside loops, if possible.
    • +
    • avoid multiplication with constants inside loops if possible
    • +
    +

    Here is an example of a part of a program where specific operations lead to a slower code

    - +
    -
    # Common imports
    -import os
    -
    -# Where to save the figures and data files
    -DATA_ID = "Results/EnergyMin"
    -
    -def data_path(dat_id):
    -    return os.path.join(DATA_ID, dat_id)
    -
    -infile = open(data_path("Energies.dat"),'r')
    -
    -from numpy import log2, zeros, mean, var, sum, loadtxt, arange, array, cumsum, dot, transpose, diagonal, sqrt
    -from numpy.linalg import inv
    -
    -def block(x):
    -    # preliminaries
    -    n = len(x)
    -    d = int(log2(n))
    -    s, gamma = zeros(d), zeros(d)
    -    mu = mean(x)
    -
    -    # estimate the auto-covariance and variances 
    -    # for each blocking transformation
    -    for i in arange(0,d):
    -        n = len(x)
    -        # estimate autocovariance of x
    -        gamma[i] = (n)**(-1)*sum( (x[0:(n-1)]-mu)*(x[1:n]-mu) )
    -        # estimate variance of x
    -        s[i] = var(x)
    -        # perform blocking transformation
    -        x = 0.5*(x[0::2] + x[1::2])
    -   
    -    # generate the test observator M_k from the theorem
    -    M = (cumsum( ((gamma/s)**2*2**arange(1,d+1)[::-1])[::-1] )  )[::-1]
    -
    -    # we need a list of magic numbers
    -    q =array([6.634897,9.210340, 11.344867, 13.276704, 15.086272, 16.811894, 18.475307, 20.090235, 21.665994, 23.209251, 24.724970, 26.216967, 27.688250, 29.141238, 30.577914, 31.999927, 33.408664, 34.805306, 36.190869, 37.566235, 38.932173, 40.289360, 41.638398, 42.979820, 44.314105, 45.641683, 46.962942, 48.278236, 49.587884, 50.892181])
    -
    -    # use magic to determine when we should have stopped blocking
    -    for k in arange(0,d):
    -        if(M[k] < q[k]):
    -            break
    -    if (k >= d-1):
    -        print("Warning: Use more data")
    -    return mu, s[k]/2**(d-k)
    +  
    k = n-1;
    +for (i = 0; i < n; i++){
    +    a[i] = b[i] +c*d;
    +    e = g[k];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    A better code is

    -x = loadtxt(infile) -(mean, var) = block(x) -std = sqrt(var) -import pandas as pd -from pandas import DataFrame -data ={'Mean':[mean], 'STDev':[std]} -frame = pd.DataFrame(data,index=['Values']) -print(frame) + +
    +
    +
    +
    +
    +
    temp = c*d;
    +for (i = 0; i < n; i++){
    +    a[i] = b[i] + temp;
    +}
    +e = g[n-1];
     
    @@ -275,6 +711,12 @@

    Resampling analysis

    +

    Here we avoid a repeated multiplication inside a loop. +Most compilers, depending on compiler flags, identify and optimize such bottlenecks on their own, without requiring any particular action by the programmer. However, it is always useful to single out and avoid code examples like the first one discussed here. +

    +
    +
    +

    @@ -291,6 +733,18 @@

    Resampling analysis

  • 27
  • 28
  • 29
  • +
  • 30
  • +
  • 31
  • +
  • 32
  • +
  • 33
  • +
  • 34
  • +
  • 35
  • +
  • 36
  • +
  • 37
  • +
  • 38
  • +
  • ...
  • +
  • 141
  • +
  • »
  • diff --git a/doc/pub/week9/html/._week9-bs029.html b/doc/pub/week9/html/._week9-bs029.html index a19a51b9..c7c38c51 100644 --- a/doc/pub/week9/html/._week9-bs029.html +++ b/doc/pub/week9/html/._week9-bs029.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - +

    Vectorization and the basic idea behind parallel computing

    +
    +
    + +

    Present CPUs are highly parallel processors with varying levels of parallelism. The typical situation can be described via the following three statements.

    +
      +
    • Pursuit of shorter computation time and larger simulation size gives rise to parallel computing.
    • +
    • Multiple processors are involved to solve a global problem.
    • +
    • The essence is to divide the entire computation evenly among collaborative processors. Divide and conquer.
    • +
    +

    Before we proceed with a more detailed discussion of topics like vectorization and parallelization, we need to remind ourselves about some basic features of different hardware models.

    +
    +
    -

    Automatic vectorization and vectorization inhibitors, memory stride

    - -

    -For C++ programmers it is also worth keeping in mind that an array notation is preferred to the more compact use of pointers to access array elements. The compiler can often not tell if it is safe to vectorize the code. - -

    -When dealing with arrays, you should also avoid memory stride, since this slows down considerably vectorization. When you access array element, write for example the inner loop to vectorize using unit stride, that is, access successively the next array element in memory, as shown here -

    - -

      for (int i = 0; i < n; i++) {
    -      for (int j = 0; j < n; j++) {
    -           a[i][j] += b[i][j];
    -      }  
    -  }
    -
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs030.html b/doc/pub/week9/html/._week9-bs030.html index 888eeec7..02a3522a 100644 --- a/doc/pub/week9/html/._week9-bs030.html +++ b/doc/pub/week9/html/._week9-bs030.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - -

    Memory management

    -The main memory contains the program data - -
      -
    1. Cache memory contains a copy of the main memory data
    2. -
    3. Cache is faster but consumes more space and power. It is normally assumed to be much faster than main memory
    4. -
    5. Registers contain working data only
    6. - -
        -
      • Modern CPUs perform most or all operations only on data in register
      • -
      - -
    7. Multiple Cache memories contain a copy of the main memory data
    8. +

      A rough classification of hardware models

      +
      +
      +
        -
      • Cache items accessed by their address in main memory
      • -
      • L1 cache is the fastest but has the least capacity
      • -
      • L2, L3 provide intermediate performance/size tradeoffs
      • +
      • Conventional single-processor computers are named SISD (single-instruction-single-data) machines.
      • +
      • SIMD (single-instruction-multiple-data) machines incorporate the idea of parallel processing, using a large number of processing units to execute the same instruction on different data.
      • +
      • Modern parallel computers are so-called MIMD (multiple-instruction-multiple-data) machines and can execute different instruction streams in parallel on different data.
      +
      +
      -
    - -Loads and stores to memory can be as important as floating point operations when we measure performance. - -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs031.html b/doc/pub/week9/html/._week9-bs031.html index d97cafb4..00d8962c 100644 --- a/doc/pub/week9/html/._week9-bs031.html +++ b/doc/pub/week9/html/._week9-bs031.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - -

    Memory and communication

    - -
      -
    1. Most communication in a computer is carried out in chunks, blocks of bytes of data that move together
    2. -
    3. In the memory hierarchy, data moves between memory and cache, and between different levels of cache, in groups called lines
    4. - +

      Shared memory and distributed memory

      +
      +
      + +

      One way of categorizing modern parallel computers is to look at the memory configuration.

        -
      • Lines are typically 64-128 bytes, or 8-16 double precision words
      • -
      • Even if you do not use the data, it is moved and occupies space in the cache
      • +
      • In shared memory systems the CPUs share the same address space. Any CPU can access any data in the global memory.
      • +
      • In distributed memory systems each CPU has its own memory.
      +

      The CPUs are connected by some network and may exchange messages.

      +
      +
      -
    - -Many of these performance features are not captured in most programming languages. -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs032.html b/doc/pub/week9/html/._week9-bs032.html index 135aa4d9..9f51a05f 100644 --- a/doc/pub/week9/html/._week9-bs032.html +++ b/doc/pub/week9/html/._week9-bs032.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - +

    Different parallel programming paradigms

    +
    +
    + -

    Measuring performance

    - -

    -How do we measure performance? What is wrong with this code to time a loop? -

    +

      +
    • Task parallelism: the work of a global problem can be divided into a number of independent tasks, which rarely need to synchronize. Monte Carlo simulations represent a typical situation. Integration is another. However this paradigm is of limited use.
    • +
    • Data parallelism: use of multiple threads (e.g. one or more threads per processor) to dissect loops over arrays etc. Communication and synchronization between processors are often hidden, thus easy to program. However, the user surrenders much control to a specialized compiler. Examples of data parallelism are compiler-based parallelization and OpenMP directives.
    • +
    +
    +
    - -
      clock_t start, finish;
    -  start = clock();
    -  for (int j = 0; j < i; j++) {
    -    a[j] = b[j]+b[j]*c[j];
    -  }
    -  finish = clock();
    -  double timeused = (double) (finish - start)/(CLOCKS_PER_SEC );
    -
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs033.html b/doc/pub/week9/html/._week9-bs033.html index 9798d735..2e6b278d 100644 --- a/doc/pub/week9/html/._week9-bs033.html +++ b/doc/pub/week9/html/._week9-bs033.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - +

    Different parallel programming paradigms

    +
    +
    + -

    Problems with measuring time

    +
      +
    • Message passing: all involved processors have an independent memory address space. The user is responsible for partitioning the data/work of a global problem and distributing the subproblems to the processors. Collaboration between processors is achieved by explicit message passing, which is used for data transfer plus synchronization.
    • +
    • This paradigm is the most general one where the user has full control. Better parallel efficiency is usually achieved by explicit message passing. However, message-passing programming is more difficult.
    • +
    +
    +
    -
      -
    1. Timers are not infinitely accurate
    2. -
    3. All clocks have a granularity, the minimum time that they can measure
    4. -
    5. The error in a time measurement, even if everything is perfect, may be the size of this granularity (sometimes called a clock tick)
    6. -
    7. Always know what your clock granularity is
    8. -
    9. Ensure that your measurement is for a long enough duration (say 100 times the tick)
    10. -

    @@ -624,29 +684,22 @@

    Problems with measuring tim
  • 42
  • 43
  • ...
  • -
  • 120
  • +
  • 141
  • »
  • -

    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs034.html b/doc/pub/week9/html/._week9-bs034.html index 96280c9b..a5e43a70 100644 --- a/doc/pub/week9/html/._week9-bs034.html +++ b/doc/pub/week9/html/._week9-bs034.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - + +

    What is vectorization?

    +

    Vectorization is a special +case of Single Instructions Multiple Data (SIMD) to denote a single +instruction stream capable of operating on multiple data elements in +parallel. +We can think of vectorization as the unrolling of loops accompanied with SIMD instructions. +

    -

    Problems with cold start

    +

    Vectorization is the process of converting an algorithm that performs scalar operations +(typically one operation at the time) to vector operations where a single operation can refer to many simultaneous operations. +Consider the following example +

    -

    -What happens when the code is executed? The assumption is that the code is ready to -execute. But + +

    +
    +
    +
    +
    +
    for (i = 0; i < n; i++){
    +    a[i] = b[i] + c[i];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -
      -
    1. Code may still be on disk, and not even read into memory.
    2. -
    3. Data may be in slow memory rather than fast (which may be wrong or right for what you are measuring)
    4. -
    5. Multiple tests often necessary to ensure that cold start effects are not present
    6. -
    7. Special effort often required to ensure data in the intended part of the memory hierarchy.
    8. -
    +

    If the code is not vectorized, the compiler will simply start with the first element and +then perform subsequent additions operating on one address in memory at the time. +

    @@ -627,29 +712,22 @@

    Problems with cold start

  • 43
  • 44
  • ...
  • -
  • 120
  • +
  • 141
  • »
  • -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs035.html b/doc/pub/week9/html/._week9-bs035.html index 6749fc37..4b8dbfca 100644 --- a/doc/pub/week9/html/._week9-bs035.html +++ b/doc/pub/week9/html/._week9-bs035.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - - -

    Problems with smart compilers

    + +

    Number of elements that can acted upon

    +

    A SIMD instruction can operate on multiple data elements in one single instruction. +It uses the so-called 128-bit SIMD floating-point register. +In this sense, vectorization adds some form of parallelism since one instruction is applied +to many parts of say a vector. +

    -
      -
    1. If the result of the computation is not used, the compiler may eliminate the code
    2. -
    3. Performance will look impossibly fantastic
    4. -
    5. Even worse, eliminate some of the code so the performance looks plausible
    6. -
    7. Ensure that the results are (or may be) used.
    8. -
    +

    The number of elements which can be operated on in parallel +range from four single-precision floating point data elements in so-called +Streaming SIMD Extensions and two double-precision floating-point data +elements in Streaming SIMD Extensions 2 to sixteen byte operations in +a 128-bit register in Streaming SIMD Extensions 2. Thus, vector-length +ranges from 2 to 16, depending on the instruction extensions used and +on the data type. +

    +

    IN summary, our instructions operate on 128 bit (16 byte) operands

    +

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs036.html b/doc/pub/week9/html/._week9-bs036.html index 71406233..66e077b3 100644 --- a/doc/pub/week9/html/._week9-bs036.html +++ b/doc/pub/week9/html/._week9-bs036.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - - -

    Problems with interference

    - -
      -
    1. Other activities are sharing your processor
    2. - -
        -
      • Operating system, system demons, other users
      • -
      • Some parts of the hardware do not always perform with exactly the same performance
      • -
      - -
    3. Make multiple tests and report
    4. -
    5. Easy choices include
    6. + +

      Number of elements that can acted upon, examples

      +

      We start with the simple scalar operations given by

      + + +
      +
      +
      +
      +
      +
      for (i = 0; i < n; i++){
      +    a[i] = b[i] + c[i];
      +}
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      -
        -
      • Average tests represent what users might observe over time
      • -
      +

      If the code is not vectorized and we have a 128-bit register to store a 32 bits floating point number, +it means that we have \( 3\times 32 \) bits that are not used. +

      -
    +

    We have thus unused space in our SIMD registers. These registers could hold three additional integers.

    @@ -633,29 +704,22 @@

    Problems with interference 45
  • 46
  • ...
  • -
  • 120
  • +
  • 141
  • »
  • -

    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs037.html b/doc/pub/week9/html/._week9-bs037.html index fe6f5e53..a3465561 100644 --- a/doc/pub/week9/html/._week9-bs037.html +++ b/doc/pub/week9/html/._week9-bs037.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - + +

    Operation counts for scalar operation

    +

    The code

    -

    Problems with measuring performance

    + +
    +
    +
    +
    +
    +
    for (i = 0; i < n; i++){
    +    a[i] = b[i] + c[i];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    has for \( n \) repeats

      -
    1. Accurate, reproducible performance measurement is hard
    2. -
    3. Think carefully about your experiment:
    4. -
    5. What is it, precisely, that you want to measure?
    6. -
    7. How representative is your test to the situation that you are trying to measure?
    8. +
    9. one load for \( c[i] \) in address 1
    10. +
    11. one load for \( b[i] \) in address 2
    12. +
    13. add \( c[i] \) and \( b[i] \) to give \( a[i] \)
    14. +
    15. store \( a[i] \) in address 2
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs038.html b/doc/pub/week9/html/._week9-bs038.html index e1606a25..91d5dede 100644 --- a/doc/pub/week9/html/._week9-bs038.html +++ b/doc/pub/week9/html/._week9-bs038.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - + +

    Number of elements that can acted upon, examples

    +

    If we vectorize the code, we can perform, with a 128-bit register four simultaneous operations, that is +we have +

    -

    Thomas algorithm for tridiagonal linear algebra equations

    -
    -
    -

    -$$ -\left( \begin{array}{ccccc} - b_0 & c_0 & & & \\ - a_0 & b_1 & c_1 & & \\ - & & \ddots & & \\ - & & a_{m-3} & b_{m-2} & c_{m-2} \\ - & & & a_{m-2} & b_{m-1} - \end{array} \right) -\left( \begin{array}{c} - x_0 \\ - x_1 \\ - \vdots \\ - x_{m-2} \\ - x_{m-1} - \end{array} \right)=\left( \begin{array}{c} - f_0 \\ - f_1 \\ - \vdots \\ - f_{m-2} \\ - f_{m-1} \\ - \end{array} \right) -$$ + +

    +
    +
    +
    +
    +
    for (i = 0; i < n; i+=4){
    +    a[i] = b[i] + c[i];
    +    a[i+1] = b[i+1] + c[i+1];
    +    a[i+2] = b[i+2] + c[i+2];
    +    a[i+3] = b[i+3] + c[i+3];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Four additions are now done in a single step.

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs039.html b/doc/pub/week9/html/._week9-bs039.html index 9a3b4d79..15837176 100644 --- a/doc/pub/week9/html/._week9-bs039.html +++ b/doc/pub/week9/html/._week9-bs039.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - - -

    Thomas algorithm, forward substitution

    -
    -
    -

    -The first step is to multiply the first row by \( a_0/b_0 \) and subtract it from the second row. This is known as the forward substitution step. We obtain then -$$ - a_i = 0, -$$ - - -$$ - b_i = b_i - \frac{a_{i-1}}{b_{i-1}}c_{i-1}, -$$ - -and -$$ - f_i = f_i - \frac{a_{i-1}}{b_{i-1}}f_{i-1}. -$$ - -At this point the simplified equation, with only an upper triangular matrix takes the form -$$ -\left( \begin{array}{ccccc} - b_0 & c_0 & & & \\ - & b_1 & c_1 & & \\ - & & \ddots & & \\ - & & & b_{m-2} & c_{m-2} \\ - & & & & b_{m-1} - \end{array} \right)\left( \begin{array}{c} - x_0 \\ - x_1 \\ - \vdots \\ - x_{m-2} \\ - x_{m-1} - \end{array} \right)=\left( \begin{array}{c} - f_0 \\ - f_1 \\ - \vdots \\ - f_{m-2} \\ - f_{m-1} \\ - \end{array} \right) -$$ -

    -
    - - -

    + +

    Number of operations when vectorized

    +

    For \( n/4 \) repeats assuming floats or integers

    +
      +
    1. one vector load for \( c[i] \) in address 1
    2. +
    3. one load for \( b[i] \) in address 2
    4. +
    5. add \( c[i] \) and \( b[i] \) to give \( a[i] \)
    6. +
    7. store \( a[i] \) in address 2
    8. +

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs040.html b/doc/pub/week9/html/._week9-bs040.html index 6686edb6..f32f8c13 100644 --- a/doc/pub/week9/html/._week9-bs040.html +++ b/doc/pub/week9/html/._week9-bs040.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - -

    Thomas algorithm, backward substitution

    -
    -
    -

    -The next step is the backward substitution step. The last row is multiplied by \( c_{N-3}/b_{N-2} \) and subtracted from the second to last row, thus eliminating \( c_{N-3} \) from the last row. The general backward substitution procedure is -$$ - c_i = 0, -$$ - -and -$$ - f_{i-1} = f_{i-1} - \frac{c_{i-1}}{b_i}f_i -$$ - -All that ramains to be computed is the solution, which is the very straight forward process of -$$ -x_i = \frac{f_i}{b_i} -$$ +

    A simple test case with and without vectorization

    +

    We implement these operations in a simple c++ program that computes at the end the norm of a vector.

    + + + +
    +
    +
    +
    +
    +
    #include <cstdlib>
    +#include <iostream>
    +#include <cmath>
    +#include <iomanip>
    +#include "time.h"
    +
    +using namespace std; // note use of namespace
    +int main (int argc, char* argv[])
    +{
    +  // read in dimension of square matrix
    +  int n = atoi(argv[1]);
    +  double s = 1.0/sqrt( (double) n);
    +  double *a, *b, *c;
    +  // Start timing
    +  clock_t start, finish;
    +  start = clock();
    +// Allocate space for the vectors to be used
    +    a = new double [n]; b = new double [n]; c = new double [n];
    +  // Define parallel region
    +  // Set up values for vectors  a and b
    +  for (int i = 0; i < n; i++){
    +    double angle = 2.0*M_PI*i/ (( double ) n);
    +    a[i] = s*(sin(angle) + cos(angle));
    +    b[i] =  s*sin(2.0*angle);
    +    c[i] = 0.0;
    +  }
    +  // Then perform the vector addition
    +  for (int i = 0; i < n; i++){
    +    c[i] += a[i]+b[i];
    +  }
    +  // Compute now the norm-2
    +  double Norm2 = 0.0;
    +  for (int i = 0; i < n; i++){
    +    Norm2  += c[i]*c[i];
    +  }
    +  finish = clock();
    +  double timeused = (double) (finish - start)/(CLOCKS_PER_SEC );
    +  cout << setiosflags(ios::showpoint | ios::uppercase);
    +  cout << setprecision(10) << setw(20) << "Time used  for norm computation=" << timeused  << endl;
    +  cout << "  Norm-2  = " << Norm2 << endl;
    +  // Free up space
    +  delete[] a;
    +  delete[] b;
    +  delete[] c;
    +  return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs041.html b/doc/pub/week9/html/._week9-bs041.html index d3483569..d2c79367 100644 --- a/doc/pub/week9/html/._week9-bs041.html +++ b/doc/pub/week9/html/._week9-bs041.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - + +

    Compiling with and without vectorization

    +

    We can compile and link without vectorization using the clang c++ compiler

    -

    Thomas algorithm and counting of operations (floating point and memory)

    -
    -
    -

    + +

    +
    +
    +
    +
    +
    clang -o novec.x vecexample.cpp
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    +

    and with vectorization (and additional optimizations)

    -
    -
    - - - - - - - - - - - -
    Operation Floating Point
    Memory Reads \( 14(N-2) \)
    Memory Writes \( 4(N-2) \)
    Subtractions \( 3(N-2) \)
    Multiplications \( 3(N-2) \)
    Divisions \( 4(N-2) \)
    -
    -
    -

    + +

    +
    +
    +
    +
    +
    clang++ -O3 -Rpass=loop-vectorize -o  vec.x vecexample.cpp 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    The speedup depends on the size of the vectors. In the example here we have run with \( 10^7 \) elements. +The example here was run on an IMac17.1 with OSX El Capitan (10.11.4) as operating system and an Intel i5 3.3 GHz CPU. +

    -

    -

    -
    -

    -

    + +

    +
    +
    +
    +
    +
    Compphys:~ hjensen$ ./vec.x 10000000
    +Time used  for norm computation=0.04720500000
    +Compphys:~ hjensen$ ./novec.x 10000000
    +Time used  for norm computation=0.03311700000
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    This particular C++ compiler speeds up the above loop operations with a factor of 1.5 +Performing the same operations for \( 10^9 \) elements results in a smaller speedup since reading from main memory is required. The non-vectorized code is seemingly faster. +

    -
    // Forward substitution    
    -// Note that we can simplify by precalculating a[i-1]/b[i-1]
    -  for (int i=1; i < n; i++) {
    -     b[i] = b[i] - (a[i-1]*c[i-1])/b[i-1];
    -     f[i] = g[i] - (a[i-1]*f[i-1])/b[i-1];
    -  }
    -  x[n-1] = f[n-1] / b[n-1];
    -  // Backwards substitution                                                           
    -  for (int i = n-2; i >= 0; i--) {
    -     f[i] = f[i] - c[i]*f[i+1]/b[i+1];
    -     x[i] = f[i]/b[i];
    -  }
    -
    -

    +

    +
    +
    +
    +
    +
    Compphys:~ hjensen$ ./vec.x 1000000000
    +Time used  for norm computation=58.41391100
    +Compphys:~ hjensen$ ./novec.x 1000000000
    +Time used  for norm computation=46.51295300
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    We will discuss these issues further in the next slides.

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs042.html b/doc/pub/week9/html/._week9-bs042.html index 37a2188a..1d38757d 100644 --- a/doc/pub/week9/html/._week9-bs042.html +++ b/doc/pub/week9/html/._week9-bs042.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - + +

    Compiling with and without vectorization using clang

    +

    We can compile and link without vectorization with clang compiler

    + + +
    +
    +
    +
    +
    +
    clang++ -o -fno-vectorize novec.x vecexample.cpp
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    Example: Transpose of a matrix

    +

    and with vectorization

    -

    + +

    +
    +
    +
    +
    +
    clang++ -O3 -Rpass=loop-vectorize -o  vec.x vecexample.cpp 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    - -
    #include <cstdlib>
    -#include <iostream>
    -#include <cmath>
    -#include <iomanip>
    -#include "time.h"
    +

    We can also add vectorization analysis, see for example

    -using namespace std; // note use of namespace -int main (int argc, char* argv[]) -{ - // read in dimension of square matrix - int n = atoi(argv[1]); - double **A, **B; - // Allocate space for the two matrices - A = new double*[n]; B = new double*[n]; - for (int i = 0; i < n; i++){ - A[i] = new double[n]; - B[i] = new double[n]; - } - // Set up values for matrix A - for (int i = 0; i < n; i++){ - for (int j = 0; j < n; j++) { - A[i][j] = cos(i*1.0)*sin(j*3.0); - } - } - clock_t start, finish; - start = clock(); - // Then compute the transpose - for (int i = 0; i < n; i++){ - for (int j = 0; j < n; j++) { - B[i][j]= A[j][i]; - } - } + +
    +
    +
    +
    +
    +
    clang++ -O3 -Rpass-analysis=loop-vectorize -o  vec.x vecexample.cpp 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    or figure out if vectorization was missed

    + + +
    +
    +
    +
    +
    +
    clang++ -O3 -Rpass-missed=loop-vectorize -o  vec.x vecexample.cpp 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    - finish = clock(); - double timeused = (double) (finish - start)/(CLOCKS_PER_SEC ); - cout << setiosflags(ios::showpoint | ios::uppercase); - cout << setprecision(10) << setw(20) << "Time used for setting up transpose of matrix=" << timeused << endl; - // Free up space - for (int i = 0; i < n; i++){ - delete[] A[i]; - delete[] B[i]; - } - delete[] A; - delete[] B; - return 0; -} -
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs043.html b/doc/pub/week9/html/._week9-bs043.html index 44eaa3c8..a8f6a19d 100644 --- a/doc/pub/week9/html/._week9-bs043.html +++ b/doc/pub/week9/html/._week9-bs043.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - +

    Automatic vectorization and vectorization inhibitors, criteria

    -

    Matrix-matrix multiplication

    -This the matrix-matrix multiplication code with plain c++ memory allocation. It computes at the end the Frobenius norm. +

    Not all loops can be vectorized, as discussed in Intel's guide to vectorization

    -

    +

    An important criteria is that the loop counter \( n \) is known at the entry of the loop.

    - -
    #include <cstdlib>
    -#include <iostream>
    -#include <cmath>
    -#include <iomanip>
    -#include "time.h"
    +
    +
    +
    +
    +
    +
    +
      for (int j = 0; j < n; j++) {
    +    a[j] = cos(j*1.0);
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The variable \( n \) does need to be known at compile time. However, this variable must stay the same for the entire duration of the loop. It implies that an exit statement inside the loop cannot be data dependent.

    -using namespace std; // note use of namespace -int main (int argc, char* argv[]) -{ - // read in dimension of square matrix - int n = atoi(argv[1]); - double s = 1.0/sqrt( (double) n); - double **A, **B, **C; - // Start timing - clock_t start, finish; - start = clock(); - // Allocate space for the two matrices - A = new double*[n]; B = new double*[n]; C = new double*[n]; - for (int i = 0; i < n; i++){ - A[i] = new double[n]; - B[i] = new double[n]; - C[i] = new double[n]; - } - // Set up values for matrix A and B and zero matrix C - for (int i = 0; i < n; i++){ - for (int j = 0; j < n; j++) { - double angle = 2.0*M_PI*i*j/ (( double ) n); - A[i][j] = s * ( sin ( angle ) + cos ( angle ) ); - B[j][i] = A[i][j]; - } - } - // Then perform the matrix-matrix multiplication - for (int i = 0; i < n; i++){ - for (int j = 0; j < n; j++) { - double sum = 0.0; - for (int k = 0; k < n; k++) { - sum += B[i][k]*A[k][j]; - } - C[i][j] = sum; - } - } - // Compute now the Frobenius norm - double Fsum = 0.0; - for (int i = 0; i < n; i++){ - for (int j = 0; j < n; j++) { - Fsum += C[i][j]*C[i][j]; - } - } - Fsum = sqrt(Fsum); - finish = clock(); - double timeused = (double) (finish - start)/(CLOCKS_PER_SEC ); - cout << setiosflags(ios::showpoint | ios::uppercase); - cout << setprecision(10) << setw(20) << "Time used for matrix-matrix multiplication=" << timeused << endl; - cout << " Frobenius norm = " << Fsum << endl; - // Free up space - for (int i = 0; i < n; i++){ - delete[] A[i]; - delete[] B[i]; - delete[] C[i]; - } - delete[] A; - delete[] B; - delete[] C; - return 0; -} -
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs044.html b/doc/pub/week9/html/._week9-bs044.html index ad170bd0..a009250c 100644 --- a/doc/pub/week9/html/._week9-bs044.html +++ b/doc/pub/week9/html/._week9-bs044.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - +

    Automatic vectorization and vectorization inhibitors, exit criteria

    -

    How do we define speedup? Simplest form

    -
    -
    -

    +

    An exit statement should in general be avoided. +If the exit statement contains data-dependent conditions, the loop cannot be vectorized. +The following is an example of a non-vectorizable loop +

    -
      -
    • Speedup measures the ratio of performance between two objects
    • -
    • Versions of same code, with different number of processors
    • -
    • Serial and vector versions
    • -
    • Try different programing languages, c++ and Fortran
    • -
    • Two algorithms computing the same result
    • -
    + +
    +
    +
    +
    +
    +
      for (int j = 0; j < n; j++) {
    +    a[j] = cos(j*1.0);
    +    if (a[j] < 0 ) break;
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Avoid loop termination conditions and opt for a single entry loop variable \( n \). The lower and upper bounds have to be kept fixed within the loop.

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs045.html b/doc/pub/week9/html/._week9-bs045.html index 848a2843..208b044e 100644 --- a/doc/pub/week9/html/._week9-bs045.html +++ b/doc/pub/week9/html/._week9-bs045.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - -

    How do we define speedup? Correct baseline

    -
    -
    -

    -The key is choosing the correct baseline for comparison - -

      -
    • For our serial vs. vectorization examples, using compiler-provided vectorization, the baseline is simple; the same code, with vectorization turned off
    • - -
        -
      • For parallel applications, this is much harder:
      • - -
          -
        • Choice of algorithm, decomposition, performance of baseline case etc.
        • -
        - -
      - -
    +

    Automatic vectorization and vectorization inhibitors, straight-line code

    + +

    SIMD instructions perform the same type of operations multiple times. +A switch statement leads thus to a non-vectorizable loop since different statemens cannot branch. +The following code can however be vectorized since the if statement is implemented as a masked assignment. +

    + + +
    +
    +
    +
    +
    +
      for (int j = 0; j < n; j++) {
    +    double x  = cos(j*1.0);
    +    if (x > 0 ) {
    +       a[j] =  x*sin(j*2.0); 
    +    }
    +    else {
    +       a[j] = 0.0;
    +    }
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    These operations can be performed for all data elements but only those elements which the mask evaluates as true are stored. In general, one should avoid branches such as switch, go to, or return statements or if constructs that cannot be treated as masked assignments.

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs046.html b/doc/pub/week9/html/._week9-bs046.html index b3244f9e..69aac9c7 100644 --- a/doc/pub/week9/html/._week9-bs046.html +++ b/doc/pub/week9/html/._week9-bs046.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - -

    Parallel speedup

    -
    -
    -

    -For parallel applications, speedup is typically defined as - -

      -
    • Speedup \( =T_1/T_p \)
    • -
    - -Here \( T_1 \) is the time on one processor and \( T_p \) is the time using \( p \) processors. - -
      -
    • Can the speedup become larger than \( p \)? That means using \( p \) processors is more than \( p \) times faster than using one processor.
    • -
    +

    Automatic vectorization and vectorization inhibitors, nested loops

    + +

    Only the innermost loop of the following example is vectorized

    + + +
    +
    +
    +
    +
    +
      for (int i = 0; i < n; i++) {
    +      for (int j = 0; j < n; j++) {
    +           a[i][j] += b[i][j];
    +      }  
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    The exception is if an original outer loop is transformed into an inner loop as the result of compiler optimizations.

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs047.html b/doc/pub/week9/html/._week9-bs047.html index 9272d7e6..cfb30d7b 100644 --- a/doc/pub/week9/html/._week9-bs047.html +++ b/doc/pub/week9/html/._week9-bs047.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - +

    Automatic vectorization and vectorization inhibitors, function calls

    -

    Speedup and memory

    -
    -
    -

    -The speedup on \( p \) processors can -be greater than \( p \) if memory usage is optimal! -Consider the case of a memorybound computation with \( M \) words of memory +

    Calls to programmer defined functions ruin vectorization. However, calls to intrinsic functions like +\( \sin{x} \), \( \cos{x} \), \( \exp{x} \) etc are allowed since they are normally efficiently vectorized. +The following example is fully vectorizable +

    -
      -
    • If \( M/p \) fits into cache while \( M \) does not, the time to access memory will be different in the two cases:
    • -
    • \( T_1 \) uses the main memory bandwidth
    • -
    • \( T_p \) uses the appropriate cache bandwidth
    • -
    + +
    +
    +
    +
    +
    +
      for (int i = 0; i < n; i++) {
    +      a[i] = log10(i)*cos(i);
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Similarly, inline functions defined by the programmer, allow for vectorization since the function statements are glued into the actual place where the function is called.

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs048.html b/doc/pub/week9/html/._week9-bs048.html index bdcad49a..a733da36 100644 --- a/doc/pub/week9/html/._week9-bs048.html +++ b/doc/pub/week9/html/._week9-bs048.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - +

    Automatic vectorization and vectorization inhibitors, data dependencies

    + +

    One has to keep in mind that vectorization changes the order of operations inside a loop. A so-called +read-after-write statement with an explicit flow dependency cannot be vectorized. The following code +

    + + +
    +
    +
    +
    +
    +
      double b = 15.;
    +  for (int i = 1; i < n; i++) {
    +      a[i] = a[i-1] + b;
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    Upper bounds on speedup

    -
    -
    -

    -Assume that almost all parts of a code are perfectly -parallelizable (fraction \( f \)). The remainder, -fraction \( (1-f) \) cannot be parallelized at all. - -

    -That is, there is work that takes time \( W \) on one process; a fraction \( f \) of that work will take -time \( Wf/p \) on \( p \) processors. - -

      -
    • What is the maximum possible speedup as a function of \( f \)?
    • -
    +

    is an example of flow dependency and results in wrong numerical results if vectorized. For a scalar operation, the value \( a[i-1] \) computed during the iteration is loaded into the right-hand side and the results are fine. In vector mode however, with a vector length of four, the values \( a[0] \), \( a[1] \), \( a[2] \) and \( a[3] \) from the previous loop will be loaded into the right-hand side and produce wrong results. That is, we have

    + + +
    +
    +
    +
    +
    +
       a[1] = a[0] + b;
    +   a[2] = a[1] + b;
    +   a[3] = a[2] + b;
    +   a[4] = a[3] + b;
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    and if the two first iterations are executed at the same by the SIMD instruction, the value of say \( a[1] \) could be used by the second iteration before it has been calculated by the first iteration, leading thereby to wrong results.

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs049.html b/doc/pub/week9/html/._week9-bs049.html index 780f6641..81e0eddb 100644 --- a/doc/pub/week9/html/._week9-bs049.html +++ b/doc/pub/week9/html/._week9-bs049.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - -

    Amdahl's law

    -
    -
    -

    -On one processor we have -$$ -T_1 = (1-f)W + fW = W -$$ - -On \( p \) processors we have -$$ -T_p = (1-f)W + \frac{fW}{p}, -$$ - -resulting in a speedup of -$$ -\frac{T_1}{T_p} = \frac{W}{(1-f)W+fW/p} -$$ - -

    -As \( p \) goes to infinity, \( fW/p \) goes to zero, and the maximum speedup is -$$ -\frac{1}{1-f}, -$$ - -meaning that if -if \( f = 0.99 \) (all but \( 1\% \) parallelizable), the maximum speedup -is \( 1/(1-.99)=100 \)! +

    Automatic vectorization and vectorization inhibitors, more data dependencies

    + +

    On the other hand, a so-called +write-after-read statement can be vectorized. The following code +

    + + +
    +
    +
    +
    +
    +
      double b = 15.;
    +  for (int i = 1; i < n; i++) {
    +      a[i-1] = a[i] + b;
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    is an example of flow dependency that can be vectorized since no iteration with a higher value of \( i \) +can complete before an iteration with a lower value of \( i \). However, such code leads to problems with parallelization. +

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs050.html b/doc/pub/week9/html/._week9-bs050.html index 1c2b3c42..41ad4205 100644 --- a/doc/pub/week9/html/._week9-bs050.html +++ b/doc/pub/week9/html/._week9-bs050.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - -

    How much is parallelizable

    -
    -
    -

    -If any non-parallel code slips into the -application, the parallel -performance is limited. - -

    -In many simulations, however, the fraction of non-parallelizable work -is \( 10^{-6} \) or less due to large arrays or objects that are perfectly parallelizable. - -

    +

    Automatic vectorization and vectorization inhibitors, memory stride

    + +

    For C++ programmers it is also worth keeping in mind that an array notation is preferred to the more compact use of pointers to access array elements. The compiler can often not tell if it is safe to vectorize the code.

    + +

    When dealing with arrays, you should also avoid memory stride, since this slows down considerably vectorization. When you access array element, write for example the inner loop to vectorize using unit stride, that is, access successively the next array element in memory, as shown here

    + + +
    +
    +
    +
    +
    +
      for (int i = 0; i < n; i++) {
    +      for (int j = 0; j < n; j++) {
    +           a[i][j] += b[i][j];
    +      }  
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

      @@ -632,29 +704,22 @@

      How much is parallelizable 59
    • 60
    • ...
    • -
    • 120
    • +
    • 141
    • »
    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs051.html b/doc/pub/week9/html/._week9-bs051.html index cd6bb807..650c41f8 100644 --- a/doc/pub/week9/html/._week9-bs051.html +++ b/doc/pub/week9/html/._week9-bs051.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - -

    Today's situation of parallel computing

    -
    -
    -

    - +

    Memory management

    +

    The main memory contains the program data

    +
      +
    1. Cache memory contains a copy of the main memory data
    2. +
    3. Cache is faster but consumes more space and power. It is normally assumed to be much faster than main memory
    4. +
    5. Registers contain working data only
      • -
      • Distributed memory is the dominant hardware configuration. There is a large diversity in these machines, from MPP (massively parallel processing) systems to clusters of off-the-shelf PCs, which are very cost-effective.
      • -
      • Message-passing is a mature programming paradigm and widely accepted. It often provides an efficient match to the hardware. It is primarily used for the distributed memory systems, but can also be used on shared memory systems.
      • -
      • Modern nodes have nowadays several cores, which makes it interesting to use both shared memory (the given node) and distributed memory (several nodes with communication). This leads often to codes which use both MPI and OpenMP.
      • +
      • Modern CPUs perform most or all operations only on data in register
      +
    6. Multiple Cache memories contain a copy of the main memory data
    7. +
        +
      • Cache items accessed by their address in main memory
      • +
      • L1 cache is the fastest but has the least capacity
      • +
      • L2, L3 provide intermediate performance/size tradeoffs
      • +
      +
    +

    Loads and stores to memory can be as important as floating point operations when we measure performance.

    -Our lectures will focus on both MPI and OpenMP. - -

    -

    -
    - - -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs052.html b/doc/pub/week9/html/._week9-bs052.html index 495cdd56..3a54f757 100644 --- a/doc/pub/week9/html/._week9-bs052.html +++ b/doc/pub/week9/html/._week9-bs052.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - +

    Memory and communication

    -

    Overhead present in parallel computing

    -
    -
    -

    - +

      +
    1. Most communication in a computer is carried out in chunks, blocks of bytes of data that move together
    2. +
    3. In the memory hierarchy, data moves between memory and cache, and between different levels of cache, in groups called lines
      • -
      • Uneven load balance: not all the processors can perform useful work at all time.
      • -
      • Overhead of synchronization
      • -
      • Overhead of communication
      • -
      • Extra computation due to parallelization
      • +
      • Lines are typically 64-128 bytes, or 8-16 double precision words
      • +
      • Even if you do not use the data, it is moved and occupies space in the cache
      - -Due to the above overhead and that certain parts of a sequential -algorithm cannot be parallelized we may not achieve an optimal parallelization. -
    -
    - + +

    Many of these performance features are not captured in most programming languages.

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs053.html b/doc/pub/week9/html/._week9-bs053.html index c8342328..fe227f06 100644 --- a/doc/pub/week9/html/._week9-bs053.html +++ b/doc/pub/week9/html/._week9-bs053.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - +

    Measuring performance

    -

    Parallelizing a sequential algorithm

    -
    -
    -

    +

    How do we measure performance? What is wrong with this code to time a loop?

    -
      -
    • Identify the part(s) of a sequential algorithm that can be executed in parallel. This is the difficult part,
    • -
    • Distribute the global work and data among \( P \) processors.
    • -
    + +
    +
    +
    +
    +
    +
      clock_t start, finish;
    +  start = clock();
    +  for (int j = 0; j < i; j++) {
    +    a[j] = b[j]+b[j]*c[j];
    +  }
    +  finish = clock();
    +  double timeused = (double) (finish - start)/(CLOCKS_PER_SEC );
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs054.html b/doc/pub/week9/html/._week9-bs054.html index d075705c..3b7c0212 100644 --- a/doc/pub/week9/html/._week9-bs054.html +++ b/doc/pub/week9/html/._week9-bs054.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - -

    Strategies

    -
    -
    -

    - -

      -
    • Develop codes locally, run with some few processes and test your codes. Do benchmarking, timing and so forth on local nodes, for example your laptop or PC.
    • -
    • When you are convinced that your codes run correctly, you can start your production runs on available supercomputers.
    • -
    -
    -
    - - -

    +

    Problems with measuring time

    +
      +
    1. Timers are not infinitely accurate
    2. +
    3. All clocks have a granularity, the minimum time that they can measure
    4. +
    5. The error in a time measurement, even if everything is perfect, may be the size of this granularity (sometimes called a clock tick)
    6. +
    7. Always know what your clock granularity is
    8. +
    9. Ensure that your measurement is for a long enough duration (say 100 times the tick)
    10. +

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs055.html b/doc/pub/week9/html/._week9-bs055.html index 911c6bde..b5e0ffa4 100644 --- a/doc/pub/week9/html/._week9-bs055.html +++ b/doc/pub/week9/html/._week9-bs055.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - -

    How do I run MPI on a PC/Laptop? MPI

    -
    -
    -

    -To install MPI is rather easy on hardware running unix/linux as operating systems, follow simply the instructions from the OpenMPI website. See also subsequent slides. -When you have made sure you have installed MPI on your PC/laptop, - -

      -
    • Compile with mpicxx/mpic++ or mpif90
    • -
    - -

    - - -

      # Compile and link
    -  mpic++ -O3 -o nameofprog.x nameofprog.cpp
    -  #  run code with for example 8 processes using mpirun/mpiexec
    -  mpiexec -n 8 ./nameofprog.x
    -
    -

    -

    -
    - - -

    +

    Problems with cold start

    + +

    What happens when the code is executed? The assumption is that the code is ready to +execute. But +

    +
      +
    1. Code may still be on disk, and not even read into memory.
    2. +
    3. Data may be in slow memory rather than fast (which may be wrong or right for what you are measuring)
    4. +
    5. Multiple tests often necessary to ensure that cold start effects are not present
    6. +
    7. Special effort often required to ensure data in the intended part of the memory hierarchy.
    8. +

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs056.html b/doc/pub/week9/html/._week9-bs056.html index 3d8bba7f..3e234143 100644 --- a/doc/pub/week9/html/._week9-bs056.html +++ b/doc/pub/week9/html/._week9-bs056.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - -

    Can I do it on my own PC/laptop? OpenMP installation

    -
    -
    -

    -If you wish to install MPI and OpenMP -on your laptop/PC, we recommend the following: - -

      -
    • For OpenMP, the compile option -fopenmp is included automatically in recent versions of the C++ compiler and Fortran compilers. For users of different Linux distributions, simply use the available C++ or Fortran compilers and add the above compiler instructions, see also code examples below.
    • -
    • For OS X users however, install libomp
    • -
    - -

    - - -

      brew install libomp
    -
    -

    -and compile and link as -

    - - -

    c++ -o <name executable> <name program.cpp>  -lomp
    -
    -

    -

    -
    - - -

    +

    Problems with smart compilers

    + +
      +
    1. If the result of the computation is not used, the compiler may eliminate the code
    2. +
    3. Performance will look impossibly fantastic
    4. +
    5. Even worse, eliminate some of the code so the performance looks plausible
    6. +
    7. Ensure that the results are (or may be) used.
    8. +

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs057.html b/doc/pub/week9/html/._week9-bs057.html index 9d57c7e9..8c76800c 100644 --- a/doc/pub/week9/html/._week9-bs057.html +++ b/doc/pub/week9/html/._week9-bs057.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - -

    Installing MPI

    -
    -
    -

    -For linux/ubuntu users, you need to install two packages (alternatively use the synaptic package manager) -

    - - -

      sudo apt-get install libopenmpi-dev
    -  sudo apt-get install openmpi-bin
    -
    -

    -For OS X users, install brew (after having installed xcode and gcc, needed for the -gfortran compiler of openmpi) and then install with brew -

    - - -

       brew install openmpi
    -
    -

    -When running an executable (code.x), run as -

    - - -

      mpirun -n 10 ./code.x
    -
    -

    -where we indicate that we want the number of processes to be 10. - -

    -

    -
    - - -

    +

    Problems with interference

    +
      +
    1. Other activities are sharing your processor
    2. +
        +
      • Operating system, system demons, other users
      • +
      • Some parts of the hardware do not always perform with exactly the same performance
      • +
      +
    3. Make multiple tests and report
    4. +
    5. Easy choices include
    6. +
        +
      • Average tests represent what users might observe over time
      • +
      +

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs058.html b/doc/pub/week9/html/._week9-bs058.html index 6cc09ff9..716f9a3f 100644 --- a/doc/pub/week9/html/._week9-bs058.html +++ b/doc/pub/week9/html/._week9-bs058.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - -

    Installing MPI and using Qt

    -
    -
    -

    -With openmpi installed, when using Qt, add to your .pro file the instructions here - -

    -You may need to tell Qt where openmpi is stored. - -

    -

    -
    - - -

    +

    Problems with measuring performance

    +
      +
    1. Accurate, reproducible performance measurement is hard
    2. +
    3. Think carefully about your experiment:
    4. +
    5. What is it, precisely, that you want to measure?
    6. +
    7. How representative is your test to the situation that you are trying to measure?
    8. +

      @@ -629,29 +678,22 @@

      Installing MPI and using Qt
    • 67
    • 68
    • ...
    • -
    • 120
    • +
    • 141
    • »
    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs059.html b/doc/pub/week9/html/._week9-bs059.html index 4ed67e4b..17184cda 100644 --- a/doc/pub/week9/html/._week9-bs059.html +++ b/doc/pub/week9/html/._week9-bs059.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - -

    What is Message Passing Interface (MPI)?

    +

    Thomas algorithm for tridiagonal linear algebra equations

    -

    - -

    -MPI is a library, not a language. It specifies the names, calling sequences and results of functions -or subroutines to be called from C/C++ or Fortran programs, and the classes and methods that make up the MPI C++ -library. The programs that users write in Fortran, C or C++ are compiled with ordinary compilers and linked -with the MPI library. - -

    -MPI programs should be able to run -on all possible machines and run all MPI implementetations without change. - -

    -An MPI computation is a collection of processes communicating with messages. - -

    + +$$ +\left( \begin{array}{ccccc} + b_0 & c_0 & & & \\ + a_0 & b_1 & c_1 & & \\ + & & \ddots & & \\ + & & a_{m-3} & b_{m-2} & c_{m-2} \\ + & & & a_{m-2} & b_{m-1} + \end{array} \right) +\left( \begin{array}{c} + x_0 \\ + x_1 \\ + \vdots \\ + x_{m-2} \\ + x_{m-1} + \end{array} \right)=\left( \begin{array}{c} + f_0 \\ + f_1 \\ + \vdots \\ + f_{m-2} \\ + f_{m-1} \\ + \end{array} \right) +$$

    +

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs060.html b/doc/pub/week9/html/._week9-bs060.html index ac087d68..e66e5ddf 100644 --- a/doc/pub/week9/html/._week9-bs060.html +++ b/doc/pub/week9/html/._week9-bs060.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - -

    Going Parallel with MPI

    +

    Thomas algorithm, forward substitution

    -

    -Task parallelism: the work of a global problem can be divided -into a number of independent tasks, which rarely need to synchronize. -Monte Carlo simulations or numerical integration are examples of this. - -

    -MPI is a message-passing library where all the routines -have corresponding C/C++-binding -

    - - -

       MPI_Command_name
    -
    -

    -and Fortran-binding (routine names are in uppercase, but can also be in lower case) -

    - - -

       MPI_COMMAND_NAME
    -
    -

    + +

    The first step is to multiply the first row by \( a_0/b_0 \) and subtract it from the second row. This is known as the forward substitution step. We obtain then

    +$$ + a_i = 0, +$$ + + +$$ + b_i = b_i - \frac{a_{i-1}}{b_{i-1}}c_{i-1}, +$$ + +

    and

    +$$ + f_i = f_i - \frac{a_{i-1}}{b_{i-1}}f_{i-1}. +$$ + +

    At this point the simplified equation, with only an upper triangular matrix takes the form

    +$$ +\left( \begin{array}{ccccc} + b_0 & c_0 & & & \\ + & b_1 & c_1 & & \\ + & & \ddots & & \\ + & & & b_{m-2} & c_{m-2} \\ + & & & & b_{m-1} + \end{array} \right)\left( \begin{array}{c} + x_0 \\ + x_1 \\ + \vdots \\ + x_{m-2} \\ + x_{m-1} + \end{array} \right)=\left( \begin{array}{c} + f_0 \\ + f_1 \\ + \vdots \\ + f_{m-2} \\ + f_{m-1} \\ + \end{array} \right) +$$
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs061.html b/doc/pub/week9/html/._week9-bs061.html index 6b43b51c..49cf568e 100644 --- a/doc/pub/week9/html/._week9-bs061.html +++ b/doc/pub/week9/html/._week9-bs061.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - -

    MPI is a library

    +

    Thomas algorithm, backward substitution

    -

    -MPI is a library specification for the message passing interface, -proposed as a standard. - -

      -
    • independent of hardware;
    • -
    • not a language or compiler specification;
    • -
    • not a specific implementation or product.
    • -
    - -A message passing standard for portability and ease-of-use. -Designed for high performance. - -

    -Insert communication and synchronization functions where necessary. - -

    + +

    The next step is the backward substitution step. The last row is multiplied by \( c_{N-3}/b_{N-2} \) and subtracted from the second to last row, thus eliminating \( c_{N-3} \) from the last row. The general backward substitution procedure is

    +$$ + c_i = 0, +$$ + +

    and

    +$$ + f_{i-1} = f_{i-1} - \frac{c_{i-1}}{b_i}f_i +$$ + +

    All that ramains to be computed is the solution, which is the very straight forward process of

    +$$ +x_i = \frac{f_i}{b_i} +$$
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs062.html b/doc/pub/week9/html/._week9-bs062.html index 7bbd47db..dd041aa0 100644 --- a/doc/pub/week9/html/._week9-bs062.html +++ b/doc/pub/week9/html/._week9-bs062.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - -

    Bindings to MPI routines

    +

    Thomas algorithm and counting of operations (floating point and memory)

    -

    + -

    -MPI is a message-passing library where all the routines -have corresponding C/C++-binding -

    +

    We have in specific case the following operations with the floating operations

    - -
       MPI_Command_name
    -
    -

    -and Fortran-binding (routine names are in uppercase, but can also be in lower case) -

    +

      +
    • Memory Reads: \( 14(N-2) \);
    • +
    • Memory Writes: \( 4(N-2) \);
    • +
    • Subtractions: \( 3(N-2) \);
    • +
    • Multiplications: \( 3(N-2) \);
    • +
    • Divisions: \( 4(N-2) \).
    • +
    +
    +
    - -
       MPI_COMMAND_NAME
    -
    -

    -The discussion in these slides focuses on the C++ binding. -

    +

    +
    + + + +
    +
    +
    +
    +
    +
    // Forward substitution    
    +// Note that we can simplify by precalculating a[i-1]/b[i-1]
    +  for (int i=1; i < n; i++) {
    +     b[i] = b[i] - (a[i-1]*c[i-1])/b[i-1];
    +     f[i] = g[i] - (a[i-1]*f[i-1])/b[i-1];
    +  }
    +  x[n-1] = f[n-1] / b[n-1];
    +  // Backwards substitution                                                           
    +  for (int i = n-2; i >= 0; i--) {
    +     f[i] = f[i] - c[i]*f[i+1]/b[i+1];
    +     x[i] = f[i]/b[i];
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs063.html b/doc/pub/week9/html/._week9-bs063.html index 6e4b171d..81516243 100644 --- a/doc/pub/week9/html/._week9-bs063.html +++ b/doc/pub/week9/html/._week9-bs063.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - +

    Example: Transpose of a matrix

    + + + +
    +
    +
    +
    +
    +
    #include <cstdlib>
    +#include <iostream>
    +#include <cmath>
    +#include <iomanip>
    +#include "time.h"
    +
    +using namespace std; // note use of namespace
    +int main (int argc, char* argv[])
    +{
    +  // read in dimension of square matrix
    +  int n = atoi(argv[1]);
    +  double **A, **B;
    +  // Allocate space for the two matrices
    +  A = new double*[n]; B = new double*[n];
    +  for (int i = 0; i < n; i++){
    +    A[i] = new double[n];
    +    B[i] = new double[n];
    +  }
    +  // Set up values for matrix A
    +  for (int i = 0; i < n; i++){
    +    for (int j = 0; j < n; j++) {
    +      A[i][j] =  cos(i*1.0)*sin(j*3.0);
    +    }
    +  }
    +  clock_t start, finish;
    +  start = clock();
    +  // Then compute the transpose
    +  for (int i = 0; i < n; i++){
    +    for (int j = 0; j < n; j++) {
    +      B[i][j]= A[j][i];
    +    }
    +  }
     
    -

    Communicator

    -
    -
    -

    - -

      -
    • A group of MPI processes with a name (context).
    • -
    • Any process is identified by its rank. The rank is only meaningful within a particular communicator.
    • -
    • By default the communicator contains all the MPI processes.
    • -
    - -

    + finish = clock(); + double timeused = (double) (finish - start)/(CLOCKS_PER_SEC ); + cout << setiosflags(ios::showpoint | ios::uppercase); + cout << setprecision(10) << setw(20) << "Time used for setting up transpose of matrix=" << timeused << endl; - -

      MPI_COMM_WORLD 
    -
    -
      -
    • Mechanism to identify subset of processes.
    • -
    • Promotes modular design of parallel libraries.
    • -
    + // Free up space + for (int i = 0; i < n; i++){ + delete[] A[i]; + delete[] B[i]; + } + delete[] A; + delete[] B; + return 0; +} +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs064.html b/doc/pub/week9/html/._week9-bs064.html index 97f08b61..7d9d3169 100644 --- a/doc/pub/week9/html/._week9-bs064.html +++ b/doc/pub/week9/html/._week9-bs064.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - +

    Matrix-matrix multiplication

    +

    This the matrix-matrix multiplication code with plain c++ memory allocation. It computes at the end the Frobenius norm.

    -

    Some of the most important MPI functions

    -
    -
    -

    -

      -
    • \( MPI\_Init \) - initiate an MPI computation
    • -
    • \( MPI\_Finalize \) - terminate the MPI computation and clean up
    • -
    • \( MPI\_Comm\_size \) - how many processes participate in a given MPI communicator?
    • -
    • \( MPI\_Comm\_rank \) - which one am I? (A number between 0 and size-1.)
    • -
    • \( MPI\_Send \) - send a message to a particular process within an MPI communicator
    • -
    • \( MPI\_Recv \) - receive a message from a particular process within an MPI communicator
    • -
    • \( MPI\_reduce \) or \( MPI\_Allreduce \), send and receive messages
    • -
    + +
    +
    +
    +
    +
    +
    #include <cstdlib>
    +#include <iostream>
    +#include <cmath>
    +#include <iomanip>
    +#include "time.h"
    +
    +using namespace std; // note use of namespace
    +int main (int argc, char* argv[])
    +{
    +  // read in dimension of square matrix
    +  int n = atoi(argv[1]);
    +  double s = 1.0/sqrt( (double) n);
    +  double **A, **B, **C;
    +  // Start timing
    +  clock_t start, finish;
    +  start = clock();
    +  // Allocate space for the two matrices
    +  A = new double*[n]; B = new double*[n]; C = new double*[n];
    +  for (int i = 0; i < n; i++){
    +    A[i] = new double[n];
    +    B[i] = new double[n];
    +    C[i] = new double[n];
    +  }
    +  // Set up values for matrix A and B and zero matrix C
    +  for (int i = 0; i < n; i++){
    +    for (int j = 0; j < n; j++) {
    +      double angle = 2.0*M_PI*i*j/ (( double ) n);
    +      A[i][j] = s * ( sin ( angle ) + cos ( angle ) );
    +      B[j][i] =  A[i][j];
    +    }
    +  }
    +  // Then perform the matrix-matrix multiplication
    +  for (int i = 0; i < n; i++){
    +    for (int j = 0; j < n; j++) {
    +      double sum = 0.0;
    +       for (int k = 0; k < n; k++) {
    +           sum += B[i][k]*A[k][j];
    +       }
    +       C[i][j] = sum;
    +    }
    +  }
    +  // Compute now the Frobenius norm
    +  double Fsum = 0.0;
    +  for (int i = 0; i < n; i++){
    +    for (int j = 0; j < n; j++) {
    +      Fsum += C[i][j]*C[i][j];
    +    }
    +  }
    +  Fsum = sqrt(Fsum);
    +  finish = clock();
    +  double timeused = (double) (finish - start)/(CLOCKS_PER_SEC );
    +  cout << setiosflags(ios::showpoint | ios::uppercase);
    +  cout << setprecision(10) << setw(20) << "Time used  for matrix-matrix multiplication=" << timeused  << endl;
    +  cout << "  Frobenius norm  = " << Fsum << endl;
    +  // Free up space
    +  for (int i = 0; i < n; i++){
    +    delete[] A[i];
    +    delete[] B[i];
    +    delete[] C[i];
    +  }
    +  delete[] A;
    +  delete[] B;
    +  delete[] C;
    +  return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs065.html b/doc/pub/week9/html/._week9-bs065.html index 04875f80..48608261 100644 --- a/doc/pub/week9/html/._week9-bs065.html +++ b/doc/pub/week9/html/._week9-bs065.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - -

    The first MPI C/C++ program

    +

    How do we define speedup? Simplest form

    -

    - -

    -Let every process write "Hello world" (oh not this program again!!) on the standard output. -

    - - -

    using namespace std;
    -#include <mpi.h>
    -#include <iostream>
    -int main (int nargs, char* args[])
    -{
    -int numprocs, my_rank;
    -//   MPI initializations
    -MPI_Init (&nargs, &args);
    -MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
    -MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);
    -cout << "Hello world, I have  rank " << my_rank << " out of " 
    -     << numprocs << endl;
    -//  End MPI
    -MPI_Finalize ();
    -
    -

    + +

      +
    • Speedup measures the ratio of performance between two objects
    • +
    • Versions of same code, with different number of processors
    • +
    • Serial and vector versions
    • +
    • Try different programing languages, c++ and Fortran
    • +
    • Two algorithms computing the same result
    • +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs066.html b/doc/pub/week9/html/._week9-bs066.html index 9516c6a4..455f5be4 100644 --- a/doc/pub/week9/html/._week9-bs066.html +++ b/doc/pub/week9/html/._week9-bs066.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - -

    The Fortran program

    +

    How do we define speedup? Correct baseline

    -

    -

    - - -

    PROGRAM hello
    -INCLUDE "mpif.h"
    -INTEGER:: size, my_rank, ierr
    -
    -CALL  MPI_INIT(ierr)
    -CALL MPI_COMM_SIZE(MPI_COMM_WORLD, size, ierr)
    -CALL MPI_COMM_RANK(MPI_COMM_WORLD, my_rank, ierr)
    -WRITE(*,*)"Hello world, I've rank ",my_rank," out of ",size
    -CALL MPI_FINALIZE(ierr)
    -
    -END PROGRAM hello
    -
    -

    + +

    The key is choosing the correct baseline for comparison

    +
      +
    • For our serial vs. vectorization examples, using compiler-provided vectorization, the baseline is simple; the same code, with vectorization turned off
    • +
        +
      • For parallel applications, this is much harder:
      • +
          +
        • Choice of algorithm, decomposition, performance of baseline case etc.
        • +
        +
      +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs067.html b/doc/pub/week9/html/._week9-bs067.html index 62956a30..f231b190 100644 --- a/doc/pub/week9/html/._week9-bs067.html +++ b/doc/pub/week9/html/._week9-bs067.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Note 1

    +

    Parallel speedup

    -

    - + +

    For parallel applications, speedup is typically defined as

    +
      +
    • Speedup \( =T_1/T_p \)
    • +
    +

    Here \( T_1 \) is the time on one processor and \( T_p \) is the time using \( p \) processors.

      -
    • The output to screen is not ordered since all processes are trying to write to screen simultaneously.
    • -
    • It is the operating system which opts for an ordering.
    • -
    • If we wish to have an organized output, starting from the first process, we may rewrite our program as in the next example.
    • +
    • Can the speedup become larger than \( p \)? That means using \( p \) processors is more than \( p \) times faster than using one processor.
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs068.html b/doc/pub/week9/html/._week9-bs068.html index e9cde472..7f0fefe9 100644 --- a/doc/pub/week9/html/._week9-bs068.html +++ b/doc/pub/week9/html/._week9-bs068.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Ordered output with MPIBarrier

    +

    Speedup and memory

    -

    - -

    - - -

    int main (int nargs, char* args[])
    -{
    - int numprocs, my_rank, i;
    - MPI_Init (&nargs, &args);
    - MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
    - MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);
    - for (i = 0; i < numprocs; i++) {}
    - MPI_Barrier (MPI_COMM_WORLD);
    - if (i == my_rank) {
    - cout << "Hello world, I have  rank " << my_rank << 
    -        " out of " << numprocs << endl;}
    -      MPI_Finalize ();
    -
    -

    + +

    The speedup on \( p \) processors can +be greater than \( p \) if memory usage is optimal! +Consider the case of a memorybound computation with \( M \) words of memory +

    +
      +
    • If \( M/p \) fits into cache while \( M \) does not, the time to access memory will be different in the two cases:
    • +
    • \( T_1 \) uses the main memory bandwidth
    • +
    • \( T_p \) uses the appropriate cache bandwidth
    • +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs069.html b/doc/pub/week9/html/._week9-bs069.html index 6db01713..1ca12bc3 100644 --- a/doc/pub/week9/html/._week9-bs069.html +++ b/doc/pub/week9/html/._week9-bs069.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Note 2

    +

    Upper bounds on speedup

    -

    - + +

    Assume that almost all parts of a code are perfectly +parallelizable (fraction \( f \)). The remainder, +fraction \( (1-f) \) cannot be parallelized at all. +

    + +

    That is, there is work that takes time \( W \) on one process; a fraction \( f \) of that work will take +time \( Wf/p \) on \( p \) processors. +

      -
    • Here we have used the \( MPI\_Barrier \) function to ensure that that every process has completed its set of instructions in a particular order.
    • -
    • A barrier is a special collective operation that does not allow the processes to continue until all processes in the communicator (here \( MPI\_COMM\_WORLD \)) have called \( MPI\_Barrier \).
    • -
    • The barriers make sure that all processes have reached the same point in the code. Many of the collective operations like \( MPI\_ALLREDUCE \) to be discussed later, have the same property; that is, no process can exit the operation until all processes have started.
    • +
    • What is the maximum possible speedup as a function of \( f \)?
    - -However, this is slightly more time-consuming since the processes synchronize between themselves as many times as there -are processes. In the next Hello world example we use the send and receive functions in order to a have a synchronized -action. - -

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs070.html b/doc/pub/week9/html/._week9-bs070.html index 4d40beea..01108c04 100644 --- a/doc/pub/week9/html/._week9-bs070.html +++ b/doc/pub/week9/html/._week9-bs070.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Ordered output

    +

    Amdahl's law

    -

    - -

    - - -

    .....
    -int numprocs, my_rank, flag;
    -MPI_Status status;
    -MPI_Init (&nargs, &args);
    -MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
    -MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);
    -if (my_rank > 0)
    -MPI_Recv (&flag, 1, MPI_INT, my_rank-1, 100, 
    -           MPI_COMM_WORLD, &status);
    -cout << "Hello world, I have  rank " << my_rank << " out of " 
    -<< numprocs << endl;
    -if (my_rank < numprocs-1)
    -MPI_Send (&my_rank, 1, MPI_INT, my_rank+1, 
    -          100, MPI_COMM_WORLD);
    -MPI_Finalize ();
    -
    -

    + +

    On one processor we have

    +$$ +T_1 = (1-f)W + fW = W +$$ + +

    On \( p \) processors we have

    +$$ +T_p = (1-f)W + \frac{fW}{p}, +$$ + +

    resulting in a speedup of

    +$$ +\frac{T_1}{T_p} = \frac{W}{(1-f)W+fW/p} +$$ + +

    As \( p \) goes to infinity, \( fW/p \) goes to zero, and the maximum speedup is

    +$$ +\frac{1}{1-f}, +$$ + +

    meaning that if +if \( f = 0.99 \) (all but \( 1\% \) parallelizable), the maximum speedup +is \( 1/(1-.99)=100 \)! +

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs071.html b/doc/pub/week9/html/._week9-bs071.html index fec5d586..fed561cb 100644 --- a/doc/pub/week9/html/._week9-bs071.html +++ b/doc/pub/week9/html/._week9-bs071.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Note 3

    +

    How much is parallelizable

    -

    - -

    -The basic sending of messages is given by the function \( MPI\_SEND \), which in C/C++ -is defined as -

    - - -

    int MPI_Send(void *buf, int count, 
    -             MPI_Datatype datatype, 
    -             int dest, int tag, MPI_Comm comm)}
    -
    -

    -This single command allows the passing of any kind of variable, even a large array, to any group of tasks. -The variable buf is the variable we wish to send while count -is the number of variables we are passing. If we are passing only a single value, this should be 1. - -

    -If we transfer an array, it is the overall size of the array. -For example, if we want to send a 10 by 10 array, count would be \( 10\times 10=100 \) -since we are actually passing 100 values. - -

    + +

    If any non-parallel code slips into the +application, the parallel +performance is limited. +

    + +

    In many simulations, however, the fraction of non-parallelizable work +is \( 10^{-6} \) or less due to large arrays or objects that are perfectly parallelizable. +

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs072.html b/doc/pub/week9/html/._week9-bs072.html index b76d114e..8f094ac5 100644 --- a/doc/pub/week9/html/._week9-bs072.html +++ b/doc/pub/week9/html/._week9-bs072.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Note 4

    +

    Today's situation of parallel computing

    -

    - -

    -Once you have sent a message, you must receive it on another task. The function \( MPI\_RECV \) -is similar to the send call. -

    - - -

    int MPI_Recv( void *buf, int count, MPI_Datatype datatype, 
    -            int source, 
    -            int tag, MPI_Comm comm, MPI_Status *status )
    -
    -

    -The arguments that are different from those in MPI\_SEND are -buf which is the name of the variable where you will be storing the received data, -source which replaces the destination in the send command. This is the return ID of the sender. - -

    -Finally, we have used \( MPI\_Status\_status \), -where one can check if the receive was completed. - -

    -The output of this code is the same as the previous example, but now -process 0 sends a message to process 1, which forwards it further -to process 2, and so forth. + -

    +

      +
    • Distributed memory is the dominant hardware configuration. There is a large diversity in these machines, from MPP (massively parallel processing) systems to clusters of off-the-shelf PCs, which are very cost-effective.
    • +
    • Message-passing is a mature programming paradigm and widely accepted. It often provides an efficient match to the hardware. It is primarily used for the distributed memory systems, but can also be used on shared memory systems.
    • +
    • Modern nodes have nowadays several cores, which makes it interesting to use both shared memory (the given node) and distributed memory (several nodes with communication). This leads often to codes which use both MPI and OpenMP.
    • +
    +

    Our lectures will focus on both MPI and OpenMP.

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs073.html b/doc/pub/week9/html/._week9-bs073.html index b1b5ba3b..6acdfb79 100644 --- a/doc/pub/week9/html/._week9-bs073.html +++ b/doc/pub/week9/html/._week9-bs073.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Numerical integration in parallel

    +

    Overhead present in parallel computing

    -

    +

      -
    • The code example computes \( \pi \) using the trapezoidal rules.
    • -
    • The trapezoidal rule
    • +
    • Uneven load balance: not all the processors can perform useful work at all time.
    • +
    • Overhead of synchronization
    • +
    • Overhead of communication
    • +
    • Extra computation due to parallelization
    - -$$ - I=\int_a^bf(x) dx\approx h\left(f(a)/2 + f(a+h) +f(a+2h)+\dots +f(b-h)+ f(b)/2\right). -$$ - -Click on this link for the full program. - -

    +

    Due to the above overhead and that certain parts of a sequential +algorithm cannot be parallelized we may not achieve an optimal parallelization. +

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs074.html b/doc/pub/week9/html/._week9-bs074.html index d479d163..0d951384 100644 --- a/doc/pub/week9/html/._week9-bs074.html +++ b/doc/pub/week9/html/._week9-bs074.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Dissection of trapezoidal rule with \( MPI\_reduce \)

    +

    Parallelizing a sequential algorithm

    -

    - -

    - - -

    //    Trapezoidal rule and numerical integration usign MPI
    -using namespace std;
    -#include <mpi.h>
    -#include <iostream>
    +
     
    -//     Here we define various functions called by the main program
    -
    -double int_function(double );
    -double trapezoidal_rule(double , double , int , double (*)(double));
    -
    -//   Main function begins here
    -int main (int nargs, char* args[])
    -{
    -  int n, local_n, numprocs, my_rank; 
    -  double a, b, h, local_a, local_b, total_sum, local_sum;   
    -  double  time_start, time_end, total_time;
    -
    -

    +

      +
    • Identify the part(s) of a sequential algorithm that can be executed in parallel. This is the difficult part,
    • +
    • Distribute the global work and data among \( P \) processors.
    • +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs075.html b/doc/pub/week9/html/._week9-bs075.html index 15d5355a..5266e16f 100644 --- a/doc/pub/week9/html/._week9-bs075.html +++ b/doc/pub/week9/html/._week9-bs075.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Dissection of trapezoidal rule

    +

    Strategies

    -

    - -

    - - -

      //  MPI initializations
    -  MPI_Init (&nargs, &args);
    -  MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
    -  MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);
    -  time_start = MPI_Wtime();
    -  //  Fixed values for a, b and n 
    -  a = 0.0 ; b = 1.0;  n = 1000;
    -  h = (b-a)/n;    // h is the same for all processes 
    -  local_n = n/numprocs;  
    -  // make sure n > numprocs, else integer division gives zero
    -  // Length of each process' interval of
    -  // integration = local_n*h.  
    -  local_a = a + my_rank*local_n*h;
    -  local_b = local_a + local_n*h;
    -
    -

    + +

      +
    • Develop codes locally, run with some few processes and test your codes. Do benchmarking, timing and so forth on local nodes, for example your laptop or PC.
    • +
    • When you are convinced that your codes run correctly, you can start your production runs on available supercomputers.
    • +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs076.html b/doc/pub/week9/html/._week9-bs076.html index 80fa547b..e5f746fb 100644 --- a/doc/pub/week9/html/._week9-bs076.html +++ b/doc/pub/week9/html/._week9-bs076.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Integrating with MPI

    +

    How do I run MPI on a PC/Laptop? MPI

    -

    - -

    + +

    To install MPI is rather easy on hardware running unix/linux as operating systems, follow simply the instructions from the OpenMPI website. See also subsequent slides. +When you have made sure you have installed MPI on your PC/laptop, +

    +
      +
    • Compile with mpicxx/mpic++ or mpif90
    • +
    -
      total_sum = 0.0;
    -  local_sum = trapezoidal_rule(local_a, local_b, local_n, 
    -                               &int_function); 
    -  MPI_Reduce(&local_sum, &total_sum, 1, MPI_DOUBLE, 
    -              MPI_SUM, 0, MPI_COMM_WORLD);
    -  time_end = MPI_Wtime();
    -  total_time = time_end-time_start;
    -  if ( my_rank == 0) {
    -    cout << "Trapezoidal rule = " <<  total_sum << endl;
    -    cout << "Time = " <<  total_time  
    -         << " on number of processors: "  << numprocs  << endl;
    -  }
    -  // End MPI
    -  MPI_Finalize ();  
    -  return 0;
    -}  // end of main program
    -
    -

    +

    +
    +
    +
    +
    +
      # Compile and link
    +  mpic++ -O3 -o nameofprog.x nameofprog.cpp
    +  #  run code with for example 8 processes using mpirun/mpiexec
    +  mpiexec -n 8 ./nameofprog.x
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs077.html b/doc/pub/week9/html/._week9-bs077.html index 90fe99bd..2d14eea9 100644 --- a/doc/pub/week9/html/._week9-bs077.html +++ b/doc/pub/week9/html/._week9-bs077.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    How do I use \( MPI\_reduce \)?

    +

    Can I do it on my own PC/laptop? OpenMP installation

    -

    - -

    -Here we have used -

    + +

    If you wish to install MPI and OpenMP +on your laptop/PC, we recommend the following: +

    + +
      +
    • For OpenMP, the compile option -fopenmp is included automatically in recent versions of the C++ compiler and Fortran compilers. For users of different Linux distributions, simply use the available C++ or Fortran compilers and add the above compiler instructions, see also code examples below.
    • +
    • For OS X users however, install libomp
    • +
    -
    MPI_reduce( void *senddata, void* resultdata, int count, 
    -     MPI_Datatype datatype, MPI_Op, int root, MPI_Comm comm)
    -
    -

    -The two variables \( senddata \) and \( resultdata \) are obvious, besides the fact that one sends the address -of the variable or the first element of an array. If they are arrays they need to have the same size. -The variable \( count \) represents the total dimensionality, 1 in case of just one variable, -while \( MPI\_Datatype \) -defines the type of variable which is sent and received. +

    +
    +
    +
    +
    +
      brew install libomp
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    -The new feature is \( MPI\_Op \). It defines the type -of operation we want to do. +

    and compile and link as

    + + +
    +
    +
    +
    +
    +
    c++ -o <name executable> <name program.cpp>  -lomp
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs078.html b/doc/pub/week9/html/._week9-bs078.html index d9e91cc5..f3e68e76 100644 --- a/doc/pub/week9/html/._week9-bs078.html +++ b/doc/pub/week9/html/._week9-bs078.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    More on \( MPI\_Reduce \)

    +

    Installing MPI

    -

    -In our case, since we are summing -the rectangle contributions from every process we define \( MPI\_Op = MPI\_SUM \). -If we have an array or matrix we can search for the largest og smallest element by sending either \( MPI\_MAX \) or -\( MPI\_MIN \). If we want the location as well (which array element) we simply transfer -\( MPI\_MAXLOC \) or \( MPI\_MINOC \). If we want the product we write \( MPI\_PROD \). + +

    For linux/ubuntu users, you need to install two packages (alternatively use the synaptic package manager)

    -

    -\( MPI\_Allreduce \) is defined as -

    + +

    +
    +
    +
    +
    +
      sudo apt-get install libopenmpi-dev
    +  sudo apt-get install openmpi-bin
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    For OS X users, install brew (after having installed xcode and gcc, needed for the +gfortran compiler of openmpi) and then install with brew +

    -
    MPI_Allreduce( void *senddata, void* resultdata, int count, 
    -          MPI_Datatype datatype, MPI_Op, MPI_Comm comm)        
    -
    -

    +

    +
    +
    +
    +
    +
       brew install openmpi
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    When running an executable (code.x), run as

    + + +
    +
    +
    +
    +
    +
      mpirun -n 10 ./code.x
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    where we indicate that we want the number of processes to be 10.

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs079.html b/doc/pub/week9/html/._week9-bs079.html index 8f52b31a..9564c933 100644 --- a/doc/pub/week9/html/._week9-bs079.html +++ b/doc/pub/week9/html/._week9-bs079.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Dissection of trapezoidal rule

    +

    Installing MPI and using Qt

    -

    + +

    With openmpi installed, when using Qt, add to your .pro file the instructions here

    -

    -We use \( MPI\_reduce \) to collect data from each process. Note also the use of the function -\( MPI\_Wtime \). -

    - - -

    //  this function defines the function to integrate
    -double int_function(double x)
    -{
    -  double value = 4./(1.+x*x);
    -  return value;
    -} // end of function to evaluate
    -
    -

    +

    You may need to tell Qt where openmpi is stored.

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs080.html b/doc/pub/week9/html/._week9-bs080.html index c880cecb..07a4adcb 100644 --- a/doc/pub/week9/html/._week9-bs080.html +++ b/doc/pub/week9/html/._week9-bs080.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Dissection of trapezoidal rule

    +

    What is Message Passing Interface (MPI)?

    -

    -

    + - -

    //  this function defines the trapezoidal rule
    -double trapezoidal_rule(double a, double b, int n, 
    -                         double (*func)(double))
    -{
    -  double trapez_sum;
    -  double fa, fb, x, step;
    -  int    j;
    -  step=(b-a)/((double) n);
    -  fa=(*func)(a)/2. ;
    -  fb=(*func)(b)/2. ;
    -  trapez_sum=0.;
    -  for (j=1; j <= n-1; j++){
    -    x=j*step+a;
    -    trapez_sum+=(*func)(x);
    -  }
    -  trapez_sum=(trapez_sum+fb+fa)*step;
    -  return trapez_sum;
    -}  // end trapezoidal_rule 
    -
    -

    +

    MPI is a library, not a language. It specifies the names, calling sequences and results of functions +or subroutines to be called from C/C++ or Fortran programs, and the classes and methods that make up the MPI C++ +library. The programs that users write in Fortran, C or C++ are compiled with ordinary compilers and linked +with the MPI library. +

    + +

    MPI programs should be able to run +on all possible machines and run all MPI implementetations without change. +

    + +

    An MPI computation is a collection of processes communicating with messages.

    - -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs081.html b/doc/pub/week9/html/._week9-bs081.html index 08d7daa6..6752638a 100644 --- a/doc/pub/week9/html/._week9-bs081.html +++ b/doc/pub/week9/html/._week9-bs081.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    The quantum dot program for two electrons

    +

    Going Parallel with MPI

    -

    -

    - - -

    // Variational Monte Carlo for atoms with importance sampling, slater det
    -// Test case for 2-electron quantum dot, no classes using Mersenne-Twister RNG
    -#include "mpi.h"
    -#include <cmath>
    -#include <random>
    -#include <string>
    -#include <iostream>
    -#include <fstream>
    -#include <iomanip>
    -#include "vectormatrixclass.h"
    -
    -using namespace  std;
    -// output file as global variable
    -ofstream ofile;  
    -// the step length and its squared inverse for the second derivative 
    -//  Here we define global variables  used in various functions
    -//  These can be changed by using classes
    -int Dimension = 2; 
    -int NumberParticles  = 2;  //  we fix also the number of electrons to be 2
    -
    -// declaration of functions 
    -
    -// The Mc sampling for the variational Monte Carlo 
    -void  MonteCarloSampling(int, double &, double &, Vector &);
    -
    -// The variational wave function
    -double  WaveFunction(Matrix &, Vector &);
    -
    -// The local energy 
    -double  LocalEnergy(Matrix &, Vector &);
    -
    -// The quantum force
    -void  QuantumForce(Matrix &, Matrix &, Vector &);
    -
    -
    -// inline function for single-particle wave function
    -inline double SPwavefunction(double r, double alpha) { 
    -   return exp(-alpha*r*0.5);
    -}
    -
    -// inline function for derivative of single-particle wave function
    -inline double DerivativeSPwavefunction(double r, double alpha) { 
    -  return -r*alpha;
    -}
    -
    -// function for absolute value of relative distance
    -double RelativeDistance(Matrix &r, int i, int j) { 
    -      double r_ij = 0;  
    -      for (int k = 0; k < Dimension; k++) { 
    -	r_ij += (r(i,k)-r(j,k))*(r(i,k)-r(j,k));
    -      }
    -      return sqrt(r_ij); 
    -}
    -
    -// inline function for derivative of Jastrow factor
    -inline double JastrowDerivative(Matrix &r, double beta, int i, int j, int k){
    -  return (r(i,k)-r(j,k))/(RelativeDistance(r, i, j)*pow(1.0+beta*RelativeDistance(r, i, j),2));
    -}
    -
    -// function for square of position of single particle
    -double singleparticle_pos2(Matrix &r, int i) { 
    -    double r_single_particle = 0;
    -    for (int j = 0; j < Dimension; j++) { 
    -      r_single_particle  += r(i,j)*r(i,j);
    -    }
    -    return r_single_particle;
    -}
    -
    -void lnsrch(int n, Vector &xold, double fold, Vector &g, Vector &p, Vector &x,
    -		 double *f, double stpmax, int *check, double (*func)(Vector &p));
    -
    -void dfpmin(Vector &p, int n, double gtol, int *iter, double *fret,
    -	    double(*func)(Vector &p), void (*dfunc)(Vector &p, Vector &g));
    -
    -static double sqrarg;
    -#define SQR(a) ((sqrarg=(a)) == 0.0 ? 0.0 : sqrarg*sqrarg)
    -
    -
    -static double maxarg1,maxarg2;
    -#define FMAX(a,b) (maxarg1=(a),maxarg2=(b),(maxarg1) > (maxarg2) ?\
    -        (maxarg1) : (maxarg2))
    +
    +

    Task parallelism: the work of a global problem can be divided +into a number of independent tasks, which rarely need to synchronize. +Monte Carlo simulations or numerical integration are examples of this. +

    +

    MPI is a message-passing library where all the routines +have corresponding C/C++-binding +

    -// Begin of main program - -int main(int argc, char* argv[]) -{ - - // MPI initializations - int NumberProcesses, MyRank, NumberMCsamples; - MPI_Init (&argc, &argv); - MPI_Comm_size (MPI_COMM_WORLD, &NumberProcesses); - MPI_Comm_rank (MPI_COMM_WORLD, &MyRank); - double StartTime = MPI_Wtime(); - if (MyRank == 0 && argc <= 1) { - cout << "Bad Usage: " << argv[0] << - " Read also output file on same line and number of Monte Carlo cycles" << endl; - } - // Read filename and number of Monte Carlo cycles from the command line - if (MyRank == 0 && argc > 2) { - string filename = argv[1]; // first command line argument after name of program - NumberMCsamples = atoi(argv[2]); - string fileout = filename; - string argument = to_string(NumberMCsamples); - // Final filename as filename+NumberMCsamples - fileout.append(argument); - ofile.open(fileout); - } - // broadcast the number of Monte Carlo samples - MPI_Bcast (&NumberMCsamples, 1, MPI_INT, 0, MPI_COMM_WORLD); - // Two variational parameters only - Vector VariationalParameters(2); - int TotalNumberMCsamples = NumberMCsamples*NumberProcesses; - // Loop over variational parameters - for (double alpha = 0.5; alpha <= 1.5; alpha +=0.1){ - for (double beta = 0.1; beta <= 0.5; beta +=0.05){ - VariationalParameters(0) = alpha; // value of alpha - VariationalParameters(1) = beta; // value of beta - // Do the mc sampling and accumulate data with MPI_Reduce - double TotalEnergy, TotalEnergySquared, LocalProcessEnergy, LocalProcessEnergy2; - LocalProcessEnergy = LocalProcessEnergy2 = 0.0; - MonteCarloSampling(NumberMCsamples, LocalProcessEnergy, LocalProcessEnergy2, VariationalParameters); - // Collect data in total averages - MPI_Reduce(&LocalProcessEnergy, &TotalEnergy, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); - MPI_Reduce(&LocalProcessEnergy2, &TotalEnergySquared, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); - // Print out results in case of Master node, set to MyRank = 0 - if ( MyRank == 0) { - double Energy = TotalEnergy/( (double)NumberProcesses); - double Variance = TotalEnergySquared/( (double)NumberProcesses)-Energy*Energy; - double StandardDeviation = sqrt(Variance/((double)TotalNumberMCsamples)); // over optimistic error - ofile << setiosflags(ios::showpoint | ios::uppercase); - ofile << setw(15) << setprecision(8) << VariationalParameters(0); - ofile << setw(15) << setprecision(8) << VariationalParameters(1); - ofile << setw(15) << setprecision(8) << Energy; - ofile << setw(15) << setprecision(8) << Variance; - ofile << setw(15) << setprecision(8) << StandardDeviation << endl; - } - } - } - double EndTime = MPI_Wtime(); - double TotalTime = EndTime-StartTime; - if ( MyRank == 0 ) cout << "Time = " << TotalTime << " on number of processors: " << NumberProcesses << endl; - if (MyRank == 0) ofile.close(); // close output file - // End MPI - MPI_Finalize (); - return 0; -} // end of main function - - -// Monte Carlo sampling with the Metropolis algorithm - -void MonteCarloSampling(int NumberMCsamples, double &cumulative_e, double &cumulative_e2, Vector &VariationalParameters) -{ - - // Initialize the seed and call the Mersienne algo - std::random_device rd; - std::mt19937_64 gen(rd()); - // Set up the uniform distribution for x \in [[0, 1] - std::uniform_real_distribution<double> UniformNumberGenerator(0.0,1.0); - std::normal_distribution<double> Normaldistribution(0.0,1.0); - // diffusion constant from Schroedinger equation - double D = 0.5; - double timestep = 0.05; // we fix the time step for the gaussian deviate - // allocate matrices which contain the position of the particles - Matrix OldPosition( NumberParticles, Dimension), NewPosition( NumberParticles, Dimension); - Matrix OldQuantumForce(NumberParticles, Dimension), NewQuantumForce(NumberParticles, Dimension); - double Energy = 0.0; double EnergySquared = 0.0; double DeltaE = 0.0; - // initial trial positions - for (int i = 0; i < NumberParticles; i++) { - for (int j = 0; j < Dimension; j++) { - OldPosition(i,j) = Normaldistribution(gen)*sqrt(timestep); - } - } - double OldWaveFunction = WaveFunction(OldPosition, VariationalParameters); - QuantumForce(OldPosition, OldQuantumForce, VariationalParameters); - // loop over monte carlo cycles - for (int cycles = 1; cycles <= NumberMCsamples; cycles++){ - // new position - for (int i = 0; i < NumberParticles; i++) { - for (int j = 0; j < Dimension; j++) { - // gaussian deviate to compute new positions using a given timestep - NewPosition(i,j) = OldPosition(i,j) + Normaldistribution(gen)*sqrt(timestep)+OldQuantumForce(i,j)*timestep*D; - // NewPosition(i,j) = OldPosition(i,j) + gaussian_deviate(&idum)*sqrt(timestep)+OldQuantumForce(i,j)*timestep*D; - } - // for the other particles we need to set the position to the old position since - // we move only one particle at the time - for (int k = 0; k < NumberParticles; k++) { - if ( k != i) { - for (int j = 0; j < Dimension; j++) { - NewPosition(k,j) = OldPosition(k,j); - } - } - } - double NewWaveFunction = WaveFunction(NewPosition, VariationalParameters); - QuantumForce(NewPosition, NewQuantumForce, VariationalParameters); - // we compute the log of the ratio of the greens functions to be used in the - // Metropolis-Hastings algorithm - double GreensFunction = 0.0; - for (int j = 0; j < Dimension; j++) { - GreensFunction += 0.5*(OldQuantumForce(i,j)+NewQuantumForce(i,j))* - (D*timestep*0.5*(OldQuantumForce(i,j)-NewQuantumForce(i,j))-NewPosition(i,j)+OldPosition(i,j)); - } - GreensFunction = exp(GreensFunction); - // The Metropolis test is performed by moving one particle at the time - if(UniformNumberGenerator(gen) <= GreensFunction*NewWaveFunction*NewWaveFunction/OldWaveFunction/OldWaveFunction ) { - for (int j = 0; j < Dimension; j++) { - OldPosition(i,j) = NewPosition(i,j); - OldQuantumForce(i,j) = NewQuantumForce(i,j); - } - OldWaveFunction = NewWaveFunction; - } - } // end of loop over particles - // compute local energy - double DeltaE = LocalEnergy(OldPosition, VariationalParameters); - // update energies - Energy += DeltaE; - EnergySquared += DeltaE*DeltaE; - } // end of loop over MC trials - // update the energy average and its squared - cumulative_e = Energy/NumberMCsamples; - cumulative_e2 = EnergySquared/NumberMCsamples; -} // end MonteCarloSampling function - - -// Function to compute the squared wave function and the quantum force - -double WaveFunction(Matrix &r, Vector &VariationalParameters) -{ - double wf = 0.0; - // full Slater determinant for two particles, replace with Slater det for more particles - wf = SPwavefunction(singleparticle_pos2(r, 0), VariationalParameters(0))*SPwavefunction(singleparticle_pos2(r, 1),VariationalParameters(0)); - // contribution from Jastrow factor - for (int i = 0; i < NumberParticles-1; i++) { - for (int j = i+1; j < NumberParticles; j++) { - wf *= exp(RelativeDistance(r, i, j)/((1.0+VariationalParameters(1)*RelativeDistance(r, i, j)))); - } - } - return wf; -} - -// Function to calculate the local energy without numerical derivation of kinetic energy - -double LocalEnergy(Matrix &r, Vector &VariationalParameters) -{ - - // compute the kinetic and potential energy from the single-particle part - // for a many-electron system this has to be replaced by a Slater determinant - // The absolute value of the interparticle length - Matrix length( NumberParticles, NumberParticles); - // Set up interparticle distance - for (int i = 0; i < NumberParticles-1; i++) { - for(int j = i+1; j < NumberParticles; j++){ - length(i,j) = RelativeDistance(r, i, j); - length(j,i) = length(i,j); - } - } - double KineticEnergy = 0.0; - // Set up kinetic energy from Slater and Jastrow terms - for (int i = 0; i < NumberParticles; i++) { - for (int k = 0; k < Dimension; k++) { - double sum1 = 0.0; - for(int j = 0; j < NumberParticles; j++){ - if ( j != i) { - sum1 += JastrowDerivative(r, VariationalParameters(1), i, j, k); - } - } - KineticEnergy += (sum1+DerivativeSPwavefunction(r(i,k),VariationalParameters(0)))*(sum1+DerivativeSPwavefunction(r(i,k),VariationalParameters(0))); - } - } - KineticEnergy += -2*VariationalParameters(0)*NumberParticles; - for (int i = 0; i < NumberParticles-1; i++) { - for (int j = i+1; j < NumberParticles; j++) { - KineticEnergy += 2.0/(pow(1.0 + VariationalParameters(1)*length(i,j),2))*(1.0/length(i,j)-2*VariationalParameters(1)/(1+VariationalParameters(1)*length(i,j)) ); - } - } - KineticEnergy *= -0.5; - // Set up potential energy, external potential + eventual electron-electron repulsion - double PotentialEnergy = 0; - for (int i = 0; i < NumberParticles; i++) { - double DistanceSquared = singleparticle_pos2(r, i); - PotentialEnergy += 0.5*DistanceSquared; // sp energy HO part, note it has the oscillator frequency set to 1! - } - // Add the electron-electron repulsion - for (int i = 0; i < NumberParticles-1; i++) { - for (int j = i+1; j < NumberParticles; j++) { - PotentialEnergy += 1.0/length(i,j); - } - } - double LocalE = KineticEnergy+PotentialEnergy; - return LocalE; -} - -// Compute the analytical expression for the quantum force -void QuantumForce(Matrix &r, Matrix &qforce, Vector &VariationalParameters) -{ - // compute the first derivative - for (int i = 0; i < NumberParticles; i++) { - for (int k = 0; k < Dimension; k++) { - // single-particle part, replace with Slater det for larger systems - double sppart = DerivativeSPwavefunction(r(i,k),VariationalParameters(0)); - // Jastrow factor contribution - double Jsum = 0.0; - for (int j = 0; j < NumberParticles; j++) { - if ( j != i) { - Jsum += JastrowDerivative(r, VariationalParameters(1), i, j, k); - } - } - qforce(i,k) = 2.0*(Jsum+sppart); - } - } -} // end of QuantumForce function - - -#define ITMAX 200 -#define EPS 3.0e-8 -#define TOLX (4*EPS) -#define STPMX 100.0 - -void dfpmin(Vector &p, int n, double gtol, int *iter, double *fret, - double(*func)(Vector &p), void (*dfunc)(Vector &p, Vector &g)) -{ - - int check,i,its,j; - double den,fac,fad,fae,fp,stpmax,sum=0.0,sumdg,sumxi,temp,test; - Vector dg(n), g(n), hdg(n), pnew(n), xi(n); - Matrix hessian(n,n); - - fp=(*func)(p); - (*dfunc)(p,g); - for (i = 0;i < n;i++) { - for (j = 0; j< n;j++) hessian(i,j)=0.0; - hessian(i,i)=1.0; - xi(i) = -g(i); - sum += p(i)*p(i); - } - stpmax=STPMX*FMAX(sqrt(sum),(double)n); - for (its=1;its<=ITMAX;its++) { - *iter=its; - lnsrch(n,p,fp,g,xi,pnew,fret,stpmax,&check,func); - fp = *fret; - for (i = 0; i< n;i++) { - xi(i)=pnew(i)-p(i); - p(i)=pnew(i); - } - test=0.0; - for (i = 0;i< n;i++) { - temp=fabs(xi(i))/FMAX(fabs(p(i)),1.0); - if (temp > test) test=temp; - } - if (test < TOLX) { - return; - } - for (i=0;i<n;i++) dg(i)=g(i); - (*dfunc)(p,g); - test=0.0; - den=FMAX(*fret,1.0); - for (i=0;i<n;i++) { - temp=fabs(g(i))*FMAX(fabs(p(i)),1.0)/den; - if (temp > test) test=temp; - } - if (test < gtol) { - return; - } - for (i=0;i<n;i++) dg(i)=g(i)-dg(i); - for (i=0;i<n;i++) { - hdg(i)=0.0; - for (j=0;j<n;j++) hdg(i) += hessian(i,j)*dg(j); - } - fac=fae=sumdg=sumxi=0.0; - for (i=0;i<n;i++) { - fac += dg(i)*xi(i); - fae += dg(i)*hdg(i); - sumdg += SQR(dg(i)); - sumxi += SQR(xi(i)); - } - if (fac*fac > EPS*sumdg*sumxi) { - fac=1.0/fac; - fad=1.0/fae; - for (i=0;i<n;i++) dg(i)=fac*xi(i)-fad*hdg(i); - for (i=0;i<n;i++) { - for (j=0;j<n;j++) { - hessian(i,j) += fac*xi(i)*xi(j) - -fad*hdg(i)*hdg(j)+fae*dg(i)*dg(j); - } - } - } - for (i=0;i<n;i++) { - xi(i)=0.0; - for (j=0;j<n;j++) xi(i) -= hessian(i,j)*g(j); - } - } - cout << "too many iterations in dfpmin" << endl; -} -#undef ITMAX -#undef EPS -#undef TOLX -#undef STPMX - -#define ALF 1.0e-4 -#define TOLX 1.0e-7 + +
    +
    +
    +
    +
    +
       MPI_Command_name
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -void lnsrch(int n, Vector &xold, double fold, Vector &g, Vector &p, Vector &x, - double *f, double stpmax, int *check, double (*func)(Vector &p)) -{ - int i; - double a,alam,alam2,alamin,b,disc,f2,fold2,rhs1,rhs2,slope,sum,temp, - test,tmplam; +

    and Fortran-binding (routine names are in uppercase, but can also be in lower case)

    - *check=0; - for (sum=0.0,i=0;i<n;i++) sum += p(i)*p(i); - sum=sqrt(sum); - if (sum > stpmax) - for (i=0;i<n;i++) p(i) *= stpmax/sum; - for (slope=0.0,i=0;i<n;i++) - slope += g(i)*p(i); - test=0.0; - for (i=0;i<n;i++) { - temp=fabs(p(i))/FMAX(fabs(xold(i)),1.0); - if (temp > test) test=temp; - } - alamin=TOLX/test; - alam=1.0; - for (;;) { - for (i=0;i<n;i++) x(i)=xold(i)+alam*p(i); - *f=(*func)(x); - if (alam < alamin) { - for (i=0;i<n;i++) x(i)=xold(i); - *check=1; - return; - } else if (*f <= fold+ALF*alam*slope) return; - else { - if (alam == 1.0) - tmplam = -slope/(2.0*(*f-fold-slope)); - else { - rhs1 = *f-fold-alam*slope; - rhs2=f2-fold2-alam2*slope; - a=(rhs1/(alam*alam)-rhs2/(alam2*alam2))/(alam-alam2); - b=(-alam2*rhs1/(alam*alam)+alam*rhs2/(alam2*alam2))/(alam-alam2); - if (a == 0.0) tmplam = -slope/(2.0*b); - else { - disc=b*b-3.0*a*slope; - if (disc<0.0) cout << "Roundoff problem in lnsrch." << endl; - else tmplam=(-b+sqrt(disc))/(3.0*a); - } - if (tmplam>0.5*alam) - tmplam=0.5*alam; - } - } - alam2=alam; - f2 = *f; - fold2=fold; - alam=FMAX(tmplam,0.1*alam); - } -} -#undef ALF -#undef TOLX -
    -

    + +

    +
    +
    +
    +
    +
       MPI_COMMAND_NAME
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs082.html b/doc/pub/week9/html/._week9-bs082.html index 04c49817..70705a4a 100644 --- a/doc/pub/week9/html/._week9-bs082.html +++ b/doc/pub/week9/html/._week9-bs082.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    What is OpenMP

    +

    MPI is a library

    -

    - -

      -
    • OpenMP provides high-level thread programming
    • -
    • Multiple cooperating threads are allowed to run simultaneously
    • -
    • Threads are created and destroyed dynamically in a fork-join pattern
    • + +

      MPI is a library specification for the message passing interface, +proposed as a standard. +

        -
      • An OpenMP program consists of a number of parallel regions
      • -
      • Between two parallel regions there is only one master thread
      • -
      • In the beginning of a parallel region, a team of new threads is spawned
      • +
      • independent of hardware;
      • +
      • not a language or compiler specification;
      • +
      • not a specific implementation or product.
      +

      A message passing standard for portability and ease-of-use. +Designed for high performance. +

      -
    • The newly spawned threads work simultaneously with the master thread
    • -
    • At the end of a parallel region, the new threads are destroyed
    • -
    - -Many good tutorials online and excellent textbook - -
      -
    1. Using OpenMP, by B. Chapman, G. Jost, and A. van der Pas
    2. -
    3. Many tutorials online like OpenMP official site
    4. -
    +

    Insert communication and synchronization functions where necessary.

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs083.html b/doc/pub/week9/html/._week9-bs083.html index 09291884..4dec0aed 100644 --- a/doc/pub/week9/html/._week9-bs083.html +++ b/doc/pub/week9/html/._week9-bs083.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Getting started, things to remember

    +

    Bindings to MPI routines

    -

    + -

      -
    • Remember the header file
    • -
    - -

    +

    MPI is a message-passing library where all the routines +have corresponding C/C++-binding +

    -
    #include <omp.h>
    -
    -
      -
    • Insert compiler directives in C++ syntax as
    • -
    - -

    +

    +
    +
    +
    +
    +
       MPI_Command_name
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    - -
    #pragma omp...
    -
    -
      -
    • Compile with for example c++ -fopenmp code.cpp
    • -
    • Execute
    • +

      and Fortran-binding (routine names are in uppercase, but can also be in lower case)

      -
        -
      • Remember to assign the environment variable OMP NUM THREADS
      • -
      • It specifies the total number of threads inside a parallel region, if not otherwise overwritten
      • -
      + +
      +
      +
      +
      +
      +
         MPI_COMMAND_NAME
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      -
    +

    The discussion in these slides focuses on the C++ binding.

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs084.html b/doc/pub/week9/html/._week9-bs084.html index dae1448c..4ba1f0d6 100644 --- a/doc/pub/week9/html/._week9-bs084.html +++ b/doc/pub/week9/html/._week9-bs084.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    OpenMP syntax

    - +

    Communicator

    +
    +
    +
      -
    • Mostly directives
    • +
    • A group of MPI processes with a name (context).
    • +
    • Any process is identified by its rank. The rank is only meaningful within a particular communicator.
    • +
    • By default the communicator contains all the MPI processes.
    -

    - -

    #pragma omp construct [ clause ...]
    -
    -
      -
    • Some functions and types
    • -
    - -

    +

    +
    +
    +
    +
    +
      MPI_COMM_WORLD 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    - -
    #include <omp.h>
    -
      -
    • Most apply to a block of code
    • -
    • Specifically, a structured block
    • -
    • Enter at top, exit at bottom only, exit(), abort() permitted
    • +
    • Mechanism to identify subset of processes.
    • +
    • Promotes modular design of parallel libraries.
    +
    +
    +

    @@ -640,29 +711,22 @@

    OpenMP syntax

  • 93
  • 94
  • ...
  • -
  • 120
  • +
  • 141
  • »
  • -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs085.html b/doc/pub/week9/html/._week9-bs085.html index af59c3c3..81713681 100644 --- a/doc/pub/week9/html/._week9-bs085.html +++ b/doc/pub/week9/html/._week9-bs085.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Different OpenMP styles of parallelism

    -OpenMP supports several different ways to specify thread parallelism +

    Some of the most important MPI functions

    +
    +
    +
      -
    • General parallel regions: All threads execute the code, roughly as if you made a routine of that region and created a thread to run that code
    • -
    • Parallel loops: Special case for loops, simplifies data parallel code
    • -
    • Task parallelism, new in OpenMP 3
    • -
    • Several ways to manage thread coordination, including Master regions and Locks
    • -
    • Memory model for shared data
    • +
    • \( MPI\_Init \) - initiate an MPI computation
    • +
    • \( MPI\_Finalize \) - terminate the MPI computation and clean up
    • +
    • \( MPI\_Comm\_size \) - how many processes participate in a given MPI communicator?
    • +
    • \( MPI\_Comm\_rank \) - which one am I? (A number between 0 and size-1.)
    • +
    • \( MPI\_Send \) - send a message to a particular process within an MPI communicator
    • +
    • \( MPI\_Recv \) - receive a message from a particular process within an MPI communicator
    • +
    • \( MPI\_reduce \) or \( MPI\_Allreduce \), send and receive messages
    +
    +
    +

    @@ -625,29 +689,22 @@

    Different OpenMP
  • 94
  • 95
  • ...
  • -
  • 120
  • +
  • 141
  • »
  • -

    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs086.html b/doc/pub/week9/html/._week9-bs086.html index dfb9e30b..0d4d72c4 100644 --- a/doc/pub/week9/html/._week9-bs086.html +++ b/doc/pub/week9/html/._week9-bs086.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    General code structure

    +

    The first MPI C/C++ program

    -

    -

    + + +

    Let every process write "Hello world" (oh not this program again!!) on the standard output.

    -
    #include <omp.h>
    -main ()
    -{
    -int var1, var2, var3;
    -/* serial code */
    -/* ... */
    -/* start of a parallel region */
    -#pragma omp parallel private(var1, var2) shared(var3)
    -{
    -/* ... */
    -}
    -/* more serial code */
    -/* ... */
    -/* another parallel region */
    -#pragma omp parallel
    -{
    -/* ... */
    -}
    -}
    -
    -

    +

    +
    +
    +
    +
    +
    using namespace std;
    +#include <mpi.h>
    +#include <iostream>
    +int main (int nargs, char* args[])
    +{
    +int numprocs, my_rank;
    +//   MPI initializations
    +MPI_Init (&nargs, &args);
    +MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
    +MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);
    +cout << "Hello world, I have  rank " << my_rank << " out of " 
    +     << numprocs << endl;
    +//  End MPI
    +MPI_Finalize ();
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs087.html b/doc/pub/week9/html/._week9-bs087.html index 425b579a..134f35cc 100644 --- a/doc/pub/week9/html/._week9-bs087.html +++ b/doc/pub/week9/html/._week9-bs087.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Parallel region

    +

    The Fortran program

    -

    - -

      -
    • A parallel region is a block of code that is executed by a team of threads
    • -
    • The following compiler directive creates a parallel region
    • -
    - -

    - - -

    #pragma omp parallel { ... }
    -
    -
      -
    • Clauses can be added at the end of the directive
    • -
    • Most often used clauses:
    • - -
        -
      • default(shared) or default(none)
      • -
      • public(list of variables)
      • -
      • private(list of variables)
      • -
      - -
    + + + +
    +
    +
    +
    +
    +
    PROGRAM hello
    +INCLUDE "mpif.h"
    +INTEGER:: size, my_rank, ierr
    +
    +CALL  MPI_INIT(ierr)
    +CALL MPI_COMM_SIZE(MPI_COMM_WORLD, size, ierr)
    +CALL MPI_COMM_RANK(MPI_COMM_WORLD, my_rank, ierr)
    +WRITE(*,*)"Hello world, I've rank ",my_rank," out of ",size
    +CALL MPI_FINALIZE(ierr)
    +
    +END PROGRAM hello
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs088.html b/doc/pub/week9/html/._week9-bs088.html index 9c76ad31..4475cf1e 100644 --- a/doc/pub/week9/html/._week9-bs088.html +++ b/doc/pub/week9/html/._week9-bs088.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Hello world, not again, please!

    +

    Note 1

    -

    -

    + - -

    #include <omp.h>
    -#include <cstdio>
    -int main (int argc, char *argv[])
    -{
    -int th_id, nthreads;
    -#pragma omp parallel private(th_id) shared(nthreads)
    -{
    -th_id = omp_get_thread_num();
    -printf("Hello World from thread %d\n", th_id);
    -#pragma omp barrier
    -if ( th_id == 0 ) {
    -nthreads = omp_get_num_threads();
    -printf("There are %d threads\n",nthreads);
    -}
    -}
    -return 0;
    -}
    -
    -

    +

      +
    • The output to screen is not ordered since all processes are trying to write to screen simultaneously.
    • +
    • It is the operating system which opts for an ordering.
    • +
    • If we wish to have an organized output, starting from the first process, we may rewrite our program as in the next example.
    • +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs089.html b/doc/pub/week9/html/._week9-bs089.html index b271a939..64cbb14f 100644 --- a/doc/pub/week9/html/._week9-bs089.html +++ b/doc/pub/week9/html/._week9-bs089.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Hello world, yet another variant

    +

    Ordered output with MPIBarrier

    -

    -

    + - -

    #include <cstdio>
    -#include <omp.h>
    -int main(int argc, char *argv[]) 
    -{
    - omp_set_num_threads(4); 
    -#pragma omp parallel
    - {
    -   int id = omp_get_thread_num();
    -   int nproc = omp_get_num_threads(); 
    -   cout << "Hello world with id number and processes " <<  id <<  nproc << endl;
    - } 
    -return 0;
    -}
    -
    -

    -Variables declared outside of the parallel region are shared by all threads -If a variable like id is declared outside of the -

    -

    #pragma omp parallel, 
    -
    -

    -it would have been shared by various the threads, possibly causing erroneous output - -

      -
    • Why? What would go wrong? Why do we add possibly?
    • -
    +
    +
    +
    +
    +
    +
    int main (int nargs, char* args[])
    +{
    + int numprocs, my_rank, i;
    + MPI_Init (&nargs, &args);
    + MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
    + MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);
    + for (i = 0; i < numprocs; i++) {}
    + MPI_Barrier (MPI_COMM_WORLD);
    + if (i == my_rank) {
    + cout << "Hello world, I have  rank " << my_rank << 
    +        " out of " << numprocs << endl;}
    +      MPI_Finalize ();
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs090.html b/doc/pub/week9/html/._week9-bs090.html index 4d5a2bce..ad85091c 100644 --- a/doc/pub/week9/html/._week9-bs090.html +++ b/doc/pub/week9/html/._week9-bs090.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Important OpenMP library routines

    +

    Note 2

    -

    - +

      -
    • int omp get num threads (), returns the number of threads inside a parallel region
    • -
    • int omp get thread num (), returns the a thread for each thread inside a parallel region
    • -
    • void omp set num threads (int), sets the number of threads to be used
    • -
    • void omp set nested (int), turns nested parallelism on/off
    • +
    • Here we have used the \( MPI\_Barrier \) function to ensure that that every process has completed its set of instructions in a particular order.
    • +
    • A barrier is a special collective operation that does not allow the processes to continue until all processes in the communicator (here \( MPI\_COMM\_WORLD \)) have called \( MPI\_Barrier \).
    • +
    • The barriers make sure that all processes have reached the same point in the code. Many of the collective operations like \( MPI\_ALLREDUCE \) to be discussed later, have the same property; that is, no process can exit the operation until all processes have started.
    +

    However, this is slightly more time-consuming since the processes synchronize between themselves as many times as there +are processes. In the next Hello world example we use the send and receive functions in order to a have a synchronized +action. +

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs091.html b/doc/pub/week9/html/._week9-bs091.html index 981265e6..2dfc5223 100644 --- a/doc/pub/week9/html/._week9-bs091.html +++ b/doc/pub/week9/html/._week9-bs091.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Private variables

    +

    Ordered output

    -

    -Private clause can be used to make thread- private versions of such variables: -

    + - -

    #pragma omp parallel private(id)
    -{
    - int id = omp_get_thread_num();
    - cout << "My thread num" << id << endl; 
    -}
    -
    -
      -
    • What is their value on entry? Exit?
    • -
    • OpenMP provides ways to control that
    • -
    • Can use default(none) to require the sharing of each variable to be described
    • -
    + + +
    +
    +
    +
    +
    +
    .....
    +int numprocs, my_rank, flag;
    +MPI_Status status;
    +MPI_Init (&nargs, &args);
    +MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
    +MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);
    +if (my_rank > 0)
    +MPI_Recv (&flag, 1, MPI_INT, my_rank-1, 100, 
    +           MPI_COMM_WORLD, &status);
    +cout << "Hello world, I have  rank " << my_rank << " out of " 
    +<< numprocs << endl;
    +if (my_rank < numprocs-1)
    +MPI_Send (&my_rank, 1, MPI_INT, my_rank+1, 
    +          100, MPI_COMM_WORLD);
    +MPI_Finalize ();
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs092.html b/doc/pub/week9/html/._week9-bs092.html index 10e49d65..ae90d140 100644 --- a/doc/pub/week9/html/._week9-bs092.html +++ b/doc/pub/week9/html/._week9-bs092.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Master region

    +

    Note 3

    -

    -It is often useful to have only one thread execute some of the code in a parallel region. I/O statements are a common example -

    + + +

    The basic sending of messages is given by the function \( MPI\_SEND \), which in C/C++ +is defined as +

    -
    #pragma omp parallel 
    -{
    -  #pragma omp master
    -   {
    -      int id = omp_get_thread_num();
    -      cout << "My thread num" << id << endl; 
    -   } 
    -}
    -
    -

    +

    +
    +
    +
    +
    +
    int MPI_Send(void *buf, int count, 
    +             MPI_Datatype datatype, 
    +             int dest, int tag, MPI_Comm comm)}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    This single command allows the passing of any kind of variable, even a large array, to any group of tasks. +The variable buf is the variable we wish to send while count +is the number of variables we are passing. If we are passing only a single value, this should be 1. +

    + +

    If we transfer an array, it is the overall size of the array. +For example, if we want to send a 10 by 10 array, count would be \( 10\times 10=100 \) +since we are actually passing 100 values. +

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs093.html b/doc/pub/week9/html/._week9-bs093.html index 01280a2b..f2e07cd3 100644 --- a/doc/pub/week9/html/._week9-bs093.html +++ b/doc/pub/week9/html/._week9-bs093.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Parallel for loop

    +

    Note 4

    -

    - -

      -
    • Inside a parallel region, the following compiler directive can be used to parallelize a for-loop:
    • -
    + -

    +

    Once you have sent a message, you must receive it on another task. The function \( MPI\_RECV \) +is similar to the send call. +

    -
    #pragma omp for
    -
    -
      -
    • Clauses can be added, such as
    • +
      +
      +
      +
      +
      +
      int MPI_Recv( void *buf, int count, MPI_Datatype datatype, 
      +            int source, 
      +            int tag, MPI_Comm comm, MPI_Status *status )
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      -
        -
      • schedule(static, chunk size)
      • -
      • schedule(dynamic, chunk size)
      • -
      • schedule(guided, chunk size) (non-deterministic allocation)
      • -
      • schedule(runtime)
      • -
      • private(list of variables)
      • -
      • reduction(operator:variable)
      • -
      • nowait
      • -
      +

      The arguments that are different from those in MPI\_SEND are +buf which is the name of the variable where you will be storing the received data, +source which replaces the destination in the send command. This is the return ID of the sender. +

      -
    +

    Finally, we have used \( MPI\_Status\_status \), +where one can check if the receive was completed. +

    + +

    The output of this code is the same as the previous example, but now +process 0 sends a message to process 1, which forwards it further +to process 2, and so forth. +

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs094.html b/doc/pub/week9/html/._week9-bs094.html index f936541b..62f58770 100644 --- a/doc/pub/week9/html/._week9-bs094.html +++ b/doc/pub/week9/html/._week9-bs094.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Parallel computations and loops

    - -

    +

    Numerical integration in parallel

    -

    -OpenMP provides an easy way to parallelize a loop -

    + - -

    #pragma omp parallel for
    -  for (i=0; i<n; i++) c[i] = a[i];
    -
    -

    -OpenMP handles index variable (no need to declare in for loop or make private) +

      +
    • The code example computes \( \pi \) using the trapezoidal rules.
    • +
    • The trapezoidal rule
    • +
    +$$ + I=\int_a^bf(x) dx\approx h\left(f(a)/2 + f(a+h) +f(a+2h)+\dots +f(b-h)+ f(b)/2\right). +$$ -

    -Which thread does which values? Several options. +

    Click on this link for the full program.

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs095.html b/doc/pub/week9/html/._week9-bs095.html index 54e900b7..a463281d 100644 --- a/doc/pub/week9/html/._week9-bs095.html +++ b/doc/pub/week9/html/._week9-bs095.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Scheduling of loop computations

    - -

    +

    Dissection of trapezoidal rule with \( MPI\_reduce \)

    -

    -We can let the OpenMP runtime decide. The decision is about how the loop iterates are scheduled -and OpenMP defines three choices of loop scheduling: - -

      -
    1. Static: Predefined at compile time. Lowest overhead, predictable
    2. -
    3. Dynamic: Selection made at runtime
    4. -
    5. Guided: Special case of dynamic; attempts to reduce overhead
    6. -
    + + + + +
    +
    +
    +
    +
    +
    //    Trapezoidal rule and numerical integration usign MPI
    +using namespace std;
    +#include <mpi.h>
    +#include <iostream>
    +
    +//     Here we define various functions called by the main program
    +
    +double int_function(double );
    +double trapezoidal_rule(double , double , int , double (*)(double));
    +
    +//   Main function begins here
    +int main (int nargs, char* args[])
    +{
    +  int n, local_n, numprocs, my_rank; 
    +  double a, b, h, local_a, local_b, total_sum, local_sum;   
    +  double  time_start, time_end, total_time;
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs096.html b/doc/pub/week9/html/._week9-bs096.html index 35d1368f..4f371e52 100644 --- a/doc/pub/week9/html/._week9-bs096.html +++ b/doc/pub/week9/html/._week9-bs096.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Example code for loop scheduling

    +

    Dissection of trapezoidal rule

    -

    -

    + + -

    #include <omp.h>
    -#define CHUNKSIZE 100
    -#define N 1000
    -int main (int argc, char *argv[])
    -{
    -int i, chunk;
    -float a[N], b[N], c[N];
    -for (i=0; i < N; i++) a[i] = b[i] = i * 1.0;
    -chunk = CHUNKSIZE;
    -#pragma omp parallel shared(a,b,c,chunk) private(i)
    -{
    -#pragma omp for schedule(dynamic,chunk)
    -for (i=0; i < N; i++) c[i] = a[i] + b[i];
    -} /* end of parallel region */
    -}
    -
    -

    +

    +
    +
    +
    +
    +
      //  MPI initializations
    +  MPI_Init (&nargs, &args);
    +  MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
    +  MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);
    +  time_start = MPI_Wtime();
    +  //  Fixed values for a, b and n 
    +  a = 0.0 ; b = 1.0;  n = 1000;
    +  h = (b-a)/n;    // h is the same for all processes 
    +  local_n = n/numprocs;  
    +  // make sure n > numprocs, else integer division gives zero
    +  // Length of each process' interval of
    +  // integration = local_n*h.  
    +  local_a = a + my_rank*local_n*h;
    +  local_b = local_a + local_n*h;
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs097.html b/doc/pub/week9/html/._week9-bs097.html index 11206705..2e93defe 100644 --- a/doc/pub/week9/html/._week9-bs097.html +++ b/doc/pub/week9/html/._week9-bs097.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Example code for loop scheduling, guided instead of dynamic

    +

    Integrating with MPI

    -

    -

    + + -

    #include <omp.h>
    -#define CHUNKSIZE 100
    -#define N 1000
    -int main (int argc, char *argv[])
    -{
    -int i, chunk;
    -float a[N], b[N], c[N];
    -for (i=0; i < N; i++) a[i] = b[i] = i * 1.0;
    -chunk = CHUNKSIZE;
    -#pragma omp parallel shared(a,b,c,chunk) private(i)
    -{
    -#pragma omp for schedule(guided,chunk)
    -for (i=0; i < N; i++) c[i] = a[i] + b[i];
    -} /* end of parallel region */
    -}
    -
    -

    +

    +
    +
    +
    +
    +
      total_sum = 0.0;
    +  local_sum = trapezoidal_rule(local_a, local_b, local_n, 
    +                               &int_function); 
    +  MPI_Reduce(&local_sum, &total_sum, 1, MPI_DOUBLE, 
    +              MPI_SUM, 0, MPI_COMM_WORLD);
    +  time_end = MPI_Wtime();
    +  total_time = time_end-time_start;
    +  if ( my_rank == 0) {
    +    cout << "Trapezoidal rule = " <<  total_sum << endl;
    +    cout << "Time = " <<  total_time  
    +         << " on number of processors: "  << numprocs  << endl;
    +  }
    +  // End MPI
    +  MPI_Finalize ();  
    +  return 0;
    +}  // end of main program
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs098.html b/doc/pub/week9/html/._week9-bs098.html index 10d25551..896e921e 100644 --- a/doc/pub/week9/html/._week9-bs098.html +++ b/doc/pub/week9/html/._week9-bs098.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    More on Parallel for loop

    +

    How do I use \( MPI\_reduce \)?

    -

    + -

      -
    • The number of loop iterations cannot be non-deterministic; break, return, exit, goto not allowed inside the for-loop
    • -
    • The loop index is private to each thread
    • -
    • A reduction variable is special
    • - -
        -
      • During the for-loop there is a local private copy in each thread
      • -
      • At the end of the for-loop, all the local copies are combined together by the reduction operation
      • -
      - -
    • Unless the nowait clause is used, an implicit barrier synchronization will be added at the end by the compiler
    • -
    - -

    +

    Here we have used

    -
    // #pragma omp parallel and #pragma omp for
    -
    -

    -can be combined into -

    +

    +
    +
    +
    +
    +
    MPI_reduce( void *senddata, void* resultdata, int count, 
    +     MPI_Datatype datatype, MPI_Op, int root, MPI_Comm comm)
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    - -
    #pragma omp parallel for
    -
    -

    +

    The two variables \( senddata \) and \( resultdata \) are obvious, besides the fact that one sends the address +of the variable or the first element of an array. If they are arrays they need to have the same size. +The variable \( count \) represents the total dimensionality, 1 in case of just one variable, +while \( MPI\_Datatype \) +defines the type of variable which is sent and received. +

    + +

    The new feature is \( MPI\_Op \). It defines the type +of operation we want to do. +

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs099.html b/doc/pub/week9/html/._week9-bs099.html index 233ad1b7..5f9228b0 100644 --- a/doc/pub/week9/html/._week9-bs099.html +++ b/doc/pub/week9/html/._week9-bs099.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    What can happen with this loop?

    - -

    +

    More on \( MPI\_Reduce \)

    -

    -What happens with code like this -

    + +

    In our case, since we are summing +the rectangle contributions from every process we define \( MPI\_Op = MPI\_SUM \). +If we have an array or matrix we can search for the largest og smallest element by sending either \( MPI\_MAX \) or +\( MPI\_MIN \). If we want the location as well (which array element) we simply transfer +\( MPI\_MAXLOC \) or \( MPI\_MINOC \). If we want the product we write \( MPI\_PROD \). +

    - -
    #pragma omp parallel for
    -for (i=0; i<n; i++) sum += a[i]*a[i];
    -
    -

    -All threads can access the sum variable, but the addition is not atomic! It is important to avoid race between threads. So-called reductions in OpenMP are thus important for performance and for obtaining correct results. OpenMP lets us indicate that a variable is used for a reduction with a particular operator. The above code becomes -

    +

    \( MPI\_Allreduce \) is defined as

    -
    sum = 0.0;
    -#pragma omp parallel for reduction(+:sum)
    -for (i=0; i<n; i++) sum += a[i]*a[i];
    -
    -

    +

    +
    +
    +
    +
    +
    MPI_Allreduce( void *senddata, void* resultdata, int count, 
    +          MPI_Datatype datatype, MPI_Op, MPI_Comm comm)        
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs100.html b/doc/pub/week9/html/._week9-bs100.html index 58f59b5b..e4e70a3c 100644 --- a/doc/pub/week9/html/._week9-bs100.html +++ b/doc/pub/week9/html/._week9-bs100.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Inner product

    +

    Dissection of trapezoidal rule

    -

    -$$ -\sum_{i=0}^{n-1} a_ib_i -$$ + -

    +

    We use \( MPI\_reduce \) to collect data from each process. Note also the use of the function +\( MPI\_Wtime \). +

    -
    int i;
    -double sum = 0.;
    -/* allocating and initializing arrays */
    -/* ... */
    -#pragma omp parallel for default(shared) private(i) reduction(+:sum)
    - for (i=0; i<N; i++) sum += a[i]*b[i];
    -}
    -
    -

    +

    +
    +
    +
    +
    +
    //  this function defines the function to integrate
    +double int_function(double x)
    +{
    +  double value = 4./(1.+x*x);
    +  return value;
    +} // end of function to evaluate
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs101.html b/doc/pub/week9/html/._week9-bs101.html index 096b34fe..6370ee56 100644 --- a/doc/pub/week9/html/._week9-bs101.html +++ b/doc/pub/week9/html/._week9-bs101.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Different threads do different tasks

    +

    Dissection of trapezoidal rule

    -

    - -

    -Different threads do different tasks independently, each section is executed by one thread. -

    + -

    #pragma omp parallel
    -{
    -#pragma omp sections
    -{
    -#pragma omp section
    -funcA ();
    -#pragma omp section
    -funcB ();
    -#pragma omp section
    -funcC ();
    -}
    -}
    -
    -

    +

    +
    +
    +
    +
    +
    //  this function defines the trapezoidal rule
    +double trapezoidal_rule(double a, double b, int n, 
    +                         double (*func)(double))
    +{
    +  double trapez_sum;
    +  double fa, fb, x, step;
    +  int    j;
    +  step=(b-a)/((double) n);
    +  fa=(*func)(a)/2. ;
    +  fb=(*func)(b)/2. ;
    +  trapez_sum=0.;
    +  for (j=1; j <= n-1; j++){
    +    x=j*step+a;
    +    trapez_sum+=(*func)(x);
    +  }
    +  trapez_sum=(trapez_sum+fb+fa)*step;
    +  return trapez_sum;
    +}  // end trapezoidal_rule 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs102.html b/doc/pub/week9/html/._week9-bs102.html index 2de362c9..e3f6cc51 100644 --- a/doc/pub/week9/html/._week9-bs102.html +++ b/doc/pub/week9/html/._week9-bs102.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Single execution

    +

    The quantum dot program for two electrons

    -

    -

    + -

    #pragma omp single { ... }
    -
    -

    -The code is executed by one thread only, no guarantee which thread +

    +
    +
    +
    +
    +
    // Variational Monte Carlo for atoms with importance sampling, slater det
    +// Test case for 2-electron quantum dot, no classes using Mersenne-Twister RNG
    +#include "mpi.h"
    +#include <cmath>
    +#include <random>
    +#include <string>
    +#include <iostream>
    +#include <fstream>
    +#include <iomanip>
    +#include "vectormatrixclass.h"
     
    -

    -Can introduce an implicit barrier at the end -

    +using namespace std; +// output file as global variable +ofstream ofile; +// the step length and its squared inverse for the second derivative +// Here we define global variables used in various functions +// These can be changed by using classes +int Dimension = 2; +int NumberParticles = 2; // we fix also the number of electrons to be 2 - -

    #pragma omp master { ... }
    -
    -

    -Code executed by the master thread, guaranteed and no implicit barrier at the end. +// declaration of functions + +// The Mc sampling for the variational Monte Carlo +void MonteCarloSampling(int, double &, double &, Vector &); + +// The variational wave function +double WaveFunction(Matrix &, Vector &); + +// The local energy +double LocalEnergy(Matrix &, Vector &); + +// The quantum force +void QuantumForce(Matrix &, Matrix &, Vector &); + + +// inline function for single-particle wave function +inline double SPwavefunction(double r, double alpha) { + return exp(-alpha*r*0.5); +} + +// inline function for derivative of single-particle wave function +inline double DerivativeSPwavefunction(double r, double alpha) { + return -r*alpha; +} + +// function for absolute value of relative distance +double RelativeDistance(Matrix &r, int i, int j) { + double r_ij = 0; + for (int k = 0; k < Dimension; k++) { + r_ij += (r(i,k)-r(j,k))*(r(i,k)-r(j,k)); + } + return sqrt(r_ij); +} + +// inline function for derivative of Jastrow factor +inline double JastrowDerivative(Matrix &r, double beta, int i, int j, int k){ + return (r(i,k)-r(j,k))/(RelativeDistance(r, i, j)*pow(1.0+beta*RelativeDistance(r, i, j),2)); +} + +// function for square of position of single particle +double singleparticle_pos2(Matrix &r, int i) { + double r_single_particle = 0; + for (int j = 0; j < Dimension; j++) { + r_single_particle += r(i,j)*r(i,j); + } + return r_single_particle; +} + +void lnsrch(int n, Vector &xold, double fold, Vector &g, Vector &p, Vector &x, + double *f, double stpmax, int *check, double (*func)(Vector &p)); + +void dfpmin(Vector &p, int n, double gtol, int *iter, double *fret, + double(*func)(Vector &p), void (*dfunc)(Vector &p, Vector &g)); + +static double sqrarg; +#define SQR(a) ((sqrarg=(a)) == 0.0 ? 0.0 : sqrarg*sqrarg) + + +static double maxarg1,maxarg2; +#define FMAX(a,b) (maxarg1=(a),maxarg2=(b),(maxarg1) > (maxarg2) ?\ + (maxarg1) : (maxarg2)) + + +// Begin of main program + +int main(int argc, char* argv[]) +{ + + // MPI initializations + int NumberProcesses, MyRank, NumberMCsamples; + MPI_Init (&argc, &argv); + MPI_Comm_size (MPI_COMM_WORLD, &NumberProcesses); + MPI_Comm_rank (MPI_COMM_WORLD, &MyRank); + double StartTime = MPI_Wtime(); + if (MyRank == 0 && argc <= 1) { + cout << "Bad Usage: " << argv[0] << + " Read also output file on same line and number of Monte Carlo cycles" << endl; + } + // Read filename and number of Monte Carlo cycles from the command line + if (MyRank == 0 && argc > 2) { + string filename = argv[1]; // first command line argument after name of program + NumberMCsamples = atoi(argv[2]); + string fileout = filename; + string argument = to_string(NumberMCsamples); + // Final filename as filename+NumberMCsamples + fileout.append(argument); + ofile.open(fileout); + } + // broadcast the number of Monte Carlo samples + MPI_Bcast (&NumberMCsamples, 1, MPI_INT, 0, MPI_COMM_WORLD); + // Two variational parameters only + Vector VariationalParameters(2); + int TotalNumberMCsamples = NumberMCsamples*NumberProcesses; + // Loop over variational parameters + for (double alpha = 0.5; alpha <= 1.5; alpha +=0.1){ + for (double beta = 0.1; beta <= 0.5; beta +=0.05){ + VariationalParameters(0) = alpha; // value of alpha + VariationalParameters(1) = beta; // value of beta + // Do the mc sampling and accumulate data with MPI_Reduce + double TotalEnergy, TotalEnergySquared, LocalProcessEnergy, LocalProcessEnergy2; + LocalProcessEnergy = LocalProcessEnergy2 = 0.0; + MonteCarloSampling(NumberMCsamples, LocalProcessEnergy, LocalProcessEnergy2, VariationalParameters); + // Collect data in total averages + MPI_Reduce(&LocalProcessEnergy, &TotalEnergy, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); + MPI_Reduce(&LocalProcessEnergy2, &TotalEnergySquared, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); + // Print out results in case of Master node, set to MyRank = 0 + if ( MyRank == 0) { + double Energy = TotalEnergy/( (double)NumberProcesses); + double Variance = TotalEnergySquared/( (double)NumberProcesses)-Energy*Energy; + double StandardDeviation = sqrt(Variance/((double)TotalNumberMCsamples)); // over optimistic error + ofile << setiosflags(ios::showpoint | ios::uppercase); + ofile << setw(15) << setprecision(8) << VariationalParameters(0); + ofile << setw(15) << setprecision(8) << VariationalParameters(1); + ofile << setw(15) << setprecision(8) << Energy; + ofile << setw(15) << setprecision(8) << Variance; + ofile << setw(15) << setprecision(8) << StandardDeviation << endl; + } + } + } + double EndTime = MPI_Wtime(); + double TotalTime = EndTime-StartTime; + if ( MyRank == 0 ) cout << "Time = " << TotalTime << " on number of processors: " << NumberProcesses << endl; + if (MyRank == 0) ofile.close(); // close output file + // End MPI + MPI_Finalize (); + return 0; +} // end of main function + + +// Monte Carlo sampling with the Metropolis algorithm + +void MonteCarloSampling(int NumberMCsamples, double &cumulative_e, double &cumulative_e2, Vector &VariationalParameters) +{ + + // Initialize the seed and call the Mersienne algo + std::random_device rd; + std::mt19937_64 gen(rd()); + // Set up the uniform distribution for x \in [[0, 1] + std::uniform_real_distribution<double> UniformNumberGenerator(0.0,1.0); + std::normal_distribution<double> Normaldistribution(0.0,1.0); + // diffusion constant from Schroedinger equation + double D = 0.5; + double timestep = 0.05; // we fix the time step for the gaussian deviate + // allocate matrices which contain the position of the particles + Matrix OldPosition( NumberParticles, Dimension), NewPosition( NumberParticles, Dimension); + Matrix OldQuantumForce(NumberParticles, Dimension), NewQuantumForce(NumberParticles, Dimension); + double Energy = 0.0; double EnergySquared = 0.0; double DeltaE = 0.0; + // initial trial positions + for (int i = 0; i < NumberParticles; i++) { + for (int j = 0; j < Dimension; j++) { + OldPosition(i,j) = Normaldistribution(gen)*sqrt(timestep); + } + } + double OldWaveFunction = WaveFunction(OldPosition, VariationalParameters); + QuantumForce(OldPosition, OldQuantumForce, VariationalParameters); + // loop over monte carlo cycles + for (int cycles = 1; cycles <= NumberMCsamples; cycles++){ + // new position + for (int i = 0; i < NumberParticles; i++) { + for (int j = 0; j < Dimension; j++) { + // gaussian deviate to compute new positions using a given timestep + NewPosition(i,j) = OldPosition(i,j) + Normaldistribution(gen)*sqrt(timestep)+OldQuantumForce(i,j)*timestep*D; + // NewPosition(i,j) = OldPosition(i,j) + gaussian_deviate(&idum)*sqrt(timestep)+OldQuantumForce(i,j)*timestep*D; + } + // for the other particles we need to set the position to the old position since + // we move only one particle at the time + for (int k = 0; k < NumberParticles; k++) { + if ( k != i) { + for (int j = 0; j < Dimension; j++) { + NewPosition(k,j) = OldPosition(k,j); + } + } + } + double NewWaveFunction = WaveFunction(NewPosition, VariationalParameters); + QuantumForce(NewPosition, NewQuantumForce, VariationalParameters); + // we compute the log of the ratio of the greens functions to be used in the + // Metropolis-Hastings algorithm + double GreensFunction = 0.0; + for (int j = 0; j < Dimension; j++) { + GreensFunction += 0.5*(OldQuantumForce(i,j)+NewQuantumForce(i,j))* + (D*timestep*0.5*(OldQuantumForce(i,j)-NewQuantumForce(i,j))-NewPosition(i,j)+OldPosition(i,j)); + } + GreensFunction = exp(GreensFunction); + // The Metropolis test is performed by moving one particle at the time + if(UniformNumberGenerator(gen) <= GreensFunction*NewWaveFunction*NewWaveFunction/OldWaveFunction/OldWaveFunction ) { + for (int j = 0; j < Dimension; j++) { + OldPosition(i,j) = NewPosition(i,j); + OldQuantumForce(i,j) = NewQuantumForce(i,j); + } + OldWaveFunction = NewWaveFunction; + } + } // end of loop over particles + // compute local energy + double DeltaE = LocalEnergy(OldPosition, VariationalParameters); + // update energies + Energy += DeltaE; + EnergySquared += DeltaE*DeltaE; + } // end of loop over MC trials + // update the energy average and its squared + cumulative_e = Energy/NumberMCsamples; + cumulative_e2 = EnergySquared/NumberMCsamples; +} // end MonteCarloSampling function + + +// Function to compute the squared wave function and the quantum force + +double WaveFunction(Matrix &r, Vector &VariationalParameters) +{ + double wf = 0.0; + // full Slater determinant for two particles, replace with Slater det for more particles + wf = SPwavefunction(singleparticle_pos2(r, 0), VariationalParameters(0))*SPwavefunction(singleparticle_pos2(r, 1),VariationalParameters(0)); + // contribution from Jastrow factor + for (int i = 0; i < NumberParticles-1; i++) { + for (int j = i+1; j < NumberParticles; j++) { + wf *= exp(RelativeDistance(r, i, j)/((1.0+VariationalParameters(1)*RelativeDistance(r, i, j)))); + } + } + return wf; +} + +// Function to calculate the local energy without numerical derivation of kinetic energy + +double LocalEnergy(Matrix &r, Vector &VariationalParameters) +{ + + // compute the kinetic and potential energy from the single-particle part + // for a many-electron system this has to be replaced by a Slater determinant + // The absolute value of the interparticle length + Matrix length( NumberParticles, NumberParticles); + // Set up interparticle distance + for (int i = 0; i < NumberParticles-1; i++) { + for(int j = i+1; j < NumberParticles; j++){ + length(i,j) = RelativeDistance(r, i, j); + length(j,i) = length(i,j); + } + } + double KineticEnergy = 0.0; + // Set up kinetic energy from Slater and Jastrow terms + for (int i = 0; i < NumberParticles; i++) { + for (int k = 0; k < Dimension; k++) { + double sum1 = 0.0; + for(int j = 0; j < NumberParticles; j++){ + if ( j != i) { + sum1 += JastrowDerivative(r, VariationalParameters(1), i, j, k); + } + } + KineticEnergy += (sum1+DerivativeSPwavefunction(r(i,k),VariationalParameters(0)))*(sum1+DerivativeSPwavefunction(r(i,k),VariationalParameters(0))); + } + } + KineticEnergy += -2*VariationalParameters(0)*NumberParticles; + for (int i = 0; i < NumberParticles-1; i++) { + for (int j = i+1; j < NumberParticles; j++) { + KineticEnergy += 2.0/(pow(1.0 + VariationalParameters(1)*length(i,j),2))*(1.0/length(i,j)-2*VariationalParameters(1)/(1+VariationalParameters(1)*length(i,j)) ); + } + } + KineticEnergy *= -0.5; + // Set up potential energy, external potential + eventual electron-electron repulsion + double PotentialEnergy = 0; + for (int i = 0; i < NumberParticles; i++) { + double DistanceSquared = singleparticle_pos2(r, i); + PotentialEnergy += 0.5*DistanceSquared; // sp energy HO part, note it has the oscillator frequency set to 1! + } + // Add the electron-electron repulsion + for (int i = 0; i < NumberParticles-1; i++) { + for (int j = i+1; j < NumberParticles; j++) { + PotentialEnergy += 1.0/length(i,j); + } + } + double LocalE = KineticEnergy+PotentialEnergy; + return LocalE; +} + +// Compute the analytical expression for the quantum force +void QuantumForce(Matrix &r, Matrix &qforce, Vector &VariationalParameters) +{ + // compute the first derivative + for (int i = 0; i < NumberParticles; i++) { + for (int k = 0; k < Dimension; k++) { + // single-particle part, replace with Slater det for larger systems + double sppart = DerivativeSPwavefunction(r(i,k),VariationalParameters(0)); + // Jastrow factor contribution + double Jsum = 0.0; + for (int j = 0; j < NumberParticles; j++) { + if ( j != i) { + Jsum += JastrowDerivative(r, VariationalParameters(1), i, j, k); + } + } + qforce(i,k) = 2.0*(Jsum+sppart); + } + } +} // end of QuantumForce function + + +#define ITMAX 200 +#define EPS 3.0e-8 +#define TOLX (4*EPS) +#define STPMX 100.0 + +void dfpmin(Vector &p, int n, double gtol, int *iter, double *fret, + double(*func)(Vector &p), void (*dfunc)(Vector &p, Vector &g)) +{ + + int check,i,its,j; + double den,fac,fad,fae,fp,stpmax,sum=0.0,sumdg,sumxi,temp,test; + Vector dg(n), g(n), hdg(n), pnew(n), xi(n); + Matrix hessian(n,n); + + fp=(*func)(p); + (*dfunc)(p,g); + for (i = 0;i < n;i++) { + for (j = 0; j< n;j++) hessian(i,j)=0.0; + hessian(i,i)=1.0; + xi(i) = -g(i); + sum += p(i)*p(i); + } + stpmax=STPMX*FMAX(sqrt(sum),(double)n); + for (its=1;its<=ITMAX;its++) { + *iter=its; + lnsrch(n,p,fp,g,xi,pnew,fret,stpmax,&check,func); + fp = *fret; + for (i = 0; i< n;i++) { + xi(i)=pnew(i)-p(i); + p(i)=pnew(i); + } + test=0.0; + for (i = 0;i< n;i++) { + temp=fabs(xi(i))/FMAX(fabs(p(i)),1.0); + if (temp > test) test=temp; + } + if (test < TOLX) { + return; + } + for (i=0;i<n;i++) dg(i)=g(i); + (*dfunc)(p,g); + test=0.0; + den=FMAX(*fret,1.0); + for (i=0;i<n;i++) { + temp=fabs(g(i))*FMAX(fabs(p(i)),1.0)/den; + if (temp > test) test=temp; + } + if (test < gtol) { + return; + } + for (i=0;i<n;i++) dg(i)=g(i)-dg(i); + for (i=0;i<n;i++) { + hdg(i)=0.0; + for (j=0;j<n;j++) hdg(i) += hessian(i,j)*dg(j); + } + fac=fae=sumdg=sumxi=0.0; + for (i=0;i<n;i++) { + fac += dg(i)*xi(i); + fae += dg(i)*hdg(i); + sumdg += SQR(dg(i)); + sumxi += SQR(xi(i)); + } + if (fac*fac > EPS*sumdg*sumxi) { + fac=1.0/fac; + fad=1.0/fae; + for (i=0;i<n;i++) dg(i)=fac*xi(i)-fad*hdg(i); + for (i=0;i<n;i++) { + for (j=0;j<n;j++) { + hessian(i,j) += fac*xi(i)*xi(j) + -fad*hdg(i)*hdg(j)+fae*dg(i)*dg(j); + } + } + } + for (i=0;i<n;i++) { + xi(i)=0.0; + for (j=0;j<n;j++) xi(i) -= hessian(i,j)*g(j); + } + } + cout << "too many iterations in dfpmin" << endl; +} +#undef ITMAX +#undef EPS +#undef TOLX +#undef STPMX + +#define ALF 1.0e-4 +#define TOLX 1.0e-7 + +void lnsrch(int n, Vector &xold, double fold, Vector &g, Vector &p, Vector &x, + double *f, double stpmax, int *check, double (*func)(Vector &p)) +{ + int i; + double a,alam,alam2,alamin,b,disc,f2,fold2,rhs1,rhs2,slope,sum,temp, + test,tmplam; + + *check=0; + for (sum=0.0,i=0;i<n;i++) sum += p(i)*p(i); + sum=sqrt(sum); + if (sum > stpmax) + for (i=0;i<n;i++) p(i) *= stpmax/sum; + for (slope=0.0,i=0;i<n;i++) + slope += g(i)*p(i); + test=0.0; + for (i=0;i<n;i++) { + temp=fabs(p(i))/FMAX(fabs(xold(i)),1.0); + if (temp > test) test=temp; + } + alamin=TOLX/test; + alam=1.0; + for (;;) { + for (i=0;i<n;i++) x(i)=xold(i)+alam*p(i); + *f=(*func)(x); + if (alam < alamin) { + for (i=0;i<n;i++) x(i)=xold(i); + *check=1; + return; + } else if (*f <= fold+ALF*alam*slope) return; + else { + if (alam == 1.0) + tmplam = -slope/(2.0*(*f-fold-slope)); + else { + rhs1 = *f-fold-alam*slope; + rhs2=f2-fold2-alam2*slope; + a=(rhs1/(alam*alam)-rhs2/(alam2*alam2))/(alam-alam2); + b=(-alam2*rhs1/(alam*alam)+alam*rhs2/(alam2*alam2))/(alam-alam2); + if (a == 0.0) tmplam = -slope/(2.0*b); + else { + disc=b*b-3.0*a*slope; + if (disc<0.0) cout << "Roundoff problem in lnsrch." << endl; + else tmplam=(-b+sqrt(disc))/(3.0*a); + } + if (tmplam>0.5*alam) + tmplam=0.5*alam; + } + } + alam2=alam; + f2 = *f; + fold2=fold; + alam=FMAX(tmplam,0.1*alam); + } +} +#undef ALF +#undef TOLX +

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs103.html b/doc/pub/week9/html/._week9-bs103.html index 295e8307..8fac6fa4 100644 --- a/doc/pub/week9/html/._week9-bs103.html +++ b/doc/pub/week9/html/._week9-bs103.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Coordination and synchronization

    +

    What is OpenMP

    -

    -

    - - -

    #pragma omp barrier
    -
    -

    -Synchronization, must be encountered by all threads in a team (or none) -

    - - -

    #pragma omp ordered { a block of codes }
    -
    -

    -is another form of synchronization (in sequential order). -The form -

    - - -

    #pragma omp critical { a block of codes }
    -
    -

    -and -

    - - -

    #pragma omp atomic { single assignment statement }
    -
    -

    -is more efficient than -

    - - -

    #pragma omp critical
    -
    -

    + +

      +
    • OpenMP provides high-level thread programming
    • +
    • Multiple cooperating threads are allowed to run simultaneously
    • +
    • Threads are created and destroyed dynamically in a fork-join pattern
    • +
        +
      • An OpenMP program consists of a number of parallel regions
      • +
      • Between two parallel regions there is only one master thread
      • +
      • In the beginning of a parallel region, a team of new threads is spawned
      • +
      +
    • The newly spawned threads work simultaneously with the master thread
    • +
    • At the end of a parallel region, the new threads are destroyed
    • +
    +

    Many good tutorials online and excellent textbook

    +
      +
    1. Using OpenMP, by B. Chapman, G. Jost, and A. van der Pas
    2. +
    3. Many tutorials online like OpenMP official site
    4. +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs104.html b/doc/pub/week9/html/._week9-bs104.html index 549d7a3d..6e219014 100644 --- a/doc/pub/week9/html/._week9-bs104.html +++ b/doc/pub/week9/html/._week9-bs104.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Data scope

    +

    Getting started, things to remember

    -

    - -

      -
    • OpenMP data scope attribute clauses:
    • - +
        -
      • shared
      • -
      • private
      • -
      • firstprivate
      • -
      • lastprivate
      • -
      • reduction
      • +
      • Remember the header file
      + +
      +
      +
      +
      +
      +
      #include <omp.h>
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
        +
      • Insert compiler directives in C++ syntax as
      -What are the purposes of these attributes + +
      +
      +
      +
      +
      +
      #pragma omp...
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
        -
      • define how and which variables are transferred to a parallel region (and back)
      • -
      • define which variables are visible to all threads in a parallel region, and which variables are privately allocated to each thread
      • +
      • Compile with for example c++ -fopenmp code.cpp
      • +
      • Execute
      • +
          +
        • Remember to assign the environment variable OMP NUM THREADS
        • +
        • It specifies the total number of threads inside a parallel region, if not otherwise overwritten
        • +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs105.html b/doc/pub/week9/html/._week9-bs105.html index 6443e423..13aecc4f 100644 --- a/doc/pub/week9/html/._week9-bs105.html +++ b/doc/pub/week9/html/._week9-bs105.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - +

    OpenMP syntax

    + -

    Some remarks

    -
    -
    -

    + +

    +
    +
    +
    +
    +
    #pragma omp construct [ clause ...]
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
      -
    • When entering a parallel region, the private clause ensures each thread having its own new variable instances. The new variables are assumed to be uninitialized.
    • -
    • A shared variable exists in only one memory location and all threads can read and write to that address. It is the programmer's responsibility to ensure that multiple threads properly access a shared variable.
    • -
    • The firstprivate clause combines the behavior of the private clause with automatic initialization.
    • -
    • The lastprivate clause combines the behavior of the private clause with a copy back (from the last loop iteration or section) to the original variable outside the parallel region.
    • +
    • Some functions and types
    + + +
    +
    +
    +
    +
    +
    #include <omp.h>
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    - -

    +

      +
    • Most apply to a block of code
    • +
    • Specifically, a structured block
    • +
    • Enter at top, exit at bottom only, exit(), abort() permitted
    • +

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs106.html b/doc/pub/week9/html/._week9-bs106.html index 8a22e844..85a3adf4 100644 --- a/doc/pub/week9/html/._week9-bs106.html +++ b/doc/pub/week9/html/._week9-bs106.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - +

    Different OpenMP styles of parallelism

    +

    OpenMP supports several different ways to specify thread parallelism

    -

    Parallelizing nested for-loops

    -
    -
    -

    - -

      -
    • Serial code
    • -
    - -

    - - -

    for (i=0; i<100; i++)
    -    for (j=0; j<100; j++)
    -        a[i][j] = b[i][j] + c[i][j];
    -    }
    -}
    -
      -
    • Parallelization
    • +
    • General parallel regions: All threads execute the code, roughly as if you made a routine of that region and created a thread to run that code
    • +
    • Parallel loops: Special case for loops, simplifies data parallel code
    • +
    • Task parallelism, new in OpenMP 3
    • +
    • Several ways to manage thread coordination, including Master regions and Locks
    • +
    • Memory model for shared data
    - -

    - - -

    #pragma omp parallel for private(j)
    -for (i=0; i<100; i++)
    -    for (j=0; j<100; j++)
    -       a[i][j] = b[i][j] + c[i][j];
    -    }
    -}
    -
    -
      -
    • Why not parallelize the inner loop? to save overhead of repeated thread forks-joins
    • -
    • Why must j be private? To avoid race condition among the threads
    • -
    -
    -
    - - -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs107.html b/doc/pub/week9/html/._week9-bs107.html index a6f36bb5..83cdccb7 100644 --- a/doc/pub/week9/html/._week9-bs107.html +++ b/doc/pub/week9/html/._week9-bs107.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - -
    -
    -

     

     

     

    - - -

    Nested parallelism

    +

    General code structure

    -

    -When a thread in a parallel region encounters another parallel construct, it -may create a new team of threads and become the master of the new -team. -

    + -

    #pragma omp parallel num_threads(4)
    -{
    -/* .... */
    -#pragma omp parallel num_threads(2)
    -{
    -//  
    -}
    -}
    -
    -

    +

    +
    +
    +
    +
    +
    #include <omp.h>
    +main ()
    +{
    +int var1, var2, var3;
    +/* serial code */
    +/* ... */
    +/* start of a parallel region */
    +#pragma omp parallel private(var1, var2) shared(var3)
    +{
    +/* ... */
    +}
    +/* more serial code */
    +/* ... */
    +/* another parallel region */
    +#pragma omp parallel
    +{
    +/* ... */
    +}
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs108.html b/doc/pub/week9/html/._week9-bs108.html index 1f75de14..8302350d 100644 --- a/doc/pub/week9/html/._week9-bs108.html +++ b/doc/pub/week9/html/._week9-bs108.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Parallel tasks

    +

    Parallel region

    -

    -

    + +

      +
    • A parallel region is a block of code that is executed by a team of threads
    • +
    • The following compiler directive creates a parallel region
    • +
    -
    #pragma omp task 
    -#pragma omp parallel shared(p_vec) private(i)
    -{
    -#pragma omp single
    -{
    -for (i=0; i<N; i++) {
    -  double r = random_number();
    -  if (p_vec[i] > r) {
    -#pragma omp task
    -   do_work (p_vec[i]);
    -
    -

    +

    +
    +
    +
    +
    +
    #pragma omp parallel { ... }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +
    • Clauses can be added at the end of the directive
    • +
    • Most often used clauses:
    • +
        +
      • default(shared) or default(none)
      • +
      • public(list of variables)
      • +
      • private(list of variables)
      • +
      +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs109.html b/doc/pub/week9/html/._week9-bs109.html index 8b373337..0535ac7e 100644 --- a/doc/pub/week9/html/._week9-bs109.html +++ b/doc/pub/week9/html/._week9-bs109.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Common mistakes

    +

    Hello world, not again, please!

    -

    -Race condition -

    + -

    int nthreads;
    -#pragma omp parallel shared(nthreads)
    -{
    -nthreads = omp_get_num_threads();
    -}
    -
    -

    -Deadlock -

    - - -

    #pragma omp parallel
    -{
    -...
    -#pragma omp critical
    -{
    -...
    +
    +
    +
    +
    +
    +
    #include <omp.h>
    +#include <cstdio>
    +int main (int argc, char *argv[])
    +{
    +int th_id, nthreads;
    +#pragma omp parallel private(th_id) shared(nthreads)
    +{
    +th_id = omp_get_thread_num();
    +printf("Hello World from thread %d\n", th_id);
     #pragma omp barrier
    -}
    -}
    -
    -

    +if ( th_id == 0 ) { +nthreads = omp_get_num_threads(); +printf("There are %d threads\n",nthreads); +} +} +return 0; +} +

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + -

    - - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs110.html b/doc/pub/week9/html/._week9-bs110.html index e74a6aad..9be952fd 100644 --- a/doc/pub/week9/html/._week9-bs110.html +++ b/doc/pub/week9/html/._week9-bs110.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - - -

    Not all computations are simple

    + +

    Hello world, yet another variant

    -

    -Not all computations are simple loops where the data can be evenly -divided among threads without any dependencies between threads + -

    -An example is finding the location and value of the largest element in an array -

    + +

    +
    +
    +
    +
    +
    #include <cstdio>
    +#include <omp.h>
    +int main(int argc, char *argv[]) 
    +{
    + omp_set_num_threads(4); 
    +#pragma omp parallel
    + {
    +   int id = omp_get_thread_num();
    +   int nproc = omp_get_num_threads(); 
    +   cout << "Hello world with id number and processes " <<  id <<  nproc << endl;
    + } 
    +return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Variables declared outside of the parallel region are shared by all threads +If a variable like id is declared outside of the +

    -
    for (i=0; i<n; i++) { 
    -   if (x[i] > maxval) {
    -      maxval = x[i];
    -      maxloc = i; 
    -   }
    -}
    -
    -

    +

    +
    +
    +
    +
    +
    #pragma omp parallel, 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    it would have been shared by various the threads, possibly causing erroneous output

    +
      +
    • Why? What would go wrong? Why do we add possibly?
    • +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs111.html b/doc/pub/week9/html/._week9-bs111.html index 14c46bf5..f62dcabf 100644 --- a/doc/pub/week9/html/._week9-bs111.html +++ b/doc/pub/week9/html/._week9-bs111.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - - -

    Not all computations are simple, competing threads

    + +

    Important OpenMP library routines

    -

    -All threads are potentially accessing and changing the same values, maxloc and maxval. - -

      -
    1. OpenMP provides several ways to coordinate access to shared values
    2. -
    - -

    - - -

    #pragma omp atomic
    -
    -
      -
    1. Only one thread at a time can execute the following statement (not block). We can use the critical option
    2. -
    + -

    - - -

    #pragma omp critical
    -
    -
      -
    1. Only one thread at a time can execute the following block
    2. -
    - -Atomic may be faster than critical but depends on hardware +
      +
    • int omp get num threads (), returns the number of threads inside a parallel region
    • +
    • int omp get thread num (), returns the a thread for each thread inside a parallel region
    • +
    • void omp set num threads (int), sets the number of threads to be used
    • +
    • void omp set nested (int), turns nested parallelism on/off
    • +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs112.html b/doc/pub/week9/html/._week9-bs112.html index c5ef5c53..dc1368f0 100644 --- a/doc/pub/week9/html/._week9-bs112.html +++ b/doc/pub/week9/html/._week9-bs112.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    How to find the max value using OpenMP

    +

    Private variables

    -

    -Write down the simplest algorithm and look carefully for race conditions. How would you handle them? -The first step would be to parallelize as -

    + +

    Private clause can be used to make thread- private versions of such variables:

    -
    #pragma omp parallel for
    - for (i=0; i<n; i++) {
    -    if (x[i] > maxval) {
    -      maxval = x[i];
    -      maxloc = i; 
    -    }
    -}
    -
    -

    +

    +
    +
    +
    +
    +
    #pragma omp parallel private(id)
    +{
    + int id = omp_get_thread_num();
    + cout << "My thread num" << id << endl; 
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +
    • What is their value on entry? Exit?
    • +
    • OpenMP provides ways to control that
    • +
    • Can use default(none) to require the sharing of each variable to be described
    • +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs113.html b/doc/pub/week9/html/._week9-bs113.html index 4b6629e9..5786409f 100644 --- a/doc/pub/week9/html/._week9-bs113.html +++ b/doc/pub/week9/html/._week9-bs113.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Then deal with the race conditions

    +

    Master region

    -

    -Write down the simplest algorithm and look carefully for race conditions. How would you handle them? -The first step would be to parallelize as -

    + +

    It is often useful to have only one thread execute some of the code in a parallel region. I/O statements are a common example

    -
    #pragma omp parallel for
    - for (i=0; i<n; i++) {
    -#pragma omp critical
    -  {
    -     if (x[i] > maxval) {
    -       maxval = x[i];
    -       maxloc = i; 
    -     }
    -  }
    -} 
    -
    -

    -Exercise: write a code which implements this and give an estimate on performance. Perform several runs, -with a serial code only with and without vectorization and compare the serial code with the one that uses OpenMP. Run on different archictectures if you can. +

    +
    +
    +
    +
    +
    #pragma omp parallel 
    +{
    +  #pragma omp master
    +   {
    +      int id = omp_get_thread_num();
    +      cout << "My thread num" << id << endl; 
    +   } 
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs114.html b/doc/pub/week9/html/._week9-bs114.html index 234e9038..8de6d5b0 100644 --- a/doc/pub/week9/html/._week9-bs114.html +++ b/doc/pub/week9/html/._week9-bs114.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - +

    Parallel for loop

    +
    +
    + +
      +
    • Inside a parallel region, the following compiler directive can be used to parallelize a for-loop:
    • +
    + + +
    +
    +
    +
    +
    +
    #pragma omp for
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +
    • Clauses can be added, such as
    • +
        +
      • schedule(static, chunk size)
      • +
      • schedule(dynamic, chunk size)
      • +
      • schedule(guided, chunk size) (non-deterministic allocation)
      • +
      • schedule(runtime)
      • +
      • private(list of variables)
      • +
      • reduction(operator:variable)
      • +
      • nowait
      • +
      +
    +
    +
    -

    What can slow down OpenMP performance?

    -Give it a thought! -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs115.html b/doc/pub/week9/html/._week9-bs115.html index 75fcbf79..5ed23399 100644 --- a/doc/pub/week9/html/._week9-bs115.html +++ b/doc/pub/week9/html/._week9-bs115.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - +

    Parallel computations and loops

    -

    What can slow down OpenMP performance?

    -

    -Performance poor because we insisted on keeping track of the maxval and location during the execution of the loop. - -

      -
    • We do not care about the value during the execution of the loop, just the value at the end.
    • -
    + +

    OpenMP provides an easy way to parallelize a loop

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel for
    +  for (i=0; i<n; i++) c[i] = a[i];
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -This is a common source of performance issues, namely the description of the method used to compute a value imposes additional, unnecessary requirements or properties +

    OpenMP handles index variable (no need to declare in for loop or make private)

    -

    -Idea: Have each thread find the maxloc in its own data, then combine and use temporary arrays indexed by thread number to hold the values found by each thread +

    Which thread does which values? Several options.

    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs116.html b/doc/pub/week9/html/._week9-bs116.html index a0ac4527..604c97f6 100644 --- a/doc/pub/week9/html/._week9-bs116.html +++ b/doc/pub/week9/html/._week9-bs116.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - +

    Scheduling of loop computations

    -

    Find the max location for each thread

    -

    -

    - - -

    int maxloc[MAX_THREADS], mloc;
    -double maxval[MAX_THREADS], mval; 
    -#pragma omp parallel shared(maxval,maxloc)
    -{
    -  int id = omp_get_thread_num(); 
    -  maxval[id] = -1.0e30;
    -#pragma omp for
    -   for (int i=0; i<n; i++) {
    -       if (x[i] > maxval[id]) { 
    -           maxloc[id] = i;
    -           maxval[id] = x[i]; 
    -       }
    -    }
    -}
    -
    -

    + +

    We can let the OpenMP runtime decide. The decision is about how the loop iterates are scheduled +and OpenMP defines three choices of loop scheduling: +

    +
      +
    1. Static: Predefined at compile time. Lowest overhead, predictable
    2. +
    3. Dynamic: Selection made at runtime
    4. +
    5. Guided: Special case of dynamic; attempts to reduce overhead
    6. +
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs117.html b/doc/pub/week9/html/._week9-bs117.html index 3fb55255..7ff2f4e8 100644 --- a/doc/pub/week9/html/._week9-bs117.html +++ b/doc/pub/week9/html/._week9-bs117.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - - -

    Combine the values from each thread

    +

    Example code for loop scheduling

    -

    -

    + -

    #pragma omp flush (maxloc,maxval)
    -#pragma omp master
    -  {
    -    int nt = omp_get_num_threads(); 
    -    mloc = maxloc[0]; 
    -    mval = maxval[0]; 
    -    for (int i=1; i<nt; i++) {
    -        if (maxval[i] > mval) { 
    -           mval = maxval[i]; 
    -           mloc = maxloc[i];
    -        } 
    -     }
    -   }
    -
    -

    -Note that we let the master process perform the last operation. +

    +
    +
    +
    +
    +
    #include <omp.h>
    +#define CHUNKSIZE 100
    +#define N 1000
    +int main (int argc, char *argv[])
    +{
    +int i, chunk;
    +float a[N], b[N], c[N];
    +for (i=0; i < N; i++) a[i] = b[i] = i * 1.0;
    +chunk = CHUNKSIZE;
    +#pragma omp parallel shared(a,b,c,chunk) private(i)
    +{
    +#pragma omp for schedule(dynamic,chunk)
    +for (i=0; i < N; i++) c[i] = a[i] + b[i];
    +} /* end of parallel region */
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs118.html b/doc/pub/week9/html/._week9-bs118.html index 6e1e20ac..65988f01 100644 --- a/doc/pub/week9/html/._week9-bs118.html +++ b/doc/pub/week9/html/._week9-bs118.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - +

    Example code for loop scheduling, guided instead of dynamic

    +
    +
    + -

    Matrix-matrix multiplication

    -This code computes the norm of a vector using OpenMp -

    - - -

    //  OpenMP program to compute vector norm by adding two other vectors
    -#include <cstdlib>
    -#include <iostream>
    -#include <cmath>
    -#include <iomanip>
    -#include  <omp.h>
    -# include <ctime>
    +
    +
    +
    +
    +
    +
    +
    #include <omp.h>
    +#define CHUNKSIZE 100
    +#define N 1000
    +int main (int argc, char *argv[])
    +{
    +int i, chunk;
    +float a[N], b[N], c[N];
    +for (i=0; i < N; i++) a[i] = b[i] = i * 1.0;
    +chunk = CHUNKSIZE;
    +#pragma omp parallel shared(a,b,c,chunk) private(i)
    +{
    +#pragma omp for schedule(guided,chunk)
    +for (i=0; i < N; i++) c[i] = a[i] + b[i];
    +} /* end of parallel region */
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -using namespace std; // note use of namespace -int main (int argc, char* argv[]) -{ - // read in dimension of vector - int n = atoi(argv[1]); - double *a, *b, *c; - int i; - int thread_num; - double wtime, Norm2, s, angle; - cout << " Perform addition of two vectors and compute the norm-2." << endl; - omp_set_num_threads(4); - thread_num = omp_get_max_threads (); - cout << " The number of processors available = " << omp_get_num_procs () << endl ; - cout << " The number of threads available = " << thread_num << endl; - cout << " The matrix order n = " << n << endl; - s = 1.0/sqrt( (double) n); - wtime = omp_get_wtime ( ); - // Allocate space for the vectors to be used - a = new double [n]; b = new double [n]; c = new double [n]; - // Define parallel region -# pragma omp parallel for default(shared) private (angle, i) reduction(+:Norm2) - // Set up values for vectors a and b - for (i = 0; i < n; i++){ - angle = 2.0*M_PI*i/ (( double ) n); - a[i] = s*(sin(angle) + cos(angle)); - b[i] = s*sin(2.0*angle); - c[i] = 0.0; - } - // Then perform the vector addition - for (i = 0; i < n; i++){ - c[i] += a[i]+b[i]; - } - // Compute now the norm-2 - Norm2 = 0.0; - for (i = 0; i < n; i++){ - Norm2 += c[i]*c[i]; - } -// end parallel region - wtime = omp_get_wtime ( ) - wtime; - cout << setiosflags(ios::showpoint | ios::uppercase); - cout << setprecision(10) << setw(20) << "Time used for norm-2 computation=" << wtime << endl; - cout << " Norm-2 = " << Norm2 << endl; - // Free up space - delete[] a; - delete[] b; - delete[] c; - return 0; -} -
    -

    -
    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs119.html b/doc/pub/week9/html/._week9-bs119.html index 53fddbf5..94d0fadf 100644 --- a/doc/pub/week9/html/._week9-bs119.html +++ b/doc/pub/week9/html/._week9-bs119.html @@ -1,31 +1,28 @@ - - -Week 10 March 8-12: Object-orientation strategies and Parallelization - + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + - - - -
    -

     

     

     

    - +

    More on Parallel for loop

    +
    +
    + +
      +
    • The number of loop iterations cannot be non-deterministic; break, return, exit, goto not allowed inside the for-loop
    • +
    • The loop index is private to each thread
    • +
    • A reduction variable is special
    • +
        +
      • During the for-loop there is a local private copy in each thread
      • +
      • At the end of the for-loop, all the local copies are combined together by the reduction operation
      • +
      +
    • Unless the nowait clause is used, an implicit barrier synchronization will be added at the end by the compiler
    • +
    -

    Matrix-matrix multiplication

    -This the matrix-matrix multiplication code with plain c++ memory allocation using OpenMP + +
    +
    +
    +
    +
    +
    // #pragma omp parallel and #pragma omp for
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -

    +

    can be combined into

    - -
    //  Matrix-matrix multiplication and Frobenius norm of a matrix with OpenMP
    -#include <cstdlib>
    -#include <iostream>
    -#include <cmath>
    -#include <iomanip>
    -#include  <omp.h>
    -# include <ctime>
    +
    +
    +
    +
    +
    +
    +
    #pragma omp parallel for
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -using namespace std; // note use of namespace -int main (int argc, char* argv[]) -{ - // read in dimension of square matrix - int n = atoi(argv[1]); - double **A, **B, **C; - int i, j, k; - int thread_num; - double wtime, Fsum, s, angle; - cout << " Compute matrix product C = A * B and Frobenius norm." << endl; - omp_set_num_threads(4); - thread_num = omp_get_max_threads (); - cout << " The number of processors available = " << omp_get_num_procs () << endl ; - cout << " The number of threads available = " << thread_num << endl; - cout << " The matrix order n = " << n << endl; - - s = 1.0/sqrt( (double) n); - wtime = omp_get_wtime ( ); - // Allocate space for the two matrices - A = new double*[n]; B = new double*[n]; C = new double*[n]; - for (i = 0; i < n; i++){ - A[i] = new double[n]; - B[i] = new double[n]; - C[i] = new double[n]; - } - // Define parallel region -# pragma omp parallel for default(shared) private (angle, i, j, k) reduction(+:Fsum) - // Set up values for matrix A and B and zero matrix C - for (i = 0; i < n; i++){ - for (j = 0; j < n; j++) { - angle = 2.0*M_PI*i*j/ (( double ) n); - A[i][j] = s * ( sin ( angle ) + cos ( angle ) ); - B[j][i] = A[i][j]; - } - } - // Then perform the matrix-matrix multiplication - for (i = 0; i < n; i++){ - for (j = 0; j < n; j++) { - C[i][j] = 0.0; - for (k = 0; k < n; k++) { - C[i][j] += A[i][k]*B[k][j]; - } - } - } - // Compute now the Frobenius norm - Fsum = 0.0; - for (i = 0; i < n; i++){ - for (j = 0; j < n; j++) { - Fsum += C[i][j]*C[i][j]; - } - } - Fsum = sqrt(Fsum); -// end parallel region and letting only one thread perform I/O - wtime = omp_get_wtime ( ) - wtime; - cout << setiosflags(ios::showpoint | ios::uppercase); - cout << setprecision(10) << setw(20) << "Time used for matrix-matrix multiplication=" << wtime << endl; - cout << " Frobenius norm = " << Fsum << endl; - // Free up space - for (int i = 0; i < n; i++){ - delete[] A[i]; - delete[] B[i]; - delete[] C[i]; - } - delete[] A; - delete[] B; - delete[] C; - return 0; -} -
    -

    @@ -689,27 +725,32 @@

    118
  • 119
  • 120
  • +
  • 121
  • +
  • 122
  • +
  • 123
  • +
  • 124
  • +
  • 125
  • +
  • 126
  • +
  • 127
  • +
  • 128
  • +
  • 129
  • +
  • ...
  • +
  • 141
  • +
  • »
  • -

    - - -
    - - - diff --git a/doc/pub/week9/html/._week9-bs120.html b/doc/pub/week9/html/._week9-bs120.html new file mode 100644 index 00000000..e3df3b26 --- /dev/null +++ b/doc/pub/week9/html/._week9-bs120.html @@ -0,0 +1,751 @@ + + + + + + + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + + + + + + + + + + + + + + + + + + + + +
    +

     

     

     

    + + +

    What can happen with this loop?

    + +
    +
    + +

    What happens with code like this

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel for
    +for (i=0; i<n; i++) sum += a[i]*a[i];
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    All threads can access the sum variable, but the addition is not atomic! It is important to avoid race between threads. So-called reductions in OpenMP are thus important for performance and for obtaining correct results. OpenMP lets us indicate that a variable is used for a reduction with a particular operator. The above code becomes

    + + +
    +
    +
    +
    +
    +
    sum = 0.0;
    +#pragma omp parallel for reduction(+:sum)
    +for (i=0; i<n; i++) sum += a[i]*a[i];
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +

    + +

    + +
    + + + + +
    + +
    + + + diff --git a/doc/pub/week9/html/._week9-bs121.html b/doc/pub/week9/html/._week9-bs121.html new file mode 100644 index 00000000..04dff569 --- /dev/null +++ b/doc/pub/week9/html/._week9-bs121.html @@ -0,0 +1,732 @@ + + + + + + + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + + + + + + + + + + + + + + + + + + + + +
    +

     

     

     

    + + +

    Inner product

    +
    +
    + +$$ +\sum_{i=0}^{n-1} a_ib_i +$$ + + + +
    +
    +
    +
    +
    +
    int i;
    +double sum = 0.;
    +/* allocating and initializing arrays */
    +/* ... */
    +#pragma omp parallel for default(shared) private(i) reduction(+:sum)
    + for (i=0; i<N; i++) sum += a[i]*b[i];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +

    + +

    + +
    + + + + +
    + +
    + + + diff --git a/doc/pub/week9/html/._week9-bs122.html b/doc/pub/week9/html/._week9-bs122.html new file mode 100644 index 00000000..f39aa7a7 --- /dev/null +++ b/doc/pub/week9/html/._week9-bs122.html @@ -0,0 +1,735 @@ + + + + + + + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + + + + + + + + + + + + + + + + + + + + +
    +

     

     

     

    + + +

    Different threads do different tasks

    +
    +
    + + +

    Different threads do different tasks independently, each section is executed by one thread.

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel
    +{
    +#pragma omp sections
    +{
    +#pragma omp section
    +funcA ();
    +#pragma omp section
    +funcB ();
    +#pragma omp section
    +funcC ();
    +}
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +

    + +

    + +
    + + + + +
    + +
    + + + diff --git a/doc/pub/week9/html/._week9-bs123.html b/doc/pub/week9/html/._week9-bs123.html new file mode 100644 index 00000000..a6542487 --- /dev/null +++ b/doc/pub/week9/html/._week9-bs123.html @@ -0,0 +1,750 @@ + + + + + + + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + + + + + + + + + + + + + + + + + + + + +
    +

     

     

     

    + + +

    Single execution

    +
    +
    + + + +
    +
    +
    +
    +
    +
    #pragma omp single { ... }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The code is executed by one thread only, no guarantee which thread

    + +

    Can introduce an implicit barrier at the end

    + + +
    +
    +
    +
    +
    +
    #pragma omp master { ... }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Code executed by the master thread, guaranteed and no implicit barrier at the end.

    +
    +
    + + +

    + +

    + +
    + + + + +
    + +
    + + + diff --git a/doc/pub/week9/html/._week9-bs124.html b/doc/pub/week9/html/._week9-bs124.html new file mode 100644 index 00000000..7fafa115 --- /dev/null +++ b/doc/pub/week9/html/._week9-bs124.html @@ -0,0 +1,820 @@ + + + + + + + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + + + + + + + + + + + + + + + + + + + + +
    +

     

     

     

    + + +

    Coordination and synchronization

    +
    +
    + + + +
    +
    +
    +
    +
    +
    #pragma omp barrier
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Synchronization, must be encountered by all threads in a team (or none)

    + + +
    +
    +
    +
    +
    +
    #pragma omp ordered { a block of codes }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    is another form of synchronization (in sequential order). +The form +

    + + +
    +
    +
    +
    +
    +
    #pragma omp critical { a block of codes }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    and

    + + +
    +
    +
    +
    +
    +
    #pragma omp atomic { single assignment statement }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    is more efficient than

    + + +
    +
    +
    +
    +
    +
    #pragma omp critical
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +

    + +

    + +
    + + + + +
    + +
    + + + diff --git a/doc/pub/week9/html/._week9-bs125.html b/doc/pub/week9/html/._week9-bs125.html new file mode 100644 index 00000000..862c4125 --- /dev/null +++ b/doc/pub/week9/html/._week9-bs125.html @@ -0,0 +1,715 @@ + + + + + + + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + + + + + + + + + + + + + + + + + + + + +
    +

     

     

     

    + + +

    Data scope

    +
    +
    + +
      +
    • OpenMP data scope attribute clauses:
    • +
        +
      • shared
      • +
      • private
      • +
      • firstprivate
      • +
      • lastprivate
      • +
      • reduction
      • +
      +
    +

    What are the purposes of these attributes

    +
      +
    • define how and which variables are transferred to a parallel region (and back)
    • +
    • define which variables are visible to all threads in a parallel region, and which variables are privately allocated to each thread
    • +
    +
    +
    + + +

    + +

    + +
    + + + + +
    + +
    + + + diff --git a/doc/pub/week9/html/._week9-bs126.html b/doc/pub/week9/html/._week9-bs126.html new file mode 100644 index 00000000..68256fe4 --- /dev/null +++ b/doc/pub/week9/html/._week9-bs126.html @@ -0,0 +1,707 @@ + + + + + + + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + + + + + + + + + + + + + + + + + + + + +
    +

     

     

     

    + + +

    Some remarks

    +
    +
    + + +
      +
    • When entering a parallel region, the private clause ensures each thread having its own new variable instances. The new variables are assumed to be uninitialized.
    • +
    • A shared variable exists in only one memory location and all threads can read and write to that address. It is the programmer's responsibility to ensure that multiple threads properly access a shared variable.
    • +
    • The firstprivate clause combines the behavior of the private clause with automatic initialization.
    • +
    • The lastprivate clause combines the behavior of the private clause with a copy back (from the last loop iteration or section) to the original variable outside the parallel region.
    • +
    +
    +
    + + +

    + +

    + +
    + + + + +
    + +
    + + + diff --git a/doc/pub/week9/html/._week9-bs127.html b/doc/pub/week9/html/._week9-bs127.html new file mode 100644 index 00000000..cd237ae7 --- /dev/null +++ b/doc/pub/week9/html/._week9-bs127.html @@ -0,0 +1,768 @@ + + + + + + + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + + + + + + + + + + + + + + + + + + + + +
    +

     

     

     

    + + +

    Parallelizing nested for-loops

    +
    +
    + + +
      +
    • Serial code
    • +
    + + +
    +
    +
    +
    +
    +
    for (i=0; i<100; i++)
    +    for (j=0; j<100; j++)
    +        a[i][j] = b[i][j] + c[i][j];
    +    }
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
      +
    • Parallelization
    • +
    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel for private(j)
    +for (i=0; i<100; i++)
    +    for (j=0; j<100; j++)
    +       a[i][j] = b[i][j] + c[i][j];
    +    }
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
      +
    • Why not parallelize the inner loop? to save overhead of repeated thread forks-joins
    • +
    • Why must j be private? To avoid race condition among the threads
    • +
    +
    +
    + + +

    + +

    + +
    + + + + +
    + +
    + + + diff --git a/doc/pub/week9/html/._week9-bs128.html b/doc/pub/week9/html/._week9-bs128.html new file mode 100644 index 00000000..bbde9f39 --- /dev/null +++ b/doc/pub/week9/html/._week9-bs128.html @@ -0,0 +1,733 @@ + + + + + + + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + + + + + + + + + + + + + + + + + + + + +
    +

     

     

     

    + + +

    Nested parallelism

    +
    +
    + +

    When a thread in a parallel region encounters another parallel construct, it +may create a new team of threads and become the master of the new +team. +

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel num_threads(4)
    +{
    +/* .... */
    +#pragma omp parallel num_threads(2)
    +{
    +//  
    +}
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +

    + +

    + +
    + + + + +
    + +
    + + + diff --git a/doc/pub/week9/html/._week9-bs129.html b/doc/pub/week9/html/._week9-bs129.html new file mode 100644 index 00000000..d6c9e935 --- /dev/null +++ b/doc/pub/week9/html/._week9-bs129.html @@ -0,0 +1,731 @@ + + + + + + + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + + + + + + + + + + + + + + + + + + + + +
    +

     

     

     

    + + +

    Parallel tasks

    +
    +
    + + + +
    +
    +
    +
    +
    +
    #pragma omp task 
    +#pragma omp parallel shared(p_vec) private(i)
    +{
    +#pragma omp single
    +{
    +for (i=0; i<N; i++) {
    +  double r = random_number();
    +  if (p_vec[i] > r) {
    +#pragma omp task
    +   do_work (p_vec[i]);
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +

    + +

    + +
    + + + + +
    + +
    + + + diff --git a/doc/pub/week9/html/._week9-bs130.html b/doc/pub/week9/html/._week9-bs130.html new file mode 100644 index 00000000..3d44e13a --- /dev/null +++ b/doc/pub/week9/html/._week9-bs130.html @@ -0,0 +1,759 @@ + + + + + + + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + + + + + + + + + + + + + + + + + + + + +
    +

     

     

     

    + + +

    Common mistakes

    +
    +
    + +

    Race condition

    + + +
    +
    +
    +
    +
    +
    int nthreads;
    +#pragma omp parallel shared(nthreads)
    +{
    +nthreads = omp_get_num_threads();
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Deadlock

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel
    +{
    +...
    +#pragma omp critical
    +{
    +...
    +#pragma omp barrier
    +}
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +

    + +

    + +
    + + + + +
    + +
    + + + diff --git a/doc/pub/week9/html/._week9-bs131.html b/doc/pub/week9/html/._week9-bs131.html new file mode 100644 index 00000000..f1d5da69 --- /dev/null +++ b/doc/pub/week9/html/._week9-bs131.html @@ -0,0 +1,730 @@ + + + + + + + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + + + + + + + + + + + + + + + + + + + + +
    +

     

     

     

    + + +

    Not all computations are simple

    +
    +
    + +

    Not all computations are simple loops where the data can be evenly +divided among threads without any dependencies between threads +

    + +

    An example is finding the location and value of the largest element in an array

    + + +
    +
    +
    +
    +
    +
    for (i=0; i<n; i++) { 
    +   if (x[i] > maxval) {
    +      maxval = x[i];
    +      maxloc = i; 
    +   }
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +

    + +

    + +
    + + + + +
    + +
    + + + diff --git a/doc/pub/week9/html/._week9-bs132.html b/doc/pub/week9/html/._week9-bs132.html new file mode 100644 index 00000000..26b84107 --- /dev/null +++ b/doc/pub/week9/html/._week9-bs132.html @@ -0,0 +1,754 @@ + + + + + + + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + + + + + + + + + + + + + + + + + + + + +
    +

     

     

     

    + + +

    Not all computations are simple, competing threads

    +
    +
    + +

    All threads are potentially accessing and changing the same values, maxloc and maxval.

    +
      +
    1. OpenMP provides several ways to coordinate access to shared values
    2. +
    + + +
    +
    +
    +
    +
    +
    #pragma omp atomic
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +
    1. Only one thread at a time can execute the following statement (not block). We can use the critical option
    2. +
    + + +
    +
    +
    +
    +
    +
    #pragma omp critical
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +
    1. Only one thread at a time can execute the following block
    2. +
    +

    Atomic may be faster than critical but depends on hardware

    +
    +
    + + +

    + +

    + +
    + + + + +
    + +
    + + + diff --git a/doc/pub/week9/html/._week9-bs133.html b/doc/pub/week9/html/._week9-bs133.html new file mode 100644 index 00000000..b6b41ca9 --- /dev/null +++ b/doc/pub/week9/html/._week9-bs133.html @@ -0,0 +1,727 @@ + + + + + + + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + + + + + + + + + + + + + + + + + + + + +
    +

     

     

     

    + + +

    How to find the max value using OpenMP

    +
    +
    + +

    Write down the simplest algorithm and look carefully for race conditions. How would you handle them? +The first step would be to parallelize as +

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel for
    + for (i=0; i<n; i++) {
    +    if (x[i] > maxval) {
    +      maxval = x[i];
    +      maxloc = i; 
    +    }
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +

    + +

    + +
    + + + + +
    + +
    + + + diff --git a/doc/pub/week9/html/._week9-bs134.html b/doc/pub/week9/html/._week9-bs134.html new file mode 100644 index 00000000..b3373106 --- /dev/null +++ b/doc/pub/week9/html/._week9-bs134.html @@ -0,0 +1,732 @@ + + + + + + + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + + + + + + + + + + + + + + + + + + + + +
    +

     

     

     

    + + +

    Then deal with the race conditions

    +
    +
    + +

    Write down the simplest algorithm and look carefully for race conditions. How would you handle them? +The first step would be to parallelize as +

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel for
    + for (i=0; i<n; i++) {
    +#pragma omp critical
    +  {
    +     if (x[i] > maxval) {
    +       maxval = x[i];
    +       maxloc = i; 
    +     }
    +  }
    +} 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Exercise: write a code which implements this and give an estimate on performance. Perform several runs, +with a serial code only with and without vectorization and compare the serial code with the one that uses OpenMP. Run on different archictectures if you can. +

    +
    +
    + +

    + +

    + +
    + + + + +
    + +
    + + + diff --git a/doc/pub/week9/html/._week9-bs135.html b/doc/pub/week9/html/._week9-bs135.html new file mode 100644 index 00000000..ecd0d861 --- /dev/null +++ b/doc/pub/week9/html/._week9-bs135.html @@ -0,0 +1,689 @@ + + + + + + + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + + + + + + + + + + + + + + + + + + + + +
    +

     

     

     

    + + +

    What can slow down OpenMP performance?

    +

    Give it a thought!

    + +

    + +

    + +
    + + + + +
    + +
    + + + diff --git a/doc/pub/week9/html/._week9-bs136.html b/doc/pub/week9/html/._week9-bs136.html new file mode 100644 index 00000000..599b6c70 --- /dev/null +++ b/doc/pub/week9/html/._week9-bs136.html @@ -0,0 +1,700 @@ + + + + + + + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + + + + + + + + + + + + + + + + + + + + +
    +

     

     

     

    + + +

    What can slow down OpenMP performance?

    +
    +
    + +

    Performance poor because we insisted on keeping track of the maxval and location during the execution of the loop.

    +
      +
    • We do not care about the value during the execution of the loop, just the value at the end.
    • +
    +

    This is a common source of performance issues, namely the description of the method used to compute a value imposes additional, unnecessary requirements or properties

    + +Idea: Have each thread find the maxloc in its own data, then combine and use temporary arrays indexed by thread number to hold the values found by each thread +
    +
    + + +

    + +

    + +
    + + + + +
    + +
    + + + diff --git a/doc/pub/week9/html/._week9-bs137.html b/doc/pub/week9/html/._week9-bs137.html new file mode 100644 index 00000000..3740456b --- /dev/null +++ b/doc/pub/week9/html/._week9-bs137.html @@ -0,0 +1,727 @@ + + + + + + + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + + + + + + + + + + + + + + + + + + + + +
    +

     

     

     

    + + +

    Find the max location for each thread

    +
    +
    + + + +
    +
    +
    +
    +
    +
    int maxloc[MAX_THREADS], mloc;
    +double maxval[MAX_THREADS], mval; 
    +#pragma omp parallel shared(maxval,maxloc)
    +{
    +  int id = omp_get_thread_num(); 
    +  maxval[id] = -1.0e30;
    +#pragma omp for
    +   for (int i=0; i<n; i++) {
    +       if (x[i] > maxval[id]) { 
    +           maxloc[id] = i;
    +           maxval[id] = x[i]; 
    +       }
    +    }
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +

    + +

    + +
    + + + + +
    + +
    + + + diff --git a/doc/pub/week9/html/._week9-bs138.html b/doc/pub/week9/html/._week9-bs138.html new file mode 100644 index 00000000..060ae5da --- /dev/null +++ b/doc/pub/week9/html/._week9-bs138.html @@ -0,0 +1,726 @@ + + + + + + + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + + + + + + + + + + + + + + + + + + + + +
    +

     

     

     

    + + +

    Combine the values from each thread

    +
    +
    + + + +
    +
    +
    +
    +
    +
    #pragma omp flush (maxloc,maxval)
    +#pragma omp master
    +  {
    +    int nt = omp_get_num_threads(); 
    +    mloc = maxloc[0]; 
    +    mval = maxval[0]; 
    +    for (int i=1; i<nt; i++) {
    +        if (maxval[i] > mval) { 
    +           mval = maxval[i]; 
    +           mloc = maxloc[i];
    +        } 
    +     }
    +   }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Note that we let the master process perform the last operation.

    +
    +
    + +

    + +

    + +
    + + + + +
    + +
    + + + diff --git a/doc/pub/week9/html/._week9-bs139.html b/doc/pub/week9/html/._week9-bs139.html new file mode 100644 index 00000000..5214c06c --- /dev/null +++ b/doc/pub/week9/html/._week9-bs139.html @@ -0,0 +1,764 @@ + + + + + + + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + + + + + + + + + + + + + + + + + + + + +
    +

     

     

     

    + + +

    Matrix-matrix multiplication

    +

    This code computes the norm of a vector using OpenMp

    + + +
    +
    +
    +
    +
    +
    //  OpenMP program to compute vector norm by adding two other vectors
    +#include <cstdlib>
    +#include <iostream>
    +#include <cmath>
    +#include <iomanip>
    +#include  <omp.h>
    +# include <ctime>
    +
    +using namespace std; // note use of namespace
    +int main (int argc, char* argv[])
    +{
    +  // read in dimension of vector
    +  int n = atoi(argv[1]);
    +  double *a, *b, *c;
    +  int i;
    +  int thread_num;
    +  double wtime, Norm2, s, angle;
    +  cout << "  Perform addition of two vectors and compute the norm-2." << endl;
    +  omp_set_num_threads(4);
    +  thread_num = omp_get_max_threads ();
    +  cout << "  The number of processors available = " << omp_get_num_procs () << endl ;
    +  cout << "  The number of threads available    = " << thread_num <<  endl;
    +  cout << "  The matrix order n                 = " << n << endl;
    +
    +  s = 1.0/sqrt( (double) n);
    +  wtime = omp_get_wtime ( );
    +  // Allocate space for the vectors to be used
    +  a = new double [n]; b = new double [n]; c = new double [n];
    +  // Define parallel region
    +# pragma omp parallel for default(shared) private (angle, i) reduction(+:Norm2)
    +  // Set up values for vectors  a and b
    +  for (i = 0; i < n; i++){
    +      angle = 2.0*M_PI*i/ (( double ) n);
    +      a[i] = s*(sin(angle) + cos(angle));
    +      b[i] =  s*sin(2.0*angle);
    +      c[i] = 0.0;
    +  }
    +  // Then perform the vector addition
    +  for (i = 0; i < n; i++){
    +     c[i] += a[i]+b[i];
    +  }
    +  // Compute now the norm-2
    +  Norm2 = 0.0;
    +  for (i = 0; i < n; i++){
    +     Norm2  += c[i]*c[i];
    +  }
    +// end parallel region
    +  wtime = omp_get_wtime ( ) - wtime;
    +  cout << setiosflags(ios::showpoint | ios::uppercase);
    +  cout << setprecision(10) << setw(20) << "Time used  for norm-2 computation=" << wtime  << endl;
    +  cout << " Norm-2  = " << Norm2 << endl;
    +  // Free up space
    +  delete[] a;
    +  delete[] b;
    +  delete[] c;
    +  return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +

    + +

    + +
    + + + + +
    + +
    + + + diff --git a/doc/pub/week9/html/._week9-bs140.html b/doc/pub/week9/html/._week9-bs140.html new file mode 100644 index 00000000..7688209c --- /dev/null +++ b/doc/pub/week9/html/._week9-bs140.html @@ -0,0 +1,782 @@ + + + + + + + +Week 11, March 11-15: Resampling Techniques, Bootstrap and Blocking + + + + + + + + + + + + + + + + + + + + +
    +

     

     

     

    + + +

    Matrix-matrix multiplication

    +

    This the matrix-matrix multiplication code with plain c++ memory allocation using OpenMP

    + + + +
    +
    +
    +
    +
    +
    //  Matrix-matrix multiplication and Frobenius norm of a matrix with OpenMP
    +#include <cstdlib>
    +#include <iostream>
    +#include <cmath>
    +#include <iomanip>
    +#include  <omp.h>
    +# include <ctime>
    +
    +using namespace std; // note use of namespace
    +int main (int argc, char* argv[])
    +{
    +  // read in dimension of square matrix
    +  int n = atoi(argv[1]);
    +  double **A, **B, **C;
    +  int i, j, k;
    +  int thread_num;
    +  double wtime, Fsum, s, angle;
    +  cout << "  Compute matrix product C = A * B and Frobenius norm." << endl;
    +  omp_set_num_threads(4);
    +  thread_num = omp_get_max_threads ();
    +  cout << "  The number of processors available = " << omp_get_num_procs () << endl ;
    +  cout << "  The number of threads available    = " << thread_num <<  endl;
    +  cout << "  The matrix order n                 = " << n << endl;
    +
    +  s = 1.0/sqrt( (double) n);
    +  wtime = omp_get_wtime ( );
    +  // Allocate space for the two matrices
    +  A = new double*[n]; B = new double*[n]; C = new double*[n];
    +  for (i = 0; i < n; i++){
    +    A[i] = new double[n];
    +    B[i] = new double[n];
    +    C[i] = new double[n];
    +  }
    +  // Define parallel region
    +# pragma omp parallel for default(shared) private (angle, i, j, k) reduction(+:Fsum)
    +  // Set up values for matrix A and B and zero matrix C
    +  for (i = 0; i < n; i++){
    +    for (j = 0; j < n; j++) {
    +      angle = 2.0*M_PI*i*j/ (( double ) n);
    +      A[i][j] = s * ( sin ( angle ) + cos ( angle ) );
    +      B[j][i] =  A[i][j];
    +    }
    +  }
    +  // Then perform the matrix-matrix multiplication
    +  for (i = 0; i < n; i++){
    +    for (j = 0; j < n; j++) {
    +       C[i][j] =  0.0;    
    +       for (k = 0; k < n; k++) {
    +            C[i][j] += A[i][k]*B[k][j];
    +       }
    +    }
    +  }
    +  // Compute now the Frobenius norm
    +  Fsum = 0.0;
    +  for (i = 0; i < n; i++){
    +    for (j = 0; j < n; j++) {
    +      Fsum += C[i][j]*C[i][j];
    +    }
    +  }
    +  Fsum = sqrt(Fsum);
    +// end parallel region and letting only one thread perform I/O
    +  wtime = omp_get_wtime ( ) - wtime;
    +  cout << setiosflags(ios::showpoint | ios::uppercase);
    +  cout << setprecision(10) << setw(20) << "Time used  for matrix-matrix multiplication=" << wtime  << endl;
    +  cout << "  Frobenius norm  = " << Fsum << endl;
    +  // Free up space
    +  for (int i = 0; i < n; i++){
    +    delete[] A[i];
    +    delete[] B[i];
    +    delete[] C[i];
    +  }
    +  delete[] A;
    +  delete[] B;
    +  delete[] C;
    +  return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +

    + +

    + +
    + + + + +
    + +
    + + + diff --git a/doc/pub/week9/html/week9-bs.html b/doc/pub/week9/html/week9-bs.html index 0ee9f022..eb84da37 100644 --- a/doc/pub/week9/html/week9-bs.html +++ b/doc/pub/week9/html/week9-bs.html @@ -79,11 +79,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -137,8 +514,127 @@
  • Blocking Transformations
  • Blocking Transformations, getting there
  • Blocking Transformations, final expressions
  • -
  • Example code form last week
  • -
  • Resampling analysis
  • +
  • More on the blocking method
  • +
  • Example code form last week
  • +
  • Resampling analysis
  • +
  • Content
  • +
  • Optimization and profiling
  • +
  • More on optimization
  • +
  • Optimization and profiling
  • +
  • Optimization and debugging
  • +
  • Other hints
  • +
  • Vectorization and the basic idea behind parallel computing
  • +
  • A rough classification of hardware models
  • +
  • Shared memory and distributed memory
  • +
  • Different parallel programming paradigms
  • +
  • Different parallel programming paradigms
  • +
  • What is vectorization?
  • +
  • Number of elements that can acted upon
  • +
  • Number of elements that can acted upon, examples
  • +
  • Operation counts for scalar operation
  • +
  • Number of elements that can acted upon, examples
  • +
  • Number of operations when vectorized
  • +
  • "A simple test case with and without vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"
  • +
  • Compiling with and without vectorization
  • +
  • Compiling with and without vectorization using clang
  • +
  • Automatic vectorization and vectorization inhibitors, criteria
  • +
  • Automatic vectorization and vectorization inhibitors, exit criteria
  • +
  • Automatic vectorization and vectorization inhibitors, straight-line code
  • +
  • Automatic vectorization and vectorization inhibitors, nested loops
  • +
  • Automatic vectorization and vectorization inhibitors, function calls
  • +
  • Automatic vectorization and vectorization inhibitors, data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, more data dependencies
  • +
  • Automatic vectorization and vectorization inhibitors, memory stride
  • +
  • Memory management
  • +
  • Memory and communication
  • +
  • Measuring performance
  • +
  • Problems with measuring time
  • +
  • Problems with cold start
  • +
  • Problems with smart compilers
  • +
  • Problems with interference
  • +
  • Problems with measuring performance
  • +
  • Thomas algorithm for tridiagonal linear algebra equations
  • +
  • Thomas algorithm, forward substitution
  • +
  • Thomas algorithm, backward substitution
  • +
  • Thomas algorithm and counting of operations (floating point and memory)
  • +
  • "Example: Transpose of a matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"
  • +
  • How do we define speedup? Simplest form
  • +
  • How do we define speedup? Correct baseline
  • +
  • Parallel speedup
  • +
  • Speedup and memory
  • +
  • Upper bounds on speedup
  • +
  • Amdahl's law
  • +
  • How much is parallelizable
  • +
  • Today's situation of parallel computing
  • +
  • Overhead present in parallel computing
  • +
  • Parallelizing a sequential algorithm
  • +
  • Strategies
  • +
  • How do I run MPI on a PC/Laptop? MPI
  • +
  • Can I do it on my own PC/laptop? OpenMP installation
  • +
  • Installing MPI
  • +
  • Installing MPI and using Qt
  • +
  • What is Message Passing Interface (MPI)?
  • +
  • Going Parallel with MPI
  • +
  • MPI is a library
  • +
  • Bindings to MPI routines
  • +
  • Communicator
  • +
  • Some of the most important MPI functions
  • +
  • "The first MPI C/C++ program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"
  • +
  • The Fortran program
  • +
  • Note 1
  • +
  • "Ordered output with MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"
  • +
  • Note 2
  • +
  • "Ordered output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"
  • +
  • Note 3
  • +
  • Note 4
  • +
  • "Numerical integration in parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"
  • +
  • Dissection of trapezoidal rule with \( MPI\_reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Integrating with MPI
  • +
  • How do I use \( MPI\_reduce \)?
  • +
  • More on \( MPI\_Reduce \)
  • +
  • Dissection of trapezoidal rule
  • +
  • Dissection of trapezoidal rule
  • +
  • "The quantum dot program for two electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"
  • +
  • What is OpenMP
  • +
  • Getting started, things to remember
  • +
  • OpenMP syntax
  • +
  • Different OpenMP styles of parallelism
  • +
  • General code structure
  • +
  • Parallel region
  • +
  • Hello world, not again, please!
  • +
  • Hello world, yet another variant
  • +
  • Important OpenMP library routines
  • +
  • Private variables
  • +
  • Master region
  • +
  • Parallel for loop
  • +
  • Parallel computations and loops
  • +
  • Scheduling of loop computations
  • +
  • Example code for loop scheduling
  • +
  • Example code for loop scheduling, guided instead of dynamic
  • +
  • More on Parallel for loop
  • +
  • What can happen with this loop?
  • +
  • Inner product
  • +
  • Different threads do different tasks
  • +
  • Single execution
  • +
  • Coordination and synchronization
  • +
  • Data scope
  • +
  • Some remarks
  • +
  • Parallelizing nested for-loops
  • +
  • Nested parallelism
  • +
  • Parallel tasks
  • +
  • Common mistakes
  • +
  • Not all computations are simple
  • +
  • Not all computations are simple, competing threads
  • +
  • How to find the max value using OpenMP
  • +
  • Then deal with the race conditions
  • +
  • What can slow down OpenMP performance?
  • +
  • What can slow down OpenMP performance?
  • +
  • Find the max location for each thread
  • +
  • Combine the values from each thread
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"
  • +
  • "Matrix-matrix multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"
  • @@ -193,7 +689,7 @@

    March 11-15

  • 9
  • 10
  • ...
  • -
  • 22
  • +
  • 141
  • »
  • diff --git a/doc/pub/week9/html/week9-reveal.html b/doc/pub/week9/html/week9-reveal.html index e6b89bc5..fcf34527 100644 --- a/doc/pub/week9/html/week9-reveal.html +++ b/doc/pub/week9/html/week9-reveal.html @@ -209,15 +209,7 @@

    Overview of week 11, March 11-15

    - -
    -Teaching Material, videos and written material -

    -

    -
    +

    Note, these notes contain additional material om optimization and parallelization. Parts of this material will be discussed this week.

    @@ -437,9 +429,9 @@

    Introducing the correlation functi

    Resampling methods: Blocking

    The blocking method was made popular by Flyvbjerg and Pedersen (1989) -and has become one of the standard ways to estimate -\( V(\widehat{\theta}) \) for exactly one \( \widehat{\theta} \), namely -\( \widehat{\theta} = \overline{X} \). +and has become one of the standard ways to estimate the variance +\( \mathrm{var}(\widehat{\theta}) \) for exactly one estimator \( \widehat{\theta} \), namely +\( \widehat{\theta} = \overline{X} \), the mean value.

    Assume \( n = 2^d \) for some integer \( d>1 \) and \( X_1,X_2,\cdots, X_n \) is a stationary time series to begin with. @@ -579,11 +571,15 @@

    Blocking Transformations, fi \end{align} $$

     
    +

    + +
    +

    More on the blocking method

    Flyvbjerg and Petersen demonstrated that the sequence \( \{e_k\}_{k=0}^{d-1} \) is decreasing, and conjecture that the term \( e_k \) can be made as small as we would like by making \( k \) (and hence -\( d \)) sufficiently large. The sequence is decreasing (Master of Science thesis by Marius Jonsson, UiO 2018). +\( d \)) sufficiently large. The sequence is decreasing. It means we can apply blocking transformations until \( e_k \) is sufficiently small, and then estimate \( \mathrm{var}(\overline{X}) \) by \( \widehat{\sigma}^2_k/n_k \). @@ -919,6 +915,5206 @@

    Resampling analysis

    +
    +

    Content

    + +
    + +
    +

    Optimization and profiling

    +
    + +

    + +

    Till now we have not paid much attention to speed and possible optimization possibilities +inherent in the various compilers. We have compiled and linked as +

    + + +
    +
    +
    +
    +
    +
    c++  -c  mycode.cpp
    +c++  -o  mycode.exe  mycode.o
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    For Fortran replace with for example gfortran or ifort. +This is what we call a flat compiler option and should be used when we develop the code. +It produces normally a very large and slow code when translated to machine instructions. +We use this option for debugging and for establishing the correct program output because +every operation is done precisely as the user specified it. +

    + +

    It is instructive to look up the compiler manual for further instructions by writing

    + + +
    +
    +
    +
    +
    +
    man c++
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    More on optimization

    +
    + +

    +

    We have additional compiler options for optimization. These may include procedure inlining where +performance may be improved, moving constants inside loops outside the loop, +identify potential parallelism, include automatic vectorization or replace a division with a reciprocal +and a multiplication if this speeds up the code. +

    + + +
    +
    +
    +
    +
    +
    c++  -O3 -c  mycode.cpp
    +c++  -O3 -o  mycode.exe  mycode.o
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    This (other options are -O2 or -Ofast) is the recommended option.

    +
    +
    + +
    +

    Optimization and profiling

    +
    + +

    +

    It is also useful to profile your program under the development stage. +You would then compile with +

    + + +
    +
    +
    +
    +
    +
    c++  -pg -O3 -c  mycode.cpp
    +c++  -pg -O3 -o  mycode.exe  mycode.o
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    After you have run the code you can obtain the profiling information via

    + + +
    +
    +
    +
    +
    +
    gprof mycode.exe >  ProfileOutput
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    When you have profiled properly your code, you must take out this option as it +slows down performance. +For memory tests use valgrind. An excellent environment for all these aspects, and much more, is Qt creator. +

    +
    +
    + +
    +

    Optimization and debugging

    +
    + +

    +

    Adding debugging options is a very useful alternative under the development stage of a program. +You would then compile with +

    + + +
    +
    +
    +
    +
    +
    c++  -g -O0 -c  mycode.cpp
    +c++  -g -O0 -o  mycode.exe  mycode.o
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    This option generates debugging information allowing you to trace for example if an array is properly allocated. Some compilers work best with the no optimization option -O0.

    +
    + +
    +Other optimization flags +

    +

    Depending on the compiler, one can add flags which generate code that catches integer overflow errors. +The flag -ftrapv does this for the CLANG compiler on OS X operating systems. +

    +
    +
    + +
    +

    Other hints

    +
    + +

    +

    In general, irrespective of compiler options, it is useful to

    +
      +

    • avoid if tests or call to functions inside loops, if possible.
    • +

    • avoid multiplication with constants inside loops if possible
    • +
    +

    +

    Here is an example of a part of a program where specific operations lead to a slower code

    + + +
    +
    +
    +
    +
    +
    k = n-1;
    +for (i = 0; i < n; i++){
    +    a[i] = b[i] +c*d;
    +    e = g[k];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    A better code is

    + + +
    +
    +
    +
    +
    +
    temp = c*d;
    +for (i = 0; i < n; i++){
    +    a[i] = b[i] + temp;
    +}
    +e = g[n-1];
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Here we avoid a repeated multiplication inside a loop. +Most compilers, depending on compiler flags, identify and optimize such bottlenecks on their own, without requiring any particular action by the programmer. However, it is always useful to single out and avoid code examples like the first one discussed here. +

    +
    +
    + +
    +

    Vectorization and the basic idea behind parallel computing

    +
    + +

    +

    Present CPUs are highly parallel processors with varying levels of parallelism. The typical situation can be described via the following three statements.

    +
      +

    • Pursuit of shorter computation time and larger simulation size gives rise to parallel computing.
    • +

    • Multiple processors are involved to solve a global problem.
    • +

    • The essence is to divide the entire computation evenly among collaborative processors. Divide and conquer.
    • +
    +

    +

    Before we proceed with a more detailed discussion of topics like vectorization and parallelization, we need to remind ourselves about some basic features of different hardware models.

    +
    +
    + +
    +

    A rough classification of hardware models

    +
    + +

    + +

      +

    • Conventional single-processor computers are named SISD (single-instruction-single-data) machines.
    • +

    • SIMD (single-instruction-multiple-data) machines incorporate the idea of parallel processing, using a large number of processing units to execute the same instruction on different data.
    • +

    • Modern parallel computers are so-called MIMD (multiple-instruction-multiple-data) machines and can execute different instruction streams in parallel on different data.
    • +
    +
    +
    + +
    +

    Shared memory and distributed memory

    +
    + +

    +

    One way of categorizing modern parallel computers is to look at the memory configuration.

    +
      +

    • In shared memory systems the CPUs share the same address space. Any CPU can access any data in the global memory.
    • +

    • In distributed memory systems each CPU has its own memory.
    • +
    +

    +

    The CPUs are connected by some network and may exchange messages.

    +
    +
    + +
    +

    Different parallel programming paradigms

    +
    + +

    + +

      +

    • Task parallelism: the work of a global problem can be divided into a number of independent tasks, which rarely need to synchronize. Monte Carlo simulations represent a typical situation. Integration is another. However this paradigm is of limited use.
    • +

    • Data parallelism: use of multiple threads (e.g. one or more threads per processor) to dissect loops over arrays etc. Communication and synchronization between processors are often hidden, thus easy to program. However, the user surrenders much control to a specialized compiler. Examples of data parallelism are compiler-based parallelization and OpenMP directives.
    • +
    +
    +
    + +
    +

    Different parallel programming paradigms

    +
    + +

    + +

      +

    • Message passing: all involved processors have an independent memory address space. The user is responsible for partitioning the data/work of a global problem and distributing the subproblems to the processors. Collaboration between processors is achieved by explicit message passing, which is used for data transfer plus synchronization.
    • +

    • This paradigm is the most general one where the user has full control. Better parallel efficiency is usually achieved by explicit message passing. However, message-passing programming is more difficult.
    • +
    +
    +
    + +
    +

    What is vectorization?

    +

    Vectorization is a special +case of Single Instructions Multiple Data (SIMD) to denote a single +instruction stream capable of operating on multiple data elements in +parallel. +We can think of vectorization as the unrolling of loops accompanied with SIMD instructions. +

    + +

    Vectorization is the process of converting an algorithm that performs scalar operations +(typically one operation at the time) to vector operations where a single operation can refer to many simultaneous operations. +Consider the following example +

    + + +
    +
    +
    +
    +
    +
    for (i = 0; i < n; i++){
    +    a[i] = b[i] + c[i];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    If the code is not vectorized, the compiler will simply start with the first element and +then perform subsequent additions operating on one address in memory at the time. +

    +
    + +
    +

    Number of elements that can acted upon

    +

    A SIMD instruction can operate on multiple data elements in one single instruction. +It uses the so-called 128-bit SIMD floating-point register. +In this sense, vectorization adds some form of parallelism since one instruction is applied +to many parts of say a vector. +

    + +

    The number of elements which can be operated on in parallel +range from four single-precision floating point data elements in so-called +Streaming SIMD Extensions and two double-precision floating-point data +elements in Streaming SIMD Extensions 2 to sixteen byte operations in +a 128-bit register in Streaming SIMD Extensions 2. Thus, vector-length +ranges from 2 to 16, depending on the instruction extensions used and +on the data type. +

    + +

    IN summary, our instructions operate on 128 bit (16 byte) operands

    + +
    + +
    +

    Number of elements that can acted upon, examples

    +

    We start with the simple scalar operations given by

    + + +
    +
    +
    +
    +
    +
    for (i = 0; i < n; i++){
    +    a[i] = b[i] + c[i];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    If the code is not vectorized and we have a 128-bit register to store a 32 bits floating point number, +it means that we have \( 3\times 32 \) bits that are not used. +

    + +

    We have thus unused space in our SIMD registers. These registers could hold three additional integers.

    +
    + +
    +

    Operation counts for scalar operation

    +

    The code

    + + +
    +
    +
    +
    +
    +
    for (i = 0; i < n; i++){
    +    a[i] = b[i] + c[i];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    has for \( n \) repeats

    +
      +

    1. one load for \( c[i] \) in address 1
    2. +

    3. one load for \( b[i] \) in address 2
    4. +

    5. add \( c[i] \) and \( b[i] \) to give \( a[i] \)
    6. +

    7. store \( a[i] \) in address 2
    8. +
    +
    + +
    +

    Number of elements that can acted upon, examples

    +

    If we vectorize the code, we can perform, with a 128-bit register four simultaneous operations, that is +we have +

    + + +
    +
    +
    +
    +
    +
    for (i = 0; i < n; i+=4){
    +    a[i] = b[i] + c[i];
    +    a[i+1] = b[i+1] + c[i+1];
    +    a[i+2] = b[i+2] + c[i+2];
    +    a[i+3] = b[i+3] + c[i+3];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Four additions are now done in a single step.

    +
    + +
    +

    Number of operations when vectorized

    +

    For \( n/4 \) repeats assuming floats or integers

    +
      +

    1. one vector load for \( c[i] \) in address 1
    2. +

    3. one load for \( b[i] \) in address 2
    4. +

    5. add \( c[i] \) and \( b[i] \) to give \( a[i] \)
    6. +

    7. store \( a[i] \) in address 2
    8. +
    +
    + +
    +

    A simple test case with and without vectorization

    +

    We implement these operations in a simple c++ program that computes at the end the norm of a vector.

    + + + +
    +
    +
    +
    +
    +
    #include <cstdlib>
    +#include <iostream>
    +#include <cmath>
    +#include <iomanip>
    +#include "time.h"
    +
    +using namespace std; // note use of namespace
    +int main (int argc, char* argv[])
    +{
    +  // read in dimension of square matrix
    +  int n = atoi(argv[1]);
    +  double s = 1.0/sqrt( (double) n);
    +  double *a, *b, *c;
    +  // Start timing
    +  clock_t start, finish;
    +  start = clock();
    +// Allocate space for the vectors to be used
    +    a = new double [n]; b = new double [n]; c = new double [n];
    +  // Define parallel region
    +  // Set up values for vectors  a and b
    +  for (int i = 0; i < n; i++){
    +    double angle = 2.0*M_PI*i/ (( double ) n);
    +    a[i] = s*(sin(angle) + cos(angle));
    +    b[i] =  s*sin(2.0*angle);
    +    c[i] = 0.0;
    +  }
    +  // Then perform the vector addition
    +  for (int i = 0; i < n; i++){
    +    c[i] += a[i]+b[i];
    +  }
    +  // Compute now the norm-2
    +  double Norm2 = 0.0;
    +  for (int i = 0; i < n; i++){
    +    Norm2  += c[i]*c[i];
    +  }
    +  finish = clock();
    +  double timeused = (double) (finish - start)/(CLOCKS_PER_SEC );
    +  cout << setiosflags(ios::showpoint | ios::uppercase);
    +  cout << setprecision(10) << setw(20) << "Time used  for norm computation=" << timeused  << endl;
    +  cout << "  Norm-2  = " << Norm2 << endl;
    +  // Free up space
    +  delete[] a;
    +  delete[] b;
    +  delete[] c;
    +  return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Compiling with and without vectorization

    +

    We can compile and link without vectorization using the clang c++ compiler

    + + +
    +
    +
    +
    +
    +
    clang -o novec.x vecexample.cpp
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    and with vectorization (and additional optimizations)

    + + +
    +
    +
    +
    +
    +
    clang++ -O3 -Rpass=loop-vectorize -o  vec.x vecexample.cpp 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The speedup depends on the size of the vectors. In the example here we have run with \( 10^7 \) elements. +The example here was run on an IMac17.1 with OSX El Capitan (10.11.4) as operating system and an Intel i5 3.3 GHz CPU. +

    + + +
    +
    +
    +
    +
    +
    Compphys:~ hjensen$ ./vec.x 10000000
    +Time used  for norm computation=0.04720500000
    +Compphys:~ hjensen$ ./novec.x 10000000
    +Time used  for norm computation=0.03311700000
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    This particular C++ compiler speeds up the above loop operations with a factor of 1.5 +Performing the same operations for \( 10^9 \) elements results in a smaller speedup since reading from main memory is required. The non-vectorized code is seemingly faster. +

    + + +
    +
    +
    +
    +
    +
    Compphys:~ hjensen$ ./vec.x 1000000000
    +Time used  for norm computation=58.41391100
    +Compphys:~ hjensen$ ./novec.x 1000000000
    +Time used  for norm computation=46.51295300
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    We will discuss these issues further in the next slides.

    +
    + +
    +

    Compiling with and without vectorization using clang

    +

    We can compile and link without vectorization with clang compiler

    + + +
    +
    +
    +
    +
    +
    clang++ -o -fno-vectorize novec.x vecexample.cpp
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    and with vectorization

    + + +
    +
    +
    +
    +
    +
    clang++ -O3 -Rpass=loop-vectorize -o  vec.x vecexample.cpp 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    We can also add vectorization analysis, see for example

    + + +
    +
    +
    +
    +
    +
    clang++ -O3 -Rpass-analysis=loop-vectorize -o  vec.x vecexample.cpp 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    or figure out if vectorization was missed

    + + +
    +
    +
    +
    +
    +
    clang++ -O3 -Rpass-missed=loop-vectorize -o  vec.x vecexample.cpp 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Automatic vectorization and vectorization inhibitors, criteria

    + +

    Not all loops can be vectorized, as discussed in Intel's guide to vectorization

    + +

    An important criteria is that the loop counter \( n \) is known at the entry of the loop.

    + + +
    +
    +
    +
    +
    +
      for (int j = 0; j < n; j++) {
    +    a[j] = cos(j*1.0);
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The variable \( n \) does need to be known at compile time. However, this variable must stay the same for the entire duration of the loop. It implies that an exit statement inside the loop cannot be data dependent.

    +
    + +
    +

    Automatic vectorization and vectorization inhibitors, exit criteria

    + +

    An exit statement should in general be avoided. +If the exit statement contains data-dependent conditions, the loop cannot be vectorized. +The following is an example of a non-vectorizable loop +

    + + +
    +
    +
    +
    +
    +
      for (int j = 0; j < n; j++) {
    +    a[j] = cos(j*1.0);
    +    if (a[j] < 0 ) break;
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Avoid loop termination conditions and opt for a single entry loop variable \( n \). The lower and upper bounds have to be kept fixed within the loop.

    +
    + +
    +

    Automatic vectorization and vectorization inhibitors, straight-line code

    + +

    SIMD instructions perform the same type of operations multiple times. +A switch statement leads thus to a non-vectorizable loop since different statemens cannot branch. +The following code can however be vectorized since the if statement is implemented as a masked assignment. +

    + + +
    +
    +
    +
    +
    +
      for (int j = 0; j < n; j++) {
    +    double x  = cos(j*1.0);
    +    if (x > 0 ) {
    +       a[j] =  x*sin(j*2.0); 
    +    }
    +    else {
    +       a[j] = 0.0;
    +    }
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    These operations can be performed for all data elements but only those elements which the mask evaluates as true are stored. In general, one should avoid branches such as switch, go to, or return statements or if constructs that cannot be treated as masked assignments.

    +
    + +
    +

    Automatic vectorization and vectorization inhibitors, nested loops

    + +

    Only the innermost loop of the following example is vectorized

    + + +
    +
    +
    +
    +
    +
      for (int i = 0; i < n; i++) {
    +      for (int j = 0; j < n; j++) {
    +           a[i][j] += b[i][j];
    +      }  
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The exception is if an original outer loop is transformed into an inner loop as the result of compiler optimizations.

    +
    + +
    +

    Automatic vectorization and vectorization inhibitors, function calls

    + +

    Calls to programmer defined functions ruin vectorization. However, calls to intrinsic functions like +\( \sin{x} \), \( \cos{x} \), \( \exp{x} \) etc are allowed since they are normally efficiently vectorized. +The following example is fully vectorizable +

    + + +
    +
    +
    +
    +
    +
      for (int i = 0; i < n; i++) {
    +      a[i] = log10(i)*cos(i);
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Similarly, inline functions defined by the programmer, allow for vectorization since the function statements are glued into the actual place where the function is called.

    +
    + +
    +

    Automatic vectorization and vectorization inhibitors, data dependencies

    + +

    One has to keep in mind that vectorization changes the order of operations inside a loop. A so-called +read-after-write statement with an explicit flow dependency cannot be vectorized. The following code +

    + + +
    +
    +
    +
    +
    +
      double b = 15.;
    +  for (int i = 1; i < n; i++) {
    +      a[i] = a[i-1] + b;
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    is an example of flow dependency and results in wrong numerical results if vectorized. For a scalar operation, the value \( a[i-1] \) computed during the iteration is loaded into the right-hand side and the results are fine. In vector mode however, with a vector length of four, the values \( a[0] \), \( a[1] \), \( a[2] \) and \( a[3] \) from the previous loop will be loaded into the right-hand side and produce wrong results. That is, we have

    + + +
    +
    +
    +
    +
    +
       a[1] = a[0] + b;
    +   a[2] = a[1] + b;
    +   a[3] = a[2] + b;
    +   a[4] = a[3] + b;
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    and if the two first iterations are executed at the same by the SIMD instruction, the value of say \( a[1] \) could be used by the second iteration before it has been calculated by the first iteration, leading thereby to wrong results.

    +
    + +
    +

    Automatic vectorization and vectorization inhibitors, more data dependencies

    + +

    On the other hand, a so-called +write-after-read statement can be vectorized. The following code +

    + + +
    +
    +
    +
    +
    +
      double b = 15.;
    +  for (int i = 1; i < n; i++) {
    +      a[i-1] = a[i] + b;
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    is an example of flow dependency that can be vectorized since no iteration with a higher value of \( i \) +can complete before an iteration with a lower value of \( i \). However, such code leads to problems with parallelization. +

    +
    + +
    +

    Automatic vectorization and vectorization inhibitors, memory stride

    + +

    For C++ programmers it is also worth keeping in mind that an array notation is preferred to the more compact use of pointers to access array elements. The compiler can often not tell if it is safe to vectorize the code.

    + +

    When dealing with arrays, you should also avoid memory stride, since this slows down considerably vectorization. When you access array element, write for example the inner loop to vectorize using unit stride, that is, access successively the next array element in memory, as shown here

    + + +
    +
    +
    +
    +
    +
      for (int i = 0; i < n; i++) {
    +      for (int j = 0; j < n; j++) {
    +           a[i][j] += b[i][j];
    +      }  
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Memory management

    +

    The main memory contains the program data

    +
      +

    1. Cache memory contains a copy of the main memory data
    2. +

    3. Cache is faster but consumes more space and power. It is normally assumed to be much faster than main memory
    4. +

    5. Registers contain working data only
    6. +
        +

      • Modern CPUs perform most or all operations only on data in register
      • +
      +

      +

    7. Multiple Cache memories contain a copy of the main memory data
    8. +
        +

      • Cache items accessed by their address in main memory
      • +

      • L1 cache is the fastest but has the least capacity
      • +

      • L2, L3 provide intermediate performance/size tradeoffs
      • +
      +

      +

    +

    +

    Loads and stores to memory can be as important as floating point operations when we measure performance.

    +
    + +
    +

    Memory and communication

    + +
      +

    1. Most communication in a computer is carried out in chunks, blocks of bytes of data that move together
    2. +

    3. In the memory hierarchy, data moves between memory and cache, and between different levels of cache, in groups called lines
    4. +
        +

      • Lines are typically 64-128 bytes, or 8-16 double precision words
      • +

      • Even if you do not use the data, it is moved and occupies space in the cache
      • +
      +

      +

    +

    +

    Many of these performance features are not captured in most programming languages.

    +
    + +
    +

    Measuring performance

    + +

    How do we measure performance? What is wrong with this code to time a loop?

    + + +
    +
    +
    +
    +
    +
      clock_t start, finish;
    +  start = clock();
    +  for (int j = 0; j < i; j++) {
    +    a[j] = b[j]+b[j]*c[j];
    +  }
    +  finish = clock();
    +  double timeused = (double) (finish - start)/(CLOCKS_PER_SEC );
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Problems with measuring time

    +
      +

    1. Timers are not infinitely accurate
    2. +

    3. All clocks have a granularity, the minimum time that they can measure
    4. +

    5. The error in a time measurement, even if everything is perfect, may be the size of this granularity (sometimes called a clock tick)
    6. +

    7. Always know what your clock granularity is
    8. +

    9. Ensure that your measurement is for a long enough duration (say 100 times the tick)
    10. +
    +
    + +
    +

    Problems with cold start

    + +

    What happens when the code is executed? The assumption is that the code is ready to +execute. But +

    +
      +

    1. Code may still be on disk, and not even read into memory.
    2. +

    3. Data may be in slow memory rather than fast (which may be wrong or right for what you are measuring)
    4. +

    5. Multiple tests often necessary to ensure that cold start effects are not present
    6. +

    7. Special effort often required to ensure data in the intended part of the memory hierarchy.
    8. +
    +
    + +
    +

    Problems with smart compilers

    + +
      +

    1. If the result of the computation is not used, the compiler may eliminate the code
    2. +

    3. Performance will look impossibly fantastic
    4. +

    5. Even worse, eliminate some of the code so the performance looks plausible
    6. +

    7. Ensure that the results are (or may be) used.
    8. +
    +
    + +
    +

    Problems with interference

    +
      +

    1. Other activities are sharing your processor
    2. +
        + +

      • Operating system, system demons, other users
      • + +

      • Some parts of the hardware do not always perform with exactly the same performance
      • +
      +

      +

    3. Make multiple tests and report
    4. +

    5. Easy choices include
    6. +
        + +

      • Average tests represent what users might observe over time
      • +
      +

      +

    +
    + +
    +

    Problems with measuring performance

    +
      +

    1. Accurate, reproducible performance measurement is hard
    2. +

    3. Think carefully about your experiment:
    4. +

    5. What is it, precisely, that you want to measure?
    6. +

    7. How representative is your test to the situation that you are trying to measure?
    8. +
    +
    + +
    +

    Thomas algorithm for tridiagonal linear algebra equations

    +
    + +

    +

     
    +$$ +\left( \begin{array}{ccccc} + b_0 & c_0 & & & \\ + a_0 & b_1 & c_1 & & \\ + & & \ddots & & \\ + & & a_{m-3} & b_{m-2} & c_{m-2} \\ + & & & a_{m-2} & b_{m-1} + \end{array} \right) +\left( \begin{array}{c} + x_0 \\ + x_1 \\ + \vdots \\ + x_{m-2} \\ + x_{m-1} + \end{array} \right)=\left( \begin{array}{c} + f_0 \\ + f_1 \\ + \vdots \\ + f_{m-2} \\ + f_{m-1} \\ + \end{array} \right) +$$ +

     
    +

    +
    + +
    +

    Thomas algorithm, forward substitution

    +
    + +

    +

    The first step is to multiply the first row by \( a_0/b_0 \) and subtract it from the second row. This is known as the forward substitution step. We obtain then

    +

     
    +$$ + a_i = 0, +$$ +

     
    + +

     
    +$$ + b_i = b_i - \frac{a_{i-1}}{b_{i-1}}c_{i-1}, +$$ +

     
    + +

    and

    +

     
    +$$ + f_i = f_i - \frac{a_{i-1}}{b_{i-1}}f_{i-1}. +$$ +

     
    + +

    At this point the simplified equation, with only an upper triangular matrix takes the form

    +

     
    +$$ +\left( \begin{array}{ccccc} + b_0 & c_0 & & & \\ + & b_1 & c_1 & & \\ + & & \ddots & & \\ + & & & b_{m-2} & c_{m-2} \\ + & & & & b_{m-1} + \end{array} \right)\left( \begin{array}{c} + x_0 \\ + x_1 \\ + \vdots \\ + x_{m-2} \\ + x_{m-1} + \end{array} \right)=\left( \begin{array}{c} + f_0 \\ + f_1 \\ + \vdots \\ + f_{m-2} \\ + f_{m-1} \\ + \end{array} \right) +$$ +

     
    +

    +
    + +
    +

    Thomas algorithm, backward substitution

    +
    + +

    +

    The next step is the backward substitution step. The last row is multiplied by \( c_{N-3}/b_{N-2} \) and subtracted from the second to last row, thus eliminating \( c_{N-3} \) from the last row. The general backward substitution procedure is

    +

     
    +$$ + c_i = 0, +$$ +

     
    + +

    and

    +

     
    +$$ + f_{i-1} = f_{i-1} - \frac{c_{i-1}}{b_i}f_i +$$ +

     
    + +

    All that ramains to be computed is the solution, which is the very straight forward process of

    +

     
    +$$ +x_i = \frac{f_i}{b_i} +$$ +

     
    +

    +
    + +
    +

    Thomas algorithm and counting of operations (floating point and memory)

    +
    + +

    + +

    We have in specific case the following operations with the floating operations

    + +
      +

    • Memory Reads: \( 14(N-2) \);
    • +

    • Memory Writes: \( 4(N-2) \);
    • +

    • Subtractions: \( 3(N-2) \);
    • +

    • Multiplications: \( 3(N-2) \);
    • +

    • Divisions: \( 4(N-2) \).
    • +
    +
    + + +
    + +

    + + +

    +
    +
    +
    +
    +
    // Forward substitution    
    +// Note that we can simplify by precalculating a[i-1]/b[i-1]
    +  for (int i=1; i < n; i++) {
    +     b[i] = b[i] - (a[i-1]*c[i-1])/b[i-1];
    +     f[i] = g[i] - (a[i-1]*f[i-1])/b[i-1];
    +  }
    +  x[n-1] = f[n-1] / b[n-1];
    +  // Backwards substitution                                                           
    +  for (int i = n-2; i >= 0; i--) {
    +     f[i] = f[i] - c[i]*f[i+1]/b[i+1];
    +     x[i] = f[i]/b[i];
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Example: Transpose of a matrix

    + + + +
    +
    +
    +
    +
    +
    #include <cstdlib>
    +#include <iostream>
    +#include <cmath>
    +#include <iomanip>
    +#include "time.h"
    +
    +using namespace std; // note use of namespace
    +int main (int argc, char* argv[])
    +{
    +  // read in dimension of square matrix
    +  int n = atoi(argv[1]);
    +  double **A, **B;
    +  // Allocate space for the two matrices
    +  A = new double*[n]; B = new double*[n];
    +  for (int i = 0; i < n; i++){
    +    A[i] = new double[n];
    +    B[i] = new double[n];
    +  }
    +  // Set up values for matrix A
    +  for (int i = 0; i < n; i++){
    +    for (int j = 0; j < n; j++) {
    +      A[i][j] =  cos(i*1.0)*sin(j*3.0);
    +    }
    +  }
    +  clock_t start, finish;
    +  start = clock();
    +  // Then compute the transpose
    +  for (int i = 0; i < n; i++){
    +    for (int j = 0; j < n; j++) {
    +      B[i][j]= A[j][i];
    +    }
    +  }
    +
    +  finish = clock();
    +  double timeused = (double) (finish - start)/(CLOCKS_PER_SEC );
    +  cout << setiosflags(ios::showpoint | ios::uppercase);
    +  cout << setprecision(10) << setw(20) << "Time used  for setting up transpose of matrix=" << timeused  << endl;
    +
    +  // Free up space
    +  for (int i = 0; i < n; i++){
    +    delete[] A[i];
    +    delete[] B[i];
    +  }
    +  delete[] A;
    +  delete[] B;
    +  return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Matrix-matrix multiplication

    +

    This the matrix-matrix multiplication code with plain c++ memory allocation. It computes at the end the Frobenius norm.

    + + + +
    +
    +
    +
    +
    +
    #include <cstdlib>
    +#include <iostream>
    +#include <cmath>
    +#include <iomanip>
    +#include "time.h"
    +
    +using namespace std; // note use of namespace
    +int main (int argc, char* argv[])
    +{
    +  // read in dimension of square matrix
    +  int n = atoi(argv[1]);
    +  double s = 1.0/sqrt( (double) n);
    +  double **A, **B, **C;
    +  // Start timing
    +  clock_t start, finish;
    +  start = clock();
    +  // Allocate space for the two matrices
    +  A = new double*[n]; B = new double*[n]; C = new double*[n];
    +  for (int i = 0; i < n; i++){
    +    A[i] = new double[n];
    +    B[i] = new double[n];
    +    C[i] = new double[n];
    +  }
    +  // Set up values for matrix A and B and zero matrix C
    +  for (int i = 0; i < n; i++){
    +    for (int j = 0; j < n; j++) {
    +      double angle = 2.0*M_PI*i*j/ (( double ) n);
    +      A[i][j] = s * ( sin ( angle ) + cos ( angle ) );
    +      B[j][i] =  A[i][j];
    +    }
    +  }
    +  // Then perform the matrix-matrix multiplication
    +  for (int i = 0; i < n; i++){
    +    for (int j = 0; j < n; j++) {
    +      double sum = 0.0;
    +       for (int k = 0; k < n; k++) {
    +           sum += B[i][k]*A[k][j];
    +       }
    +       C[i][j] = sum;
    +    }
    +  }
    +  // Compute now the Frobenius norm
    +  double Fsum = 0.0;
    +  for (int i = 0; i < n; i++){
    +    for (int j = 0; j < n; j++) {
    +      Fsum += C[i][j]*C[i][j];
    +    }
    +  }
    +  Fsum = sqrt(Fsum);
    +  finish = clock();
    +  double timeused = (double) (finish - start)/(CLOCKS_PER_SEC );
    +  cout << setiosflags(ios::showpoint | ios::uppercase);
    +  cout << setprecision(10) << setw(20) << "Time used  for matrix-matrix multiplication=" << timeused  << endl;
    +  cout << "  Frobenius norm  = " << Fsum << endl;
    +  // Free up space
    +  for (int i = 0; i < n; i++){
    +    delete[] A[i];
    +    delete[] B[i];
    +    delete[] C[i];
    +  }
    +  delete[] A;
    +  delete[] B;
    +  delete[] C;
    +  return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    How do we define speedup? Simplest form

    +
    + +

    +

      +

    • Speedup measures the ratio of performance between two objects
    • +

    • Versions of same code, with different number of processors
    • +

    • Serial and vector versions
    • +

    • Try different programing languages, c++ and Fortran
    • +

    • Two algorithms computing the same result
    • +
    +
    +
    + +
    +

    How do we define speedup? Correct baseline

    +
    + +

    +

    The key is choosing the correct baseline for comparison

    +
      +

    • For our serial vs. vectorization examples, using compiler-provided vectorization, the baseline is simple; the same code, with vectorization turned off
    • +
        +

      • For parallel applications, this is much harder:
      • +
          + +

        • Choice of algorithm, decomposition, performance of baseline case etc.
        • +
        +

        +

      +

      +

    +
    +
    + +
    +

    Parallel speedup

    +
    + +

    +

    For parallel applications, speedup is typically defined as

    +
      +

    • Speedup \( =T_1/T_p \)
    • +
    +

    +

    Here \( T_1 \) is the time on one processor and \( T_p \) is the time using \( p \) processors.

    +
      +

    • Can the speedup become larger than \( p \)? That means using \( p \) processors is more than \( p \) times faster than using one processor.
    • +
    +
    +
    + +
    +

    Speedup and memory

    +
    + +

    +

    The speedup on \( p \) processors can +be greater than \( p \) if memory usage is optimal! +Consider the case of a memorybound computation with \( M \) words of memory +

    +
      +

    • If \( M/p \) fits into cache while \( M \) does not, the time to access memory will be different in the two cases:
    • +

    • \( T_1 \) uses the main memory bandwidth
    • +

    • \( T_p \) uses the appropriate cache bandwidth
    • +
    +
    +
    + +
    +

    Upper bounds on speedup

    +
    + +

    +

    Assume that almost all parts of a code are perfectly +parallelizable (fraction \( f \)). The remainder, +fraction \( (1-f) \) cannot be parallelized at all. +

    + +

    That is, there is work that takes time \( W \) on one process; a fraction \( f \) of that work will take +time \( Wf/p \) on \( p \) processors. +

    +
      +

    • What is the maximum possible speedup as a function of \( f \)?
    • +
    +
    +
    + +
    +

    Amdahl's law

    +
    + +

    +

    On one processor we have

    +

     
    +$$ +T_1 = (1-f)W + fW = W +$$ +

     
    + +

    On \( p \) processors we have

    +

     
    +$$ +T_p = (1-f)W + \frac{fW}{p}, +$$ +

     
    + +

    resulting in a speedup of

    +

     
    +$$ +\frac{T_1}{T_p} = \frac{W}{(1-f)W+fW/p} +$$ +

     
    + +

    As \( p \) goes to infinity, \( fW/p \) goes to zero, and the maximum speedup is

    +

     
    +$$ +\frac{1}{1-f}, +$$ +

     
    + +

    meaning that if +if \( f = 0.99 \) (all but \( 1\% \) parallelizable), the maximum speedup +is \( 1/(1-.99)=100 \)! +

    +
    +
    + +
    +

    How much is parallelizable

    +
    + +

    +

    If any non-parallel code slips into the +application, the parallel +performance is limited. +

    + +

    In many simulations, however, the fraction of non-parallelizable work +is \( 10^{-6} \) or less due to large arrays or objects that are perfectly parallelizable. +

    +
    +
    + +
    +

    Today's situation of parallel computing

    +
    + +

    + +

      +

    • Distributed memory is the dominant hardware configuration. There is a large diversity in these machines, from MPP (massively parallel processing) systems to clusters of off-the-shelf PCs, which are very cost-effective.
    • +

    • Message-passing is a mature programming paradigm and widely accepted. It often provides an efficient match to the hardware. It is primarily used for the distributed memory systems, but can also be used on shared memory systems.
    • +

    • Modern nodes have nowadays several cores, which makes it interesting to use both shared memory (the given node) and distributed memory (several nodes with communication). This leads often to codes which use both MPI and OpenMP.
    • +
    +

    +

    Our lectures will focus on both MPI and OpenMP.

    +
    +
    + +
    +

    Overhead present in parallel computing

    +
    + +

    + +

      +

    • Uneven load balance: not all the processors can perform useful work at all time.
    • +

    • Overhead of synchronization
    • +

    • Overhead of communication
    • +

    • Extra computation due to parallelization
    • +
    +

    +

    Due to the above overhead and that certain parts of a sequential +algorithm cannot be parallelized we may not achieve an optimal parallelization. +

    +
    +
    + +
    +

    Parallelizing a sequential algorithm

    +
    + +

    + +

      +

    • Identify the part(s) of a sequential algorithm that can be executed in parallel. This is the difficult part,
    • +

    • Distribute the global work and data among \( P \) processors.
    • +
    +
    +
    + +
    +

    Strategies

    +
    + +

    +

      +

    • Develop codes locally, run with some few processes and test your codes. Do benchmarking, timing and so forth on local nodes, for example your laptop or PC.
    • +

    • When you are convinced that your codes run correctly, you can start your production runs on available supercomputers.
    • +
    +
    +
    + +
    +

    How do I run MPI on a PC/Laptop? MPI

    +
    + +

    +

    To install MPI is rather easy on hardware running unix/linux as operating systems, follow simply the instructions from the OpenMPI website. See also subsequent slides. +When you have made sure you have installed MPI on your PC/laptop, +

    +
      +

    • Compile with mpicxx/mpic++ or mpif90
    • +
    +

    + + +

    +
    +
    +
    +
    +
      # Compile and link
    +  mpic++ -O3 -o nameofprog.x nameofprog.cpp
    +  #  run code with for example 8 processes using mpirun/mpiexec
    +  mpiexec -n 8 ./nameofprog.x
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Can I do it on my own PC/laptop? OpenMP installation

    +
    + +

    +

    If you wish to install MPI and OpenMP +on your laptop/PC, we recommend the following: +

    + +
      +

    • For OpenMP, the compile option -fopenmp is included automatically in recent versions of the C++ compiler and Fortran compilers. For users of different Linux distributions, simply use the available C++ or Fortran compilers and add the above compiler instructions, see also code examples below.
    • +

    • For OS X users however, install libomp
    • +
    +

    + + +

    +
    +
    +
    +
    +
      brew install libomp
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    and compile and link as

    + + +
    +
    +
    +
    +
    +
    c++ -o <name executable> <name program.cpp>  -lomp
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Installing MPI

    +
    + +

    +

    For linux/ubuntu users, you need to install two packages (alternatively use the synaptic package manager)

    + + +
    +
    +
    +
    +
    +
      sudo apt-get install libopenmpi-dev
    +  sudo apt-get install openmpi-bin
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    For OS X users, install brew (after having installed xcode and gcc, needed for the +gfortran compiler of openmpi) and then install with brew +

    + + +
    +
    +
    +
    +
    +
       brew install openmpi
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    When running an executable (code.x), run as

    + + +
    +
    +
    +
    +
    +
      mpirun -n 10 ./code.x
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    where we indicate that we want the number of processes to be 10.

    +
    +
    + +
    +

    Installing MPI and using Qt

    +
    + +

    +

    With openmpi installed, when using Qt, add to your .pro file the instructions here

    + +

    You may need to tell Qt where openmpi is stored.

    +
    +
    + +
    +

    What is Message Passing Interface (MPI)?

    +
    + +

    + +

    MPI is a library, not a language. It specifies the names, calling sequences and results of functions +or subroutines to be called from C/C++ or Fortran programs, and the classes and methods that make up the MPI C++ +library. The programs that users write in Fortran, C or C++ are compiled with ordinary compilers and linked +with the MPI library. +

    + +

    MPI programs should be able to run +on all possible machines and run all MPI implementetations without change. +

    + +

    An MPI computation is a collection of processes communicating with messages.

    +
    +
    + +
    +

    Going Parallel with MPI

    +
    + +

    +

    Task parallelism: the work of a global problem can be divided +into a number of independent tasks, which rarely need to synchronize. +Monte Carlo simulations or numerical integration are examples of this. +

    + +

    MPI is a message-passing library where all the routines +have corresponding C/C++-binding +

    + + +
    +
    +
    +
    +
    +
       MPI_Command_name
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    and Fortran-binding (routine names are in uppercase, but can also be in lower case)

    + + +
    +
    +
    +
    +
    +
       MPI_COMMAND_NAME
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    MPI is a library

    +
    + +

    +

    MPI is a library specification for the message passing interface, +proposed as a standard. +

    + +
      +

    • independent of hardware;
    • +

    • not a language or compiler specification;
    • +

    • not a specific implementation or product.
    • +
    +

    +

    A message passing standard for portability and ease-of-use. +Designed for high performance. +

    + +

    Insert communication and synchronization functions where necessary.

    +
    +
    + +
    +

    Bindings to MPI routines

    +
    + +

    + +

    MPI is a message-passing library where all the routines +have corresponding C/C++-binding +

    + + +
    +
    +
    +
    +
    +
       MPI_Command_name
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    and Fortran-binding (routine names are in uppercase, but can also be in lower case)

    + + +
    +
    +
    +
    +
    +
       MPI_COMMAND_NAME
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The discussion in these slides focuses on the C++ binding.

    +
    +
    + +
    +

    Communicator

    +
    + +

    +

      +

    • A group of MPI processes with a name (context).
    • +

    • Any process is identified by its rank. The rank is only meaningful within a particular communicator.
    • +

    • By default the communicator contains all the MPI processes.
    • +
    +

    + + +

    +
    +
    +
    +
    +
      MPI_COMM_WORLD 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +

    • Mechanism to identify subset of processes.
    • +

    • Promotes modular design of parallel libraries.
    • +
    +
    +
    + +
    +

    Some of the most important MPI functions

    +
    + +

    + +

      +

    • \( MPI\_Init \) - initiate an MPI computation
    • +

    • \( MPI\_Finalize \) - terminate the MPI computation and clean up
    • +

    • \( MPI\_Comm\_size \) - how many processes participate in a given MPI communicator?
    • +

    • \( MPI\_Comm\_rank \) - which one am I? (A number between 0 and size-1.)
    • +

    • \( MPI\_Send \) - send a message to a particular process within an MPI communicator
    • +

    • \( MPI\_Recv \) - receive a message from a particular process within an MPI communicator
    • +

    • \( MPI\_reduce \) or \( MPI\_Allreduce \), send and receive messages
    • +
    +
    +
    + +
    +

    The first MPI C/C++ program

    +
    + +

    + +

    Let every process write "Hello world" (oh not this program again!!) on the standard output.

    + + +
    +
    +
    +
    +
    +
    using namespace std;
    +#include <mpi.h>
    +#include <iostream>
    +int main (int nargs, char* args[])
    +{
    +int numprocs, my_rank;
    +//   MPI initializations
    +MPI_Init (&nargs, &args);
    +MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
    +MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);
    +cout << "Hello world, I have  rank " << my_rank << " out of " 
    +     << numprocs << endl;
    +//  End MPI
    +MPI_Finalize ();
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    The Fortran program

    +
    + +

    + + +

    +
    +
    +
    +
    +
    PROGRAM hello
    +INCLUDE "mpif.h"
    +INTEGER:: size, my_rank, ierr
    +
    +CALL  MPI_INIT(ierr)
    +CALL MPI_COMM_SIZE(MPI_COMM_WORLD, size, ierr)
    +CALL MPI_COMM_RANK(MPI_COMM_WORLD, my_rank, ierr)
    +WRITE(*,*)"Hello world, I've rank ",my_rank," out of ",size
    +CALL MPI_FINALIZE(ierr)
    +
    +END PROGRAM hello
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Note 1

    +
    + +

    + +

      +

    • The output to screen is not ordered since all processes are trying to write to screen simultaneously.
    • +

    • It is the operating system which opts for an ordering.
    • + +

    • If we wish to have an organized output, starting from the first process, we may rewrite our program as in the next example.
    • +
    +
    +
    + +
    +

    Ordered output with MPIBarrier

    +
    + +

    + + + +

    +
    +
    +
    +
    +
    int main (int nargs, char* args[])
    +{
    + int numprocs, my_rank, i;
    + MPI_Init (&nargs, &args);
    + MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
    + MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);
    + for (i = 0; i < numprocs; i++) {}
    + MPI_Barrier (MPI_COMM_WORLD);
    + if (i == my_rank) {
    + cout << "Hello world, I have  rank " << my_rank << 
    +        " out of " << numprocs << endl;}
    +      MPI_Finalize ();
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Note 2

    +
    + +

    +

      +

    • Here we have used the \( MPI\_Barrier \) function to ensure that that every process has completed its set of instructions in a particular order.
    • +

    • A barrier is a special collective operation that does not allow the processes to continue until all processes in the communicator (here \( MPI\_COMM\_WORLD \)) have called \( MPI\_Barrier \).
    • +

    • The barriers make sure that all processes have reached the same point in the code. Many of the collective operations like \( MPI\_ALLREDUCE \) to be discussed later, have the same property; that is, no process can exit the operation until all processes have started.
    • +
    +

    +

    However, this is slightly more time-consuming since the processes synchronize between themselves as many times as there +are processes. In the next Hello world example we use the send and receive functions in order to a have a synchronized +action. +

    +
    +
    + +
    +

    Ordered output

    +
    + +

    + + + +

    +
    +
    +
    +
    +
    .....
    +int numprocs, my_rank, flag;
    +MPI_Status status;
    +MPI_Init (&nargs, &args);
    +MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
    +MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);
    +if (my_rank > 0)
    +MPI_Recv (&flag, 1, MPI_INT, my_rank-1, 100, 
    +           MPI_COMM_WORLD, &status);
    +cout << "Hello world, I have  rank " << my_rank << " out of " 
    +<< numprocs << endl;
    +if (my_rank < numprocs-1)
    +MPI_Send (&my_rank, 1, MPI_INT, my_rank+1, 
    +          100, MPI_COMM_WORLD);
    +MPI_Finalize ();
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Note 3

    +
    + +

    + +

    The basic sending of messages is given by the function \( MPI\_SEND \), which in C/C++ +is defined as +

    + + +
    +
    +
    +
    +
    +
    int MPI_Send(void *buf, int count, 
    +             MPI_Datatype datatype, 
    +             int dest, int tag, MPI_Comm comm)}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    This single command allows the passing of any kind of variable, even a large array, to any group of tasks. +The variable buf is the variable we wish to send while count +is the number of variables we are passing. If we are passing only a single value, this should be 1. +

    + +

    If we transfer an array, it is the overall size of the array. +For example, if we want to send a 10 by 10 array, count would be \( 10\times 10=100 \) +since we are actually passing 100 values. +

    +
    +
    + +
    +

    Note 4

    +
    + +

    + +

    Once you have sent a message, you must receive it on another task. The function \( MPI\_RECV \) +is similar to the send call. +

    + + +
    +
    +
    +
    +
    +
    int MPI_Recv( void *buf, int count, MPI_Datatype datatype, 
    +            int source, 
    +            int tag, MPI_Comm comm, MPI_Status *status )
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The arguments that are different from those in MPI\_SEND are +buf which is the name of the variable where you will be storing the received data, +source which replaces the destination in the send command. This is the return ID of the sender. +

    + +

    Finally, we have used \( MPI\_Status\_status \), +where one can check if the receive was completed. +

    + +

    The output of this code is the same as the previous example, but now +process 0 sends a message to process 1, which forwards it further +to process 2, and so forth. +

    +
    +
    + +
    +

    Numerical integration in parallel

    +
    +Integrating \( \pi \) +

    + +

      +

    • The code example computes \( \pi \) using the trapezoidal rules.
    • +

    • The trapezoidal rule
    • +
    +

    +

     
    +$$ + I=\int_a^bf(x) dx\approx h\left(f(a)/2 + f(a+h) +f(a+2h)+\dots +f(b-h)+ f(b)/2\right). +$$ +

     
    + +

    Click on this link for the full program.

    +
    +
    + +
    +

    Dissection of trapezoidal rule with \( MPI\_reduce \)

    +
    + +

    + + + +

    +
    +
    +
    +
    +
    //    Trapezoidal rule and numerical integration usign MPI
    +using namespace std;
    +#include <mpi.h>
    +#include <iostream>
    +
    +//     Here we define various functions called by the main program
    +
    +double int_function(double );
    +double trapezoidal_rule(double , double , int , double (*)(double));
    +
    +//   Main function begins here
    +int main (int nargs, char* args[])
    +{
    +  int n, local_n, numprocs, my_rank; 
    +  double a, b, h, local_a, local_b, total_sum, local_sum;   
    +  double  time_start, time_end, total_time;
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Dissection of trapezoidal rule

    +
    + +

    + + + +

    +
    +
    +
    +
    +
      //  MPI initializations
    +  MPI_Init (&nargs, &args);
    +  MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
    +  MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);
    +  time_start = MPI_Wtime();
    +  //  Fixed values for a, b and n 
    +  a = 0.0 ; b = 1.0;  n = 1000;
    +  h = (b-a)/n;    // h is the same for all processes 
    +  local_n = n/numprocs;  
    +  // make sure n > numprocs, else integer division gives zero
    +  // Length of each process' interval of
    +  // integration = local_n*h.  
    +  local_a = a + my_rank*local_n*h;
    +  local_b = local_a + local_n*h;
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Integrating with MPI

    +
    + +

    + + + +

    +
    +
    +
    +
    +
      total_sum = 0.0;
    +  local_sum = trapezoidal_rule(local_a, local_b, local_n, 
    +                               &int_function); 
    +  MPI_Reduce(&local_sum, &total_sum, 1, MPI_DOUBLE, 
    +              MPI_SUM, 0, MPI_COMM_WORLD);
    +  time_end = MPI_Wtime();
    +  total_time = time_end-time_start;
    +  if ( my_rank == 0) {
    +    cout << "Trapezoidal rule = " <<  total_sum << endl;
    +    cout << "Time = " <<  total_time  
    +         << " on number of processors: "  << numprocs  << endl;
    +  }
    +  // End MPI
    +  MPI_Finalize ();  
    +  return 0;
    +}  // end of main program
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    How do I use \( MPI\_reduce \)?

    +
    + +

    + +

    Here we have used

    + + +
    +
    +
    +
    +
    +
    MPI_reduce( void *senddata, void* resultdata, int count, 
    +     MPI_Datatype datatype, MPI_Op, int root, MPI_Comm comm)
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The two variables \( senddata \) and \( resultdata \) are obvious, besides the fact that one sends the address +of the variable or the first element of an array. If they are arrays they need to have the same size. +The variable \( count \) represents the total dimensionality, 1 in case of just one variable, +while \( MPI\_Datatype \) +defines the type of variable which is sent and received. +

    + +

    The new feature is \( MPI\_Op \). It defines the type +of operation we want to do. +

    +
    +
    + +
    +

    More on \( MPI\_Reduce \)

    +
    + +

    +

    In our case, since we are summing +the rectangle contributions from every process we define \( MPI\_Op = MPI\_SUM \). +If we have an array or matrix we can search for the largest og smallest element by sending either \( MPI\_MAX \) or +\( MPI\_MIN \). If we want the location as well (which array element) we simply transfer +\( MPI\_MAXLOC \) or \( MPI\_MINOC \). If we want the product we write \( MPI\_PROD \). +

    + +

    \( MPI\_Allreduce \) is defined as

    + + +
    +
    +
    +
    +
    +
    MPI_Allreduce( void *senddata, void* resultdata, int count, 
    +          MPI_Datatype datatype, MPI_Op, MPI_Comm comm)        
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Dissection of trapezoidal rule

    +
    + +

    + +

    We use \( MPI\_reduce \) to collect data from each process. Note also the use of the function +\( MPI\_Wtime \). +

    + + +
    +
    +
    +
    +
    +
    //  this function defines the function to integrate
    +double int_function(double x)
    +{
    +  double value = 4./(1.+x*x);
    +  return value;
    +} // end of function to evaluate
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Dissection of trapezoidal rule

    +
    + +

    + + +

    +
    +
    +
    +
    +
    //  this function defines the trapezoidal rule
    +double trapezoidal_rule(double a, double b, int n, 
    +                         double (*func)(double))
    +{
    +  double trapez_sum;
    +  double fa, fb, x, step;
    +  int    j;
    +  step=(b-a)/((double) n);
    +  fa=(*func)(a)/2. ;
    +  fb=(*func)(b)/2. ;
    +  trapez_sum=0.;
    +  for (j=1; j <= n-1; j++){
    +    x=j*step+a;
    +    trapez_sum+=(*func)(x);
    +  }
    +  trapez_sum=(trapez_sum+fb+fa)*step;
    +  return trapez_sum;
    +}  // end trapezoidal_rule 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    The quantum dot program for two electrons

    +
    + +

    + + +

    +
    +
    +
    +
    +
    // Variational Monte Carlo for atoms with importance sampling, slater det
    +// Test case for 2-electron quantum dot, no classes using Mersenne-Twister RNG
    +#include "mpi.h"
    +#include <cmath>
    +#include <random>
    +#include <string>
    +#include <iostream>
    +#include <fstream>
    +#include <iomanip>
    +#include "vectormatrixclass.h"
    +
    +using namespace  std;
    +// output file as global variable
    +ofstream ofile;  
    +// the step length and its squared inverse for the second derivative 
    +//  Here we define global variables  used in various functions
    +//  These can be changed by using classes
    +int Dimension = 2; 
    +int NumberParticles  = 2;  //  we fix also the number of electrons to be 2
    +
    +// declaration of functions 
    +
    +// The Mc sampling for the variational Monte Carlo 
    +void  MonteCarloSampling(int, double &, double &, Vector &);
    +
    +// The variational wave function
    +double  WaveFunction(Matrix &, Vector &);
    +
    +// The local energy 
    +double  LocalEnergy(Matrix &, Vector &);
    +
    +// The quantum force
    +void  QuantumForce(Matrix &, Matrix &, Vector &);
    +
    +
    +// inline function for single-particle wave function
    +inline double SPwavefunction(double r, double alpha) { 
    +   return exp(-alpha*r*0.5);
    +}
    +
    +// inline function for derivative of single-particle wave function
    +inline double DerivativeSPwavefunction(double r, double alpha) { 
    +  return -r*alpha;
    +}
    +
    +// function for absolute value of relative distance
    +double RelativeDistance(Matrix &r, int i, int j) { 
    +      double r_ij = 0;  
    +      for (int k = 0; k < Dimension; k++) { 
    +	r_ij += (r(i,k)-r(j,k))*(r(i,k)-r(j,k));
    +      }
    +      return sqrt(r_ij); 
    +}
    +
    +// inline function for derivative of Jastrow factor
    +inline double JastrowDerivative(Matrix &r, double beta, int i, int j, int k){
    +  return (r(i,k)-r(j,k))/(RelativeDistance(r, i, j)*pow(1.0+beta*RelativeDistance(r, i, j),2));
    +}
    +
    +// function for square of position of single particle
    +double singleparticle_pos2(Matrix &r, int i) { 
    +    double r_single_particle = 0;
    +    for (int j = 0; j < Dimension; j++) { 
    +      r_single_particle  += r(i,j)*r(i,j);
    +    }
    +    return r_single_particle;
    +}
    +
    +void lnsrch(int n, Vector &xold, double fold, Vector &g, Vector &p, Vector &x,
    +		 double *f, double stpmax, int *check, double (*func)(Vector &p));
    +
    +void dfpmin(Vector &p, int n, double gtol, int *iter, double *fret,
    +	    double(*func)(Vector &p), void (*dfunc)(Vector &p, Vector &g));
    +
    +static double sqrarg;
    +#define SQR(a) ((sqrarg=(a)) == 0.0 ? 0.0 : sqrarg*sqrarg)
    +
    +
    +static double maxarg1,maxarg2;
    +#define FMAX(a,b) (maxarg1=(a),maxarg2=(b),(maxarg1) > (maxarg2) ?\
    +        (maxarg1) : (maxarg2))
    +
    +
    +// Begin of main program   
    +
    +int main(int argc, char* argv[])
    +{
    +
    +  //  MPI initializations
    +  int NumberProcesses, MyRank, NumberMCsamples;
    +  MPI_Init (&argc, &argv);
    +  MPI_Comm_size (MPI_COMM_WORLD, &NumberProcesses);
    +  MPI_Comm_rank (MPI_COMM_WORLD, &MyRank);
    +  double StartTime = MPI_Wtime();
    +  if (MyRank == 0 && argc <= 1) {
    +    cout << "Bad Usage: " << argv[0] << 
    +      " Read also output file on same line and number of Monte Carlo cycles" << endl;
    +  }
    +  // Read filename and number of Monte Carlo cycles from the command line
    +  if (MyRank == 0 && argc > 2) {
    +    string filename = argv[1]; // first command line argument after name of program
    +    NumberMCsamples  = atoi(argv[2]);
    +    string fileout = filename;
    +    string argument = to_string(NumberMCsamples);
    +    // Final filename as filename+NumberMCsamples
    +    fileout.append(argument);
    +    ofile.open(fileout);
    +  }
    +  // broadcast the number of  Monte Carlo samples
    +  MPI_Bcast (&NumberMCsamples, 1, MPI_INT, 0, MPI_COMM_WORLD);
    +  // Two variational parameters only
    +  Vector VariationalParameters(2);
    +  int TotalNumberMCsamples = NumberMCsamples*NumberProcesses; 
    +  // Loop over variational parameters
    +  for (double alpha = 0.5; alpha <= 1.5; alpha +=0.1){
    +    for (double beta = 0.1; beta <= 0.5; beta +=0.05){
    +      VariationalParameters(0) = alpha;  // value of alpha
    +      VariationalParameters(1) = beta;  // value of beta
    +      //  Do the mc sampling  and accumulate data with MPI_Reduce
    +      double TotalEnergy, TotalEnergySquared, LocalProcessEnergy, LocalProcessEnergy2;
    +      LocalProcessEnergy = LocalProcessEnergy2 = 0.0;
    +      MonteCarloSampling(NumberMCsamples, LocalProcessEnergy, LocalProcessEnergy2, VariationalParameters);
    +      //  Collect data in total averages
    +      MPI_Reduce(&LocalProcessEnergy, &TotalEnergy, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
    +      MPI_Reduce(&LocalProcessEnergy2, &TotalEnergySquared, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
    +      // Print out results  in case of Master node, set to MyRank = 0
    +      if ( MyRank == 0) {
    +	double Energy = TotalEnergy/( (double)NumberProcesses);
    +	double Variance = TotalEnergySquared/( (double)NumberProcesses)-Energy*Energy;
    +	double StandardDeviation = sqrt(Variance/((double)TotalNumberMCsamples)); // over optimistic error
    +	ofile << setiosflags(ios::showpoint | ios::uppercase);
    +	ofile << setw(15) << setprecision(8) << VariationalParameters(0);
    +	ofile << setw(15) << setprecision(8) << VariationalParameters(1);
    +	ofile << setw(15) << setprecision(8) << Energy;
    +	ofile << setw(15) << setprecision(8) << Variance;
    +	ofile << setw(15) << setprecision(8) << StandardDeviation << endl;
    +      }
    +    }
    +  }
    +  double EndTime = MPI_Wtime();
    +  double TotalTime = EndTime-StartTime;
    +  if ( MyRank == 0 )  cout << "Time = " <<  TotalTime  << " on number of processors: "  << NumberProcesses  << endl;
    +  if (MyRank == 0)  ofile.close();  // close output file
    +  // End MPI
    +  MPI_Finalize ();  
    +  return 0;
    +}  //  end of main function
    +
    +
    +// Monte Carlo sampling with the Metropolis algorithm  
    +
    +void MonteCarloSampling(int NumberMCsamples, double &cumulative_e, double &cumulative_e2, Vector &VariationalParameters)
    +{
    +
    + // Initialize the seed and call the Mersienne algo
    +  std::random_device rd;
    +  std::mt19937_64 gen(rd());
    +  // Set up the uniform distribution for x \in [[0, 1]
    +  std::uniform_real_distribution<double> UniformNumberGenerator(0.0,1.0);
    +  std::normal_distribution<double> Normaldistribution(0.0,1.0);
    +  // diffusion constant from Schroedinger equation
    +  double D = 0.5; 
    +  double timestep = 0.05;  //  we fix the time step  for the gaussian deviate
    +  // allocate matrices which contain the position of the particles  
    +  Matrix OldPosition( NumberParticles, Dimension), NewPosition( NumberParticles, Dimension);
    +  Matrix OldQuantumForce(NumberParticles, Dimension), NewQuantumForce(NumberParticles, Dimension);
    +  double Energy = 0.0; double EnergySquared = 0.0; double DeltaE = 0.0;
    +  //  initial trial positions
    +  for (int i = 0; i < NumberParticles; i++) { 
    +    for (int j = 0; j < Dimension; j++) {
    +      OldPosition(i,j) = Normaldistribution(gen)*sqrt(timestep);
    +    }
    +  }
    +  double OldWaveFunction = WaveFunction(OldPosition, VariationalParameters);
    +  QuantumForce(OldPosition, OldQuantumForce, VariationalParameters);
    +  // loop over monte carlo cycles 
    +  for (int cycles = 1; cycles <= NumberMCsamples; cycles++){ 
    +    // new position 
    +    for (int i = 0; i < NumberParticles; i++) { 
    +      for (int j = 0; j < Dimension; j++) {
    +	// gaussian deviate to compute new positions using a given timestep
    +	NewPosition(i,j) = OldPosition(i,j) + Normaldistribution(gen)*sqrt(timestep)+OldQuantumForce(i,j)*timestep*D;
    +	//	NewPosition(i,j) = OldPosition(i,j) + gaussian_deviate(&idum)*sqrt(timestep)+OldQuantumForce(i,j)*timestep*D;
    +      }  
    +      //  for the other particles we need to set the position to the old position since
    +      //  we move only one particle at the time
    +      for (int k = 0; k < NumberParticles; k++) {
    +	if ( k != i) {
    +	  for (int j = 0; j < Dimension; j++) {
    +	    NewPosition(k,j) = OldPosition(k,j);
    +	  }
    +	} 
    +      }
    +      double NewWaveFunction = WaveFunction(NewPosition, VariationalParameters); 
    +      QuantumForce(NewPosition, NewQuantumForce, VariationalParameters);
    +      //  we compute the log of the ratio of the greens functions to be used in the 
    +      //  Metropolis-Hastings algorithm
    +      double GreensFunction = 0.0;            
    +      for (int j = 0; j < Dimension; j++) {
    +	GreensFunction += 0.5*(OldQuantumForce(i,j)+NewQuantumForce(i,j))*
    +	  (D*timestep*0.5*(OldQuantumForce(i,j)-NewQuantumForce(i,j))-NewPosition(i,j)+OldPosition(i,j));
    +      }
    +      GreensFunction = exp(GreensFunction);
    +      // The Metropolis test is performed by moving one particle at the time
    +      if(UniformNumberGenerator(gen) <= GreensFunction*NewWaveFunction*NewWaveFunction/OldWaveFunction/OldWaveFunction ) { 
    +	for (int  j = 0; j < Dimension; j++) {
    +	  OldPosition(i,j) = NewPosition(i,j);
    +	  OldQuantumForce(i,j) = NewQuantumForce(i,j);
    +	}
    +	OldWaveFunction = NewWaveFunction;
    +      }
    +    }  //  end of loop over particles
    +    // compute local energy  
    +    double DeltaE = LocalEnergy(OldPosition, VariationalParameters);
    +    // update energies
    +    Energy += DeltaE;
    +    EnergySquared += DeltaE*DeltaE;
    +  }   // end of loop over MC trials   
    +  // update the energy average and its squared 
    +  cumulative_e = Energy/NumberMCsamples;
    +  cumulative_e2 = EnergySquared/NumberMCsamples;
    +}   // end MonteCarloSampling function  
    +
    +
    +// Function to compute the squared wave function and the quantum force
    +
    +double  WaveFunction(Matrix &r, Vector &VariationalParameters)
    +{
    +  double wf = 0.0;
    +  // full Slater determinant for two particles, replace with Slater det for more particles 
    +  wf  = SPwavefunction(singleparticle_pos2(r, 0), VariationalParameters(0))*SPwavefunction(singleparticle_pos2(r, 1),VariationalParameters(0));
    +  // contribution from Jastrow factor
    +  for (int i = 0; i < NumberParticles-1; i++) { 
    +    for (int j = i+1; j < NumberParticles; j++) {
    +      wf *= exp(RelativeDistance(r, i, j)/((1.0+VariationalParameters(1)*RelativeDistance(r, i, j))));
    +    }
    +  }
    +  return wf;
    +}
    +
    +// Function to calculate the local energy without numerical derivation of kinetic energy
    +
    +double  LocalEnergy(Matrix &r, Vector &VariationalParameters)
    +{
    +
    +  // compute the kinetic and potential energy from the single-particle part
    +  // for a many-electron system this has to be replaced by a Slater determinant
    +  // The absolute value of the interparticle length
    +  Matrix length( NumberParticles, NumberParticles);
    +  // Set up interparticle distance
    +  for (int i = 0; i < NumberParticles-1; i++) { 
    +    for(int j = i+1; j < NumberParticles; j++){
    +      length(i,j) = RelativeDistance(r, i, j);
    +      length(j,i) =  length(i,j);
    +    }
    +  }
    +  double KineticEnergy = 0.0;
    +  // Set up kinetic energy from Slater and Jastrow terms
    +  for (int i = 0; i < NumberParticles; i++) { 
    +    for (int k = 0; k < Dimension; k++) {
    +      double sum1 = 0.0; 
    +      for(int j = 0; j < NumberParticles; j++){
    +	if ( j != i) {
    +	  sum1 += JastrowDerivative(r, VariationalParameters(1), i, j, k);
    +	}
    +      }
    +      KineticEnergy += (sum1+DerivativeSPwavefunction(r(i,k),VariationalParameters(0)))*(sum1+DerivativeSPwavefunction(r(i,k),VariationalParameters(0)));
    +    }
    +  }
    +  KineticEnergy += -2*VariationalParameters(0)*NumberParticles;
    +  for (int i = 0; i < NumberParticles-1; i++) {
    +      for (int j = i+1; j < NumberParticles; j++) {
    +        KineticEnergy += 2.0/(pow(1.0 + VariationalParameters(1)*length(i,j),2))*(1.0/length(i,j)-2*VariationalParameters(1)/(1+VariationalParameters(1)*length(i,j)) );
    +      }
    +  }
    +  KineticEnergy *= -0.5;
    +  // Set up potential energy, external potential + eventual electron-electron repulsion
    +  double PotentialEnergy = 0;
    +  for (int i = 0; i < NumberParticles; i++) { 
    +    double DistanceSquared = singleparticle_pos2(r, i);
    +    PotentialEnergy += 0.5*DistanceSquared;  // sp energy HO part, note it has the oscillator frequency set to 1!
    +  }
    +  // Add the electron-electron repulsion
    +  for (int i = 0; i < NumberParticles-1; i++) { 
    +    for (int j = i+1; j < NumberParticles; j++) {
    +      PotentialEnergy += 1.0/length(i,j);          
    +    }
    +  }
    +  double LocalE = KineticEnergy+PotentialEnergy;
    +  return LocalE;
    +}
    +
    +// Compute the analytical expression for the quantum force
    +void  QuantumForce(Matrix &r, Matrix &qforce, Vector &VariationalParameters)
    +{
    +  // compute the first derivative 
    +  for (int i = 0; i < NumberParticles; i++) {
    +    for (int k = 0; k < Dimension; k++) {
    +      // single-particle part, replace with Slater det for larger systems
    +      double sppart = DerivativeSPwavefunction(r(i,k),VariationalParameters(0));
    +      //  Jastrow factor contribution
    +      double Jsum = 0.0;
    +      for (int j = 0; j < NumberParticles; j++) {
    +	if ( j != i) {
    +	  Jsum += JastrowDerivative(r, VariationalParameters(1), i, j, k);
    +	}
    +      }
    +      qforce(i,k) = 2.0*(Jsum+sppart);
    +    }
    +  }
    +} // end of QuantumForce function
    +
    +
    +#define ITMAX 200
    +#define EPS 3.0e-8
    +#define TOLX (4*EPS)
    +#define STPMX 100.0
    +
    +void dfpmin(Vector &p, int n, double gtol, int *iter, double *fret,
    +	    double(*func)(Vector &p), void (*dfunc)(Vector &p, Vector &g))
    +{
    +
    +  int check,i,its,j;
    +  double den,fac,fad,fae,fp,stpmax,sum=0.0,sumdg,sumxi,temp,test;
    +  Vector dg(n), g(n), hdg(n), pnew(n), xi(n);
    +  Matrix hessian(n,n);
    +
    +  fp=(*func)(p);
    +  (*dfunc)(p,g);
    +  for (i = 0;i < n;i++) {
    +    for (j = 0; j< n;j++) hessian(i,j)=0.0;
    +    hessian(i,i)=1.0;
    +    xi(i) = -g(i);
    +    sum += p(i)*p(i);
    +  }
    +  stpmax=STPMX*FMAX(sqrt(sum),(double)n);
    +  for (its=1;its<=ITMAX;its++) {
    +    *iter=its;
    +    lnsrch(n,p,fp,g,xi,pnew,fret,stpmax,&check,func);
    +    fp = *fret;
    +    for (i = 0; i< n;i++) {
    +      xi(i)=pnew(i)-p(i);
    +      p(i)=pnew(i);
    +    }
    +    test=0.0;
    +    for (i = 0;i< n;i++) {
    +      temp=fabs(xi(i))/FMAX(fabs(p(i)),1.0);
    +      if (temp > test) test=temp;
    +    }
    +    if (test < TOLX) {
    +      return;
    +    }
    +    for (i=0;i<n;i++) dg(i)=g(i);
    +    (*dfunc)(p,g);
    +    test=0.0;
    +    den=FMAX(*fret,1.0);
    +    for (i=0;i<n;i++) {
    +      temp=fabs(g(i))*FMAX(fabs(p(i)),1.0)/den;
    +      if (temp > test) test=temp;
    +    }
    +    if (test < gtol) {
    +      return;
    +    }
    +    for (i=0;i<n;i++) dg(i)=g(i)-dg(i);
    +    for (i=0;i<n;i++) {
    +      hdg(i)=0.0;
    +      for (j=0;j<n;j++) hdg(i) += hessian(i,j)*dg(j);
    +    }
    +    fac=fae=sumdg=sumxi=0.0;
    +    for (i=0;i<n;i++) {
    +      fac += dg(i)*xi(i);
    +      fae += dg(i)*hdg(i);
    +      sumdg += SQR(dg(i));
    +      sumxi += SQR(xi(i));
    +    }
    +    if (fac*fac > EPS*sumdg*sumxi) {
    +      fac=1.0/fac;
    +      fad=1.0/fae;
    +      for (i=0;i<n;i++) dg(i)=fac*xi(i)-fad*hdg(i);
    +      for (i=0;i<n;i++) {
    +	for (j=0;j<n;j++) {
    +	  hessian(i,j) += fac*xi(i)*xi(j)
    +	    -fad*hdg(i)*hdg(j)+fae*dg(i)*dg(j);
    +	}
    +      }
    +    }
    +    for (i=0;i<n;i++) {
    +      xi(i)=0.0;
    +      for (j=0;j<n;j++) xi(i) -= hessian(i,j)*g(j);
    +    }
    +  }
    +  cout << "too many iterations in dfpmin" << endl;
    +}
    +#undef ITMAX
    +#undef EPS
    +#undef TOLX
    +#undef STPMX
    +
    +#define ALF 1.0e-4
    +#define TOLX 1.0e-7
    +
    +void lnsrch(int n, Vector &xold, double fold, Vector &g, Vector &p, Vector &x,
    +	    double *f, double stpmax, int *check, double (*func)(Vector &p))
    +{
    +  int i;
    +  double a,alam,alam2,alamin,b,disc,f2,fold2,rhs1,rhs2,slope,sum,temp,
    +    test,tmplam;
    +
    +  *check=0;
    +  for (sum=0.0,i=0;i<n;i++) sum += p(i)*p(i);
    +  sum=sqrt(sum);
    +  if (sum > stpmax)
    +    for (i=0;i<n;i++) p(i) *= stpmax/sum;
    +  for (slope=0.0,i=0;i<n;i++)
    +    slope += g(i)*p(i);
    +  test=0.0;
    +  for (i=0;i<n;i++) {
    +    temp=fabs(p(i))/FMAX(fabs(xold(i)),1.0);
    +    if (temp > test) test=temp;
    +  }
    +  alamin=TOLX/test;
    +  alam=1.0;
    +  for (;;) {
    +    for (i=0;i<n;i++) x(i)=xold(i)+alam*p(i);
    +    *f=(*func)(x);
    +    if (alam < alamin) {
    +      for (i=0;i<n;i++) x(i)=xold(i);
    +      *check=1;
    +      return;
    +    } else if (*f <= fold+ALF*alam*slope) return;
    +    else {
    +      if (alam == 1.0)
    +	tmplam = -slope/(2.0*(*f-fold-slope));
    +      else {
    +	rhs1 = *f-fold-alam*slope;
    +	rhs2=f2-fold2-alam2*slope;
    +	a=(rhs1/(alam*alam)-rhs2/(alam2*alam2))/(alam-alam2);
    +	b=(-alam2*rhs1/(alam*alam)+alam*rhs2/(alam2*alam2))/(alam-alam2);
    +	if (a == 0.0) tmplam = -slope/(2.0*b);
    +	else {
    +	  disc=b*b-3.0*a*slope;
    +	  if (disc<0.0) cout << "Roundoff problem in lnsrch." << endl;
    +	  else tmplam=(-b+sqrt(disc))/(3.0*a);
    +	}
    +	if (tmplam>0.5*alam)
    +	  tmplam=0.5*alam;
    +      }
    +    }
    +    alam2=alam;
    +    f2 = *f;
    +    fold2=fold;
    +    alam=FMAX(tmplam,0.1*alam);
    +  }
    +}
    +#undef ALF
    +#undef TOLX
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    What is OpenMP

    +
    + +

    +

      +

    • OpenMP provides high-level thread programming
    • +

    • Multiple cooperating threads are allowed to run simultaneously
    • +

    • Threads are created and destroyed dynamically in a fork-join pattern
    • +
        + +

      • An OpenMP program consists of a number of parallel regions
      • + +

      • Between two parallel regions there is only one master thread
      • + +

      • In the beginning of a parallel region, a team of new threads is spawned
      • +
      +

      + +

    • The newly spawned threads work simultaneously with the master thread
    • + +

    • At the end of a parallel region, the new threads are destroyed
    • +
    +

    +

    Many good tutorials online and excellent textbook

    +
      +

    1. Using OpenMP, by B. Chapman, G. Jost, and A. van der Pas
    2. +

    3. Many tutorials online like OpenMP official site
    4. +
    +
    +
    + +
    +

    Getting started, things to remember

    +
    + +

    +

      +

    • Remember the header file
    • +
    +

    + + +

    +
    +
    +
    +
    +
    #include <omp.h>
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +

    • Insert compiler directives in C++ syntax as
    • +
    +

    + + +

    +
    +
    +
    +
    +
    #pragma omp...
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +

    • Compile with for example c++ -fopenmp code.cpp
    • +

    • Execute
    • +
        + +

      • Remember to assign the environment variable OMP NUM THREADS
      • + +

      • It specifies the total number of threads inside a parallel region, if not otherwise overwritten
      • +
      +

      +

    +
    +
    + +
    +

    OpenMP syntax

    + +

    + + +

    +
    +
    +
    +
    +
    #pragma omp construct [ clause ...]
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +

    + + +

    +
    +
    +
    +
    +
    #include <omp.h>
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
    + +
    +

    Different OpenMP styles of parallelism

    +

    OpenMP supports several different ways to specify thread parallelism

    + + +
    + +
    +

    General code structure

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #include <omp.h>
    +main ()
    +{
    +int var1, var2, var3;
    +/* serial code */
    +/* ... */
    +/* start of a parallel region */
    +#pragma omp parallel private(var1, var2) shared(var3)
    +{
    +/* ... */
    +}
    +/* more serial code */
    +/* ... */
    +/* another parallel region */
    +#pragma omp parallel
    +{
    +/* ... */
    +}
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Parallel region

    +
    + +

    +

      +

    • A parallel region is a block of code that is executed by a team of threads
    • +

    • The following compiler directive creates a parallel region
    • +
    +

    + + +

    +
    +
    +
    +
    +
    #pragma omp parallel { ... }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +

    • Clauses can be added at the end of the directive
    • +

    • Most often used clauses:
    • +
        +

      • default(shared) or default(none)
      • +

      • public(list of variables)
      • +

      • private(list of variables)
      • +
      +

      +

    +
    +
    + +
    +

    Hello world, not again, please!

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #include <omp.h>
    +#include <cstdio>
    +int main (int argc, char *argv[])
    +{
    +int th_id, nthreads;
    +#pragma omp parallel private(th_id) shared(nthreads)
    +{
    +th_id = omp_get_thread_num();
    +printf("Hello World from thread %d\n", th_id);
    +#pragma omp barrier
    +if ( th_id == 0 ) {
    +nthreads = omp_get_num_threads();
    +printf("There are %d threads\n",nthreads);
    +}
    +}
    +return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Hello world, yet another variant

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #include <cstdio>
    +#include <omp.h>
    +int main(int argc, char *argv[]) 
    +{
    + omp_set_num_threads(4); 
    +#pragma omp parallel
    + {
    +   int id = omp_get_thread_num();
    +   int nproc = omp_get_num_threads(); 
    +   cout << "Hello world with id number and processes " <<  id <<  nproc << endl;
    + } 
    +return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Variables declared outside of the parallel region are shared by all threads +If a variable like id is declared outside of the +

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel, 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    it would have been shared by various the threads, possibly causing erroneous output

    +
      +

    • Why? What would go wrong? Why do we add possibly?
    • +
    +
    +
    + +
    +

    Important OpenMP library routines

    +
    + +

    + +

      +

    • int omp get num threads (), returns the number of threads inside a parallel region
    • +

    • int omp get thread num (), returns the a thread for each thread inside a parallel region
    • +

    • void omp set num threads (int), sets the number of threads to be used
    • +

    • void omp set nested (int), turns nested parallelism on/off
    • +
    +
    +
    + +
    +

    Private variables

    +
    + +

    +

    Private clause can be used to make thread- private versions of such variables:

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel private(id)
    +{
    + int id = omp_get_thread_num();
    + cout << "My thread num" << id << endl; 
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +

    • What is their value on entry? Exit?
    • +

    • OpenMP provides ways to control that
    • +

    • Can use default(none) to require the sharing of each variable to be described
    • +
    +
    +
    + +
    +

    Master region

    +
    + +

    +

    It is often useful to have only one thread execute some of the code in a parallel region. I/O statements are a common example

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel 
    +{
    +  #pragma omp master
    +   {
    +      int id = omp_get_thread_num();
    +      cout << "My thread num" << id << endl; 
    +   } 
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Parallel for loop

    +
    + +

    +

      +

    • Inside a parallel region, the following compiler directive can be used to parallelize a for-loop:
    • +
    +

    + + +

    +
    +
    +
    +
    +
    #pragma omp for
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +

    • Clauses can be added, such as
    • +
        + +

      • schedule(static, chunk size)
      • + +

      • schedule(dynamic, chunk size)
      • + +

      • schedule(guided, chunk size) (non-deterministic allocation)
      • + +

      • schedule(runtime)
      • + +

      • private(list of variables)
      • + +

      • reduction(operator:variable)
      • + +

      • nowait
      • +
      +

      +

    +
    +
    + +
    +

    Parallel computations and loops

    + +
    + +

    +

    OpenMP provides an easy way to parallelize a loop

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel for
    +  for (i=0; i<n; i++) c[i] = a[i];
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    OpenMP handles index variable (no need to declare in for loop or make private)

    + +

    Which thread does which values? Several options.

    +
    +
    + +
    +

    Scheduling of loop computations

    + +
    + +

    +

    We can let the OpenMP runtime decide. The decision is about how the loop iterates are scheduled +and OpenMP defines three choices of loop scheduling: +

    +
      +

    1. Static: Predefined at compile time. Lowest overhead, predictable
    2. +

    3. Dynamic: Selection made at runtime
    4. +

    5. Guided: Special case of dynamic; attempts to reduce overhead
    6. +
    +
    +
    + +
    +

    Example code for loop scheduling

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #include <omp.h>
    +#define CHUNKSIZE 100
    +#define N 1000
    +int main (int argc, char *argv[])
    +{
    +int i, chunk;
    +float a[N], b[N], c[N];
    +for (i=0; i < N; i++) a[i] = b[i] = i * 1.0;
    +chunk = CHUNKSIZE;
    +#pragma omp parallel shared(a,b,c,chunk) private(i)
    +{
    +#pragma omp for schedule(dynamic,chunk)
    +for (i=0; i < N; i++) c[i] = a[i] + b[i];
    +} /* end of parallel region */
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Example code for loop scheduling, guided instead of dynamic

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #include <omp.h>
    +#define CHUNKSIZE 100
    +#define N 1000
    +int main (int argc, char *argv[])
    +{
    +int i, chunk;
    +float a[N], b[N], c[N];
    +for (i=0; i < N; i++) a[i] = b[i] = i * 1.0;
    +chunk = CHUNKSIZE;
    +#pragma omp parallel shared(a,b,c,chunk) private(i)
    +{
    +#pragma omp for schedule(guided,chunk)
    +for (i=0; i < N; i++) c[i] = a[i] + b[i];
    +} /* end of parallel region */
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    More on Parallel for loop

    +
    + +

    +

      +

    • The number of loop iterations cannot be non-deterministic; break, return, exit, goto not allowed inside the for-loop
    • +

    • The loop index is private to each thread
    • +

    • A reduction variable is special
    • +
        + +

      • During the for-loop there is a local private copy in each thread
      • + +

      • At the end of the for-loop, all the local copies are combined together by the reduction operation
      • +
      +

      +

    • Unless the nowait clause is used, an implicit barrier synchronization will be added at the end by the compiler
    • +
    +

    + + +

    +
    +
    +
    +
    +
    // #pragma omp parallel and #pragma omp for
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    can be combined into

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel for
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    What can happen with this loop?

    + +
    + +

    +

    What happens with code like this

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel for
    +for (i=0; i<n; i++) sum += a[i]*a[i];
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    All threads can access the sum variable, but the addition is not atomic! It is important to avoid race between threads. So-called reductions in OpenMP are thus important for performance and for obtaining correct results. OpenMP lets us indicate that a variable is used for a reduction with a particular operator. The above code becomes

    + + +
    +
    +
    +
    +
    +
    sum = 0.0;
    +#pragma omp parallel for reduction(+:sum)
    +for (i=0; i<n; i++) sum += a[i]*a[i];
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Inner product

    +
    + +

    +

     
    +$$ +\sum_{i=0}^{n-1} a_ib_i +$$ +

     
    + + + +

    +
    +
    +
    +
    +
    int i;
    +double sum = 0.;
    +/* allocating and initializing arrays */
    +/* ... */
    +#pragma omp parallel for default(shared) private(i) reduction(+:sum)
    + for (i=0; i<N; i++) sum += a[i]*b[i];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Different threads do different tasks

    +
    + +

    + +

    Different threads do different tasks independently, each section is executed by one thread.

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel
    +{
    +#pragma omp sections
    +{
    +#pragma omp section
    +funcA ();
    +#pragma omp section
    +funcB ();
    +#pragma omp section
    +funcC ();
    +}
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Single execution

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #pragma omp single { ... }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The code is executed by one thread only, no guarantee which thread

    + +

    Can introduce an implicit barrier at the end

    + + +
    +
    +
    +
    +
    +
    #pragma omp master { ... }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Code executed by the master thread, guaranteed and no implicit barrier at the end.

    +
    +
    + +
    +

    Coordination and synchronization

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #pragma omp barrier
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Synchronization, must be encountered by all threads in a team (or none)

    + + +
    +
    +
    +
    +
    +
    #pragma omp ordered { a block of codes }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    is another form of synchronization (in sequential order). +The form +

    + + +
    +
    +
    +
    +
    +
    #pragma omp critical { a block of codes }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    and

    + + +
    +
    +
    +
    +
    +
    #pragma omp atomic { single assignment statement }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    is more efficient than

    + + +
    +
    +
    +
    +
    +
    #pragma omp critical
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Data scope

    +
    + +

    +

      +

    • OpenMP data scope attribute clauses:
    • +
        +

      • shared
      • +

      • private
      • +

      • firstprivate
      • +

      • lastprivate
      • +

      • reduction
      • +
      +

      +

    +

    +

    What are the purposes of these attributes

    +
      +

    • define how and which variables are transferred to a parallel region (and back)
    • +

    • define which variables are visible to all threads in a parallel region, and which variables are privately allocated to each thread
    • +
    +
    +
    + +
    +

    Some remarks

    +
    + +

    + +

      +

    • When entering a parallel region, the private clause ensures each thread having its own new variable instances. The new variables are assumed to be uninitialized.
    • +

    • A shared variable exists in only one memory location and all threads can read and write to that address. It is the programmer's responsibility to ensure that multiple threads properly access a shared variable.
    • +

    • The firstprivate clause combines the behavior of the private clause with automatic initialization.
    • +

    • The lastprivate clause combines the behavior of the private clause with a copy back (from the last loop iteration or section) to the original variable outside the parallel region.
    • +
    +
    +
    + +
    +

    Parallelizing nested for-loops

    +
    + +

    + +

      +

    • Serial code
    • +
    +

    + + +

    +
    +
    +
    +
    +
    for (i=0; i<100; i++)
    +    for (j=0; j<100; j++)
    +        a[i][j] = b[i][j] + c[i][j];
    +    }
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
      +

    • Parallelization
    • +
    +

    + + +

    +
    +
    +
    +
    +
    #pragma omp parallel for private(j)
    +for (i=0; i<100; i++)
    +    for (j=0; j<100; j++)
    +       a[i][j] = b[i][j] + c[i][j];
    +    }
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
      +

    • Why not parallelize the inner loop? to save overhead of repeated thread forks-joins
    • +

    • Why must j be private? To avoid race condition among the threads
    • +
    +
    +
    + +
    +

    Nested parallelism

    +
    + +

    +

    When a thread in a parallel region encounters another parallel construct, it +may create a new team of threads and become the master of the new +team. +

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel num_threads(4)
    +{
    +/* .... */
    +#pragma omp parallel num_threads(2)
    +{
    +//  
    +}
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Parallel tasks

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #pragma omp task 
    +#pragma omp parallel shared(p_vec) private(i)
    +{
    +#pragma omp single
    +{
    +for (i=0; i<N; i++) {
    +  double r = random_number();
    +  if (p_vec[i] > r) {
    +#pragma omp task
    +   do_work (p_vec[i]);
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Common mistakes

    +
    + +

    +

    Race condition

    + + +
    +
    +
    +
    +
    +
    int nthreads;
    +#pragma omp parallel shared(nthreads)
    +{
    +nthreads = omp_get_num_threads();
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Deadlock

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel
    +{
    +...
    +#pragma omp critical
    +{
    +...
    +#pragma omp barrier
    +}
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Not all computations are simple

    +
    + +

    +

    Not all computations are simple loops where the data can be evenly +divided among threads without any dependencies between threads +

    + +

    An example is finding the location and value of the largest element in an array

    + + +
    +
    +
    +
    +
    +
    for (i=0; i<n; i++) { 
    +   if (x[i] > maxval) {
    +      maxval = x[i];
    +      maxloc = i; 
    +   }
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Not all computations are simple, competing threads

    +
    + +

    +

    All threads are potentially accessing and changing the same values, maxloc and maxval.

    +
      +

    1. OpenMP provides several ways to coordinate access to shared values
    2. +
    +

    + + +

    +
    +
    +
    +
    +
    #pragma omp atomic
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +

    1. Only one thread at a time can execute the following statement (not block). We can use the critical option
    2. +
    +

    + + +

    +
    +
    +
    +
    +
    #pragma omp critical
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +

    1. Only one thread at a time can execute the following block
    2. +
    +

    +

    Atomic may be faster than critical but depends on hardware

    +
    +
    + +
    +

    How to find the max value using OpenMP

    +
    + +

    +

    Write down the simplest algorithm and look carefully for race conditions. How would you handle them? +The first step would be to parallelize as +

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel for
    + for (i=0; i<n; i++) {
    +    if (x[i] > maxval) {
    +      maxval = x[i];
    +      maxloc = i; 
    +    }
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Then deal with the race conditions

    +
    + +

    +

    Write down the simplest algorithm and look carefully for race conditions. How would you handle them? +The first step would be to parallelize as +

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel for
    + for (i=0; i<n; i++) {
    +#pragma omp critical
    +  {
    +     if (x[i] > maxval) {
    +       maxval = x[i];
    +       maxloc = i; 
    +     }
    +  }
    +} 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Exercise: write a code which implements this and give an estimate on performance. Perform several runs, +with a serial code only with and without vectorization and compare the serial code with the one that uses OpenMP. Run on different archictectures if you can. +

    +
    +
    + +
    +

    What can slow down OpenMP performance?

    +

    Give it a thought!

    +
    + +
    +

    What can slow down OpenMP performance?

    +
    + +

    +

    Performance poor because we insisted on keeping track of the maxval and location during the execution of the loop.

    +
      +

    • We do not care about the value during the execution of the loop, just the value at the end.
    • +
    +

    +

    This is a common source of performance issues, namely the description of the method used to compute a value imposes additional, unnecessary requirements or properties

    + +Idea: Have each thread find the maxloc in its own data, then combine and use temporary arrays indexed by thread number to hold the values found by each thread +
    +
    + +
    +

    Find the max location for each thread

    +
    + +

    + + +

    +
    +
    +
    +
    +
    int maxloc[MAX_THREADS], mloc;
    +double maxval[MAX_THREADS], mval; 
    +#pragma omp parallel shared(maxval,maxloc)
    +{
    +  int id = omp_get_thread_num(); 
    +  maxval[id] = -1.0e30;
    +#pragma omp for
    +   for (int i=0; i<n; i++) {
    +       if (x[i] > maxval[id]) { 
    +           maxloc[id] = i;
    +           maxval[id] = x[i]; 
    +       }
    +    }
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Combine the values from each thread

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #pragma omp flush (maxloc,maxval)
    +#pragma omp master
    +  {
    +    int nt = omp_get_num_threads(); 
    +    mloc = maxloc[0]; 
    +    mval = maxval[0]; 
    +    for (int i=1; i<nt; i++) {
    +        if (maxval[i] > mval) { 
    +           mval = maxval[i]; 
    +           mloc = maxloc[i];
    +        } 
    +     }
    +   }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Note that we let the master process perform the last operation.

    +
    +
    + +
    +

    Matrix-matrix multiplication

    +

    This code computes the norm of a vector using OpenMp

    + + +
    +
    +
    +
    +
    +
    //  OpenMP program to compute vector norm by adding two other vectors
    +#include <cstdlib>
    +#include <iostream>
    +#include <cmath>
    +#include <iomanip>
    +#include  <omp.h>
    +# include <ctime>
    +
    +using namespace std; // note use of namespace
    +int main (int argc, char* argv[])
    +{
    +  // read in dimension of vector
    +  int n = atoi(argv[1]);
    +  double *a, *b, *c;
    +  int i;
    +  int thread_num;
    +  double wtime, Norm2, s, angle;
    +  cout << "  Perform addition of two vectors and compute the norm-2." << endl;
    +  omp_set_num_threads(4);
    +  thread_num = omp_get_max_threads ();
    +  cout << "  The number of processors available = " << omp_get_num_procs () << endl ;
    +  cout << "  The number of threads available    = " << thread_num <<  endl;
    +  cout << "  The matrix order n                 = " << n << endl;
    +
    +  s = 1.0/sqrt( (double) n);
    +  wtime = omp_get_wtime ( );
    +  // Allocate space for the vectors to be used
    +  a = new double [n]; b = new double [n]; c = new double [n];
    +  // Define parallel region
    +# pragma omp parallel for default(shared) private (angle, i) reduction(+:Norm2)
    +  // Set up values for vectors  a and b
    +  for (i = 0; i < n; i++){
    +      angle = 2.0*M_PI*i/ (( double ) n);
    +      a[i] = s*(sin(angle) + cos(angle));
    +      b[i] =  s*sin(2.0*angle);
    +      c[i] = 0.0;
    +  }
    +  // Then perform the vector addition
    +  for (i = 0; i < n; i++){
    +     c[i] += a[i]+b[i];
    +  }
    +  // Compute now the norm-2
    +  Norm2 = 0.0;
    +  for (i = 0; i < n; i++){
    +     Norm2  += c[i]*c[i];
    +  }
    +// end parallel region
    +  wtime = omp_get_wtime ( ) - wtime;
    +  cout << setiosflags(ios::showpoint | ios::uppercase);
    +  cout << setprecision(10) << setw(20) << "Time used  for norm-2 computation=" << wtime  << endl;
    +  cout << " Norm-2  = " << Norm2 << endl;
    +  // Free up space
    +  delete[] a;
    +  delete[] b;
    +  delete[] c;
    +  return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    Matrix-matrix multiplication

    +

    This the matrix-matrix multiplication code with plain c++ memory allocation using OpenMP

    + + + +
    +
    +
    +
    +
    +
    //  Matrix-matrix multiplication and Frobenius norm of a matrix with OpenMP
    +#include <cstdlib>
    +#include <iostream>
    +#include <cmath>
    +#include <iomanip>
    +#include  <omp.h>
    +# include <ctime>
    +
    +using namespace std; // note use of namespace
    +int main (int argc, char* argv[])
    +{
    +  // read in dimension of square matrix
    +  int n = atoi(argv[1]);
    +  double **A, **B, **C;
    +  int i, j, k;
    +  int thread_num;
    +  double wtime, Fsum, s, angle;
    +  cout << "  Compute matrix product C = A * B and Frobenius norm." << endl;
    +  omp_set_num_threads(4);
    +  thread_num = omp_get_max_threads ();
    +  cout << "  The number of processors available = " << omp_get_num_procs () << endl ;
    +  cout << "  The number of threads available    = " << thread_num <<  endl;
    +  cout << "  The matrix order n                 = " << n << endl;
    +
    +  s = 1.0/sqrt( (double) n);
    +  wtime = omp_get_wtime ( );
    +  // Allocate space for the two matrices
    +  A = new double*[n]; B = new double*[n]; C = new double*[n];
    +  for (i = 0; i < n; i++){
    +    A[i] = new double[n];
    +    B[i] = new double[n];
    +    C[i] = new double[n];
    +  }
    +  // Define parallel region
    +# pragma omp parallel for default(shared) private (angle, i, j, k) reduction(+:Fsum)
    +  // Set up values for matrix A and B and zero matrix C
    +  for (i = 0; i < n; i++){
    +    for (j = 0; j < n; j++) {
    +      angle = 2.0*M_PI*i*j/ (( double ) n);
    +      A[i][j] = s * ( sin ( angle ) + cos ( angle ) );
    +      B[j][i] =  A[i][j];
    +    }
    +  }
    +  // Then perform the matrix-matrix multiplication
    +  for (i = 0; i < n; i++){
    +    for (j = 0; j < n; j++) {
    +       C[i][j] =  0.0;    
    +       for (k = 0; k < n; k++) {
    +            C[i][j] += A[i][k]*B[k][j];
    +       }
    +    }
    +  }
    +  // Compute now the Frobenius norm
    +  Fsum = 0.0;
    +  for (i = 0; i < n; i++){
    +    for (j = 0; j < n; j++) {
    +      Fsum += C[i][j]*C[i][j];
    +    }
    +  }
    +  Fsum = sqrt(Fsum);
    +// end parallel region and letting only one thread perform I/O
    +  wtime = omp_get_wtime ( ) - wtime;
    +  cout << setiosflags(ios::showpoint | ios::uppercase);
    +  cout << setprecision(10) << setw(20) << "Time used  for matrix-matrix multiplication=" << wtime  << endl;
    +  cout << "  Frobenius norm  = " << Fsum << endl;
    +  // Free up space
    +  for (int i = 0; i < n; i++){
    +    delete[] A[i];
    +    delete[] B[i];
    +    delete[] C[i];
    +  }
    +  delete[] A;
    +  delete[] B;
    +  delete[] C;
    +  return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + diff --git a/doc/pub/week9/html/week9-solarized.html b/doc/pub/week9/html/week9-solarized.html index 8becdd7a..38716503 100644 --- a/doc/pub/week9/html/week9-solarized.html +++ b/doc/pub/week9/html/week9-solarized.html @@ -106,11 +106,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -168,15 +545,7 @@

    Overview of week 11, March 11-15

    -
    -Teaching Material, videos and written material -

    -

    -
    - +

    Note, these notes contain additional material om optimization and parallelization. Parts of this material will be discussed this week.











    Why resampling methods ?

    @@ -362,9 +731,9 @@

    Introducing the correlation functi

    Resampling methods: Blocking

    The blocking method was made popular by Flyvbjerg and Pedersen (1989) -and has become one of the standard ways to estimate -\( V(\widehat{\theta}) \) for exactly one \( \widehat{\theta} \), namely -\( \widehat{\theta} = \overline{X} \). +and has become one of the standard ways to estimate the variance +\( \mathrm{var}(\widehat{\theta}) \) for exactly one estimator \( \widehat{\theta} \), namely +\( \widehat{\theta} = \overline{X} \), the mean value.

    Assume \( n = 2^d \) for some integer \( d>1 \) and \( X_1,X_2,\cdots, X_n \) is a stationary time series to begin with. @@ -487,10 +856,14 @@

    Blocking Transformations, fi \end{align} $$ + +









    +

    More on the blocking method

    +

    Flyvbjerg and Petersen demonstrated that the sequence \( \{e_k\}_{k=0}^{d-1} \) is decreasing, and conjecture that the term \( e_k \) can be made as small as we would like by making \( k \) (and hence -\( d \)) sufficiently large. The sequence is decreasing (Master of Science thesis by Marius Jonsson, UiO 2018). +\( d \)) sufficiently large. The sequence is decreasing. It means we can apply blocking transformations until \( e_k \) is sufficiently small, and then estimate \( \mathrm{var}(\overline{X}) \) by \( \widehat{\sigma}^2_k/n_k \). @@ -825,6 +1198,5073 @@

    Resampling analysis

    +









    +

    Content

    + +









    +

    Optimization and profiling

    +
    + +

    + +

    Till now we have not paid much attention to speed and possible optimization possibilities +inherent in the various compilers. We have compiled and linked as +

    + + +
    +
    +
    +
    +
    +
    c++  -c  mycode.cpp
    +c++  -o  mycode.exe  mycode.o
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    For Fortran replace with for example gfortran or ifort. +This is what we call a flat compiler option and should be used when we develop the code. +It produces normally a very large and slow code when translated to machine instructions. +We use this option for debugging and for establishing the correct program output because +every operation is done precisely as the user specified it. +

    + +

    It is instructive to look up the compiler manual for further instructions by writing

    + + +
    +
    +
    +
    +
    +
    man c++
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +









    +

    More on optimization

    +
    + +

    +

    We have additional compiler options for optimization. These may include procedure inlining where +performance may be improved, moving constants inside loops outside the loop, +identify potential parallelism, include automatic vectorization or replace a division with a reciprocal +and a multiplication if this speeds up the code. +

    + + +
    +
    +
    +
    +
    +
    c++  -O3 -c  mycode.cpp
    +c++  -O3 -o  mycode.exe  mycode.o
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    This (other options are -O2 or -Ofast) is the recommended option.

    +
    + +









    +

    Optimization and profiling

    +
    + +

    +

    It is also useful to profile your program under the development stage. +You would then compile with +

    + + +
    +
    +
    +
    +
    +
    c++  -pg -O3 -c  mycode.cpp
    +c++  -pg -O3 -o  mycode.exe  mycode.o
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    After you have run the code you can obtain the profiling information via

    + + +
    +
    +
    +
    +
    +
    gprof mycode.exe >  ProfileOutput
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    When you have profiled properly your code, you must take out this option as it +slows down performance. +For memory tests use valgrind. An excellent environment for all these aspects, and much more, is Qt creator. +

    +
    + + +









    +

    Optimization and debugging

    +
    + +

    +

    Adding debugging options is a very useful alternative under the development stage of a program. +You would then compile with +

    + + +
    +
    +
    +
    +
    +
    c++  -g -O0 -c  mycode.cpp
    +c++  -g -O0 -o  mycode.exe  mycode.o
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    This option generates debugging information allowing you to trace for example if an array is properly allocated. Some compilers work best with the no optimization option -O0.

    +
    + +
    +Other optimization flags +

    +

    Depending on the compiler, one can add flags which generate code that catches integer overflow errors. +The flag -ftrapv does this for the CLANG compiler on OS X operating systems. +

    +
    + + +









    +

    Other hints

    +
    + +

    +

    In general, irrespective of compiler options, it is useful to

    + +

    Here is an example of a part of a program where specific operations lead to a slower code

    + + +
    +
    +
    +
    +
    +
    k = n-1;
    +for (i = 0; i < n; i++){
    +    a[i] = b[i] +c*d;
    +    e = g[k];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    A better code is

    + + +
    +
    +
    +
    +
    +
    temp = c*d;
    +for (i = 0; i < n; i++){
    +    a[i] = b[i] + temp;
    +}
    +e = g[n-1];
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Here we avoid a repeated multiplication inside a loop. +Most compilers, depending on compiler flags, identify and optimize such bottlenecks on their own, without requiring any particular action by the programmer. However, it is always useful to single out and avoid code examples like the first one discussed here. +

    +
    + + +









    +

    Vectorization and the basic idea behind parallel computing

    +
    + +

    +

    Present CPUs are highly parallel processors with varying levels of parallelism. The typical situation can be described via the following three statements.

    + +

    Before we proceed with a more detailed discussion of topics like vectorization and parallelization, we need to remind ourselves about some basic features of different hardware models.

    +
    + + +









    +

    A rough classification of hardware models

    +
    + +

    + +

    +
    + +









    +

    Shared memory and distributed memory

    +
    + +

    +

    One way of categorizing modern parallel computers is to look at the memory configuration.

    + +

    The CPUs are connected by some network and may exchange messages.

    +
    + + +









    +

    Different parallel programming paradigms

    +
    + +

    + +

    +
    + +









    +

    Different parallel programming paradigms

    +
    + +

    + +

    +
    + + + +

    What is vectorization?

    +

    Vectorization is a special +case of Single Instructions Multiple Data (SIMD) to denote a single +instruction stream capable of operating on multiple data elements in +parallel. +We can think of vectorization as the unrolling of loops accompanied with SIMD instructions. +

    + +

    Vectorization is the process of converting an algorithm that performs scalar operations +(typically one operation at the time) to vector operations where a single operation can refer to many simultaneous operations. +Consider the following example +

    + + +
    +
    +
    +
    +
    +
    for (i = 0; i < n; i++){
    +    a[i] = b[i] + c[i];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    If the code is not vectorized, the compiler will simply start with the first element and +then perform subsequent additions operating on one address in memory at the time. +

    + + +

    Number of elements that can acted upon

    +

    A SIMD instruction can operate on multiple data elements in one single instruction. +It uses the so-called 128-bit SIMD floating-point register. +In this sense, vectorization adds some form of parallelism since one instruction is applied +to many parts of say a vector. +

    + +

    The number of elements which can be operated on in parallel +range from four single-precision floating point data elements in so-called +Streaming SIMD Extensions and two double-precision floating-point data +elements in Streaming SIMD Extensions 2 to sixteen byte operations in +a 128-bit register in Streaming SIMD Extensions 2. Thus, vector-length +ranges from 2 to 16, depending on the instruction extensions used and +on the data type. +

    + +

    IN summary, our instructions operate on 128 bit (16 byte) operands

    + + +

    Number of elements that can acted upon, examples

    +

    We start with the simple scalar operations given by

    + + +
    +
    +
    +
    +
    +
    for (i = 0; i < n; i++){
    +    a[i] = b[i] + c[i];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    If the code is not vectorized and we have a 128-bit register to store a 32 bits floating point number, +it means that we have \( 3\times 32 \) bits that are not used. +

    + +

    We have thus unused space in our SIMD registers. These registers could hold three additional integers.

    + + +

    Operation counts for scalar operation

    +

    The code

    + + +
    +
    +
    +
    +
    +
    for (i = 0; i < n; i++){
    +    a[i] = b[i] + c[i];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    has for \( n \) repeats

    +
      +
    1. one load for \( c[i] \) in address 1
    2. +
    3. one load for \( b[i] \) in address 2
    4. +
    5. add \( c[i] \) and \( b[i] \) to give \( a[i] \)
    6. +
    7. store \( a[i] \) in address 2
    8. +
    + +

    Number of elements that can acted upon, examples

    +

    If we vectorize the code, we can perform, with a 128-bit register four simultaneous operations, that is +we have +

    + + +
    +
    +
    +
    +
    +
    for (i = 0; i < n; i+=4){
    +    a[i] = b[i] + c[i];
    +    a[i+1] = b[i+1] + c[i+1];
    +    a[i+2] = b[i+2] + c[i+2];
    +    a[i+3] = b[i+3] + c[i+3];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Four additions are now done in a single step.

    + + +

    Number of operations when vectorized

    +

    For \( n/4 \) repeats assuming floats or integers

    +
      +
    1. one vector load for \( c[i] \) in address 1
    2. +
    3. one load for \( b[i] \) in address 2
    4. +
    5. add \( c[i] \) and \( b[i] \) to give \( a[i] \)
    6. +
    7. store \( a[i] \) in address 2
    8. +
    +









    +

    A simple test case with and without vectorization

    +

    We implement these operations in a simple c++ program that computes at the end the norm of a vector.

    + + + +
    +
    +
    +
    +
    +
    #include <cstdlib>
    +#include <iostream>
    +#include <cmath>
    +#include <iomanip>
    +#include "time.h"
    +
    +using namespace std; // note use of namespace
    +int main (int argc, char* argv[])
    +{
    +  // read in dimension of square matrix
    +  int n = atoi(argv[1]);
    +  double s = 1.0/sqrt( (double) n);
    +  double *a, *b, *c;
    +  // Start timing
    +  clock_t start, finish;
    +  start = clock();
    +// Allocate space for the vectors to be used
    +    a = new double [n]; b = new double [n]; c = new double [n];
    +  // Define parallel region
    +  // Set up values for vectors  a and b
    +  for (int i = 0; i < n; i++){
    +    double angle = 2.0*M_PI*i/ (( double ) n);
    +    a[i] = s*(sin(angle) + cos(angle));
    +    b[i] =  s*sin(2.0*angle);
    +    c[i] = 0.0;
    +  }
    +  // Then perform the vector addition
    +  for (int i = 0; i < n; i++){
    +    c[i] += a[i]+b[i];
    +  }
    +  // Compute now the norm-2
    +  double Norm2 = 0.0;
    +  for (int i = 0; i < n; i++){
    +    Norm2  += c[i]*c[i];
    +  }
    +  finish = clock();
    +  double timeused = (double) (finish - start)/(CLOCKS_PER_SEC );
    +  cout << setiosflags(ios::showpoint | ios::uppercase);
    +  cout << setprecision(10) << setw(20) << "Time used  for norm computation=" << timeused  << endl;
    +  cout << "  Norm-2  = " << Norm2 << endl;
    +  // Free up space
    +  delete[] a;
    +  delete[] b;
    +  delete[] c;
    +  return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + +

    Compiling with and without vectorization

    +

    We can compile and link without vectorization using the clang c++ compiler

    + + +
    +
    +
    +
    +
    +
    clang -o novec.x vecexample.cpp
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    and with vectorization (and additional optimizations)

    + + +
    +
    +
    +
    +
    +
    clang++ -O3 -Rpass=loop-vectorize -o  vec.x vecexample.cpp 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The speedup depends on the size of the vectors. In the example here we have run with \( 10^7 \) elements. +The example here was run on an IMac17.1 with OSX El Capitan (10.11.4) as operating system and an Intel i5 3.3 GHz CPU. +

    + + +
    +
    +
    +
    +
    +
    Compphys:~ hjensen$ ./vec.x 10000000
    +Time used  for norm computation=0.04720500000
    +Compphys:~ hjensen$ ./novec.x 10000000
    +Time used  for norm computation=0.03311700000
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    This particular C++ compiler speeds up the above loop operations with a factor of 1.5 +Performing the same operations for \( 10^9 \) elements results in a smaller speedup since reading from main memory is required. The non-vectorized code is seemingly faster. +

    + + +
    +
    +
    +
    +
    +
    Compphys:~ hjensen$ ./vec.x 1000000000
    +Time used  for norm computation=58.41391100
    +Compphys:~ hjensen$ ./novec.x 1000000000
    +Time used  for norm computation=46.51295300
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    We will discuss these issues further in the next slides.

    + + +

    Compiling with and without vectorization using clang

    +

    We can compile and link without vectorization with clang compiler

    + + +
    +
    +
    +
    +
    +
    clang++ -o -fno-vectorize novec.x vecexample.cpp
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    and with vectorization

    + + +
    +
    +
    +
    +
    +
    clang++ -O3 -Rpass=loop-vectorize -o  vec.x vecexample.cpp 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    We can also add vectorization analysis, see for example

    + + +
    +
    +
    +
    +
    +
    clang++ -O3 -Rpass-analysis=loop-vectorize -o  vec.x vecexample.cpp 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    or figure out if vectorization was missed

    + + +
    +
    +
    +
    +
    +
    clang++ -O3 -Rpass-missed=loop-vectorize -o  vec.x vecexample.cpp 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Automatic vectorization and vectorization inhibitors, criteria

    + +

    Not all loops can be vectorized, as discussed in Intel's guide to vectorization

    + +

    An important criteria is that the loop counter \( n \) is known at the entry of the loop.

    + + +
    +
    +
    +
    +
    +
      for (int j = 0; j < n; j++) {
    +    a[j] = cos(j*1.0);
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The variable \( n \) does need to be known at compile time. However, this variable must stay the same for the entire duration of the loop. It implies that an exit statement inside the loop cannot be data dependent.

    + +









    +

    Automatic vectorization and vectorization inhibitors, exit criteria

    + +

    An exit statement should in general be avoided. +If the exit statement contains data-dependent conditions, the loop cannot be vectorized. +The following is an example of a non-vectorizable loop +

    + + +
    +
    +
    +
    +
    +
      for (int j = 0; j < n; j++) {
    +    a[j] = cos(j*1.0);
    +    if (a[j] < 0 ) break;
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Avoid loop termination conditions and opt for a single entry loop variable \( n \). The lower and upper bounds have to be kept fixed within the loop.

    + +









    +

    Automatic vectorization and vectorization inhibitors, straight-line code

    + +

    SIMD instructions perform the same type of operations multiple times. +A switch statement leads thus to a non-vectorizable loop since different statemens cannot branch. +The following code can however be vectorized since the if statement is implemented as a masked assignment. +

    + + +
    +
    +
    +
    +
    +
      for (int j = 0; j < n; j++) {
    +    double x  = cos(j*1.0);
    +    if (x > 0 ) {
    +       a[j] =  x*sin(j*2.0); 
    +    }
    +    else {
    +       a[j] = 0.0;
    +    }
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    These operations can be performed for all data elements but only those elements which the mask evaluates as true are stored. In general, one should avoid branches such as switch, go to, or return statements or if constructs that cannot be treated as masked assignments.

    + +









    +

    Automatic vectorization and vectorization inhibitors, nested loops

    + +

    Only the innermost loop of the following example is vectorized

    + + +
    +
    +
    +
    +
    +
      for (int i = 0; i < n; i++) {
    +      for (int j = 0; j < n; j++) {
    +           a[i][j] += b[i][j];
    +      }  
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The exception is if an original outer loop is transformed into an inner loop as the result of compiler optimizations.

    + +









    +

    Automatic vectorization and vectorization inhibitors, function calls

    + +

    Calls to programmer defined functions ruin vectorization. However, calls to intrinsic functions like +\( \sin{x} \), \( \cos{x} \), \( \exp{x} \) etc are allowed since they are normally efficiently vectorized. +The following example is fully vectorizable +

    + + +
    +
    +
    +
    +
    +
      for (int i = 0; i < n; i++) {
    +      a[i] = log10(i)*cos(i);
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Similarly, inline functions defined by the programmer, allow for vectorization since the function statements are glued into the actual place where the function is called.

    + +









    +

    Automatic vectorization and vectorization inhibitors, data dependencies

    + +

    One has to keep in mind that vectorization changes the order of operations inside a loop. A so-called +read-after-write statement with an explicit flow dependency cannot be vectorized. The following code +

    + + +
    +
    +
    +
    +
    +
      double b = 15.;
    +  for (int i = 1; i < n; i++) {
    +      a[i] = a[i-1] + b;
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    is an example of flow dependency and results in wrong numerical results if vectorized. For a scalar operation, the value \( a[i-1] \) computed during the iteration is loaded into the right-hand side and the results are fine. In vector mode however, with a vector length of four, the values \( a[0] \), \( a[1] \), \( a[2] \) and \( a[3] \) from the previous loop will be loaded into the right-hand side and produce wrong results. That is, we have

    + + +
    +
    +
    +
    +
    +
       a[1] = a[0] + b;
    +   a[2] = a[1] + b;
    +   a[3] = a[2] + b;
    +   a[4] = a[3] + b;
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    and if the two first iterations are executed at the same by the SIMD instruction, the value of say \( a[1] \) could be used by the second iteration before it has been calculated by the first iteration, leading thereby to wrong results.

    + +









    +

    Automatic vectorization and vectorization inhibitors, more data dependencies

    + +

    On the other hand, a so-called +write-after-read statement can be vectorized. The following code +

    + + +
    +
    +
    +
    +
    +
      double b = 15.;
    +  for (int i = 1; i < n; i++) {
    +      a[i-1] = a[i] + b;
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    is an example of flow dependency that can be vectorized since no iteration with a higher value of \( i \) +can complete before an iteration with a lower value of \( i \). However, such code leads to problems with parallelization. +

    + +









    +

    Automatic vectorization and vectorization inhibitors, memory stride

    + +

    For C++ programmers it is also worth keeping in mind that an array notation is preferred to the more compact use of pointers to access array elements. The compiler can often not tell if it is safe to vectorize the code.

    + +

    When dealing with arrays, you should also avoid memory stride, since this slows down considerably vectorization. When you access array element, write for example the inner loop to vectorize using unit stride, that is, access successively the next array element in memory, as shown here

    + + +
    +
    +
    +
    +
    +
      for (int i = 0; i < n; i++) {
    +      for (int j = 0; j < n; j++) {
    +           a[i][j] += b[i][j];
    +      }  
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Memory management

    +

    The main memory contains the program data

    +
      +
    1. Cache memory contains a copy of the main memory data
    2. +
    3. Cache is faster but consumes more space and power. It is normally assumed to be much faster than main memory
    4. +
    5. Registers contain working data only
    6. + +
    7. Multiple Cache memories contain a copy of the main memory data
    8. + +
    +

    Loads and stores to memory can be as important as floating point operations when we measure performance.

    + +









    +

    Memory and communication

    + +
      +
    1. Most communication in a computer is carried out in chunks, blocks of bytes of data that move together
    2. +
    3. In the memory hierarchy, data moves between memory and cache, and between different levels of cache, in groups called lines
    4. + +
    +

    Many of these performance features are not captured in most programming languages.

    + +









    +

    Measuring performance

    + +

    How do we measure performance? What is wrong with this code to time a loop?

    + + +
    +
    +
    +
    +
    +
      clock_t start, finish;
    +  start = clock();
    +  for (int j = 0; j < i; j++) {
    +    a[j] = b[j]+b[j]*c[j];
    +  }
    +  finish = clock();
    +  double timeused = (double) (finish - start)/(CLOCKS_PER_SEC );
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Problems with measuring time

    +
      +
    1. Timers are not infinitely accurate
    2. +
    3. All clocks have a granularity, the minimum time that they can measure
    4. +
    5. The error in a time measurement, even if everything is perfect, may be the size of this granularity (sometimes called a clock tick)
    6. +
    7. Always know what your clock granularity is
    8. +
    9. Ensure that your measurement is for a long enough duration (say 100 times the tick)
    10. +
    +









    +

    Problems with cold start

    + +

    What happens when the code is executed? The assumption is that the code is ready to +execute. But +

    +
      +
    1. Code may still be on disk, and not even read into memory.
    2. +
    3. Data may be in slow memory rather than fast (which may be wrong or right for what you are measuring)
    4. +
    5. Multiple tests often necessary to ensure that cold start effects are not present
    6. +
    7. Special effort often required to ensure data in the intended part of the memory hierarchy.
    8. +
    +









    +

    Problems with smart compilers

    + +
      +
    1. If the result of the computation is not used, the compiler may eliminate the code
    2. +
    3. Performance will look impossibly fantastic
    4. +
    5. Even worse, eliminate some of the code so the performance looks plausible
    6. +
    7. Ensure that the results are (or may be) used.
    8. +
    +









    +

    Problems with interference

    +
      +
    1. Other activities are sharing your processor
    2. + +
    3. Make multiple tests and report
    4. +
    5. Easy choices include
    6. + +
    +









    +

    Problems with measuring performance

    +
      +
    1. Accurate, reproducible performance measurement is hard
    2. +
    3. Think carefully about your experiment:
    4. +
    5. What is it, precisely, that you want to measure?
    6. +
    7. How representative is your test to the situation that you are trying to measure?
    8. +
    +









    +

    Thomas algorithm for tridiagonal linear algebra equations

    +
    + +

    +$$ +\left( \begin{array}{ccccc} + b_0 & c_0 & & & \\ + a_0 & b_1 & c_1 & & \\ + & & \ddots & & \\ + & & a_{m-3} & b_{m-2} & c_{m-2} \\ + & & & a_{m-2} & b_{m-1} + \end{array} \right) +\left( \begin{array}{c} + x_0 \\ + x_1 \\ + \vdots \\ + x_{m-2} \\ + x_{m-1} + \end{array} \right)=\left( \begin{array}{c} + f_0 \\ + f_1 \\ + \vdots \\ + f_{m-2} \\ + f_{m-1} \\ + \end{array} \right) +$$ +

    + + +









    +

    Thomas algorithm, forward substitution

    +
    + +

    +

    The first step is to multiply the first row by \( a_0/b_0 \) and subtract it from the second row. This is known as the forward substitution step. We obtain then

    +$$ + a_i = 0, +$$ + + +$$ + b_i = b_i - \frac{a_{i-1}}{b_{i-1}}c_{i-1}, +$$ + +

    and

    +$$ + f_i = f_i - \frac{a_{i-1}}{b_{i-1}}f_{i-1}. +$$ + +

    At this point the simplified equation, with only an upper triangular matrix takes the form

    +$$ +\left( \begin{array}{ccccc} + b_0 & c_0 & & & \\ + & b_1 & c_1 & & \\ + & & \ddots & & \\ + & & & b_{m-2} & c_{m-2} \\ + & & & & b_{m-1} + \end{array} \right)\left( \begin{array}{c} + x_0 \\ + x_1 \\ + \vdots \\ + x_{m-2} \\ + x_{m-1} + \end{array} \right)=\left( \begin{array}{c} + f_0 \\ + f_1 \\ + \vdots \\ + f_{m-2} \\ + f_{m-1} \\ + \end{array} \right) +$$ +
    + + +









    +

    Thomas algorithm, backward substitution

    +
    + +

    +

    The next step is the backward substitution step. The last row is multiplied by \( c_{N-3}/b_{N-2} \) and subtracted from the second to last row, thus eliminating \( c_{N-3} \) from the last row. The general backward substitution procedure is

    +$$ + c_i = 0, +$$ + +

    and

    +$$ + f_{i-1} = f_{i-1} - \frac{c_{i-1}}{b_i}f_i +$$ + +

    All that ramains to be computed is the solution, which is the very straight forward process of

    +$$ +x_i = \frac{f_i}{b_i} +$$ +
    + + +









    +

    Thomas algorithm and counting of operations (floating point and memory)

    +
    + +

    + +

    We have in specific case the following operations with the floating operations

    + + +
    + + +
    + +

    + + +

    +
    +
    +
    +
    +
    // Forward substitution    
    +// Note that we can simplify by precalculating a[i-1]/b[i-1]
    +  for (int i=1; i < n; i++) {
    +     b[i] = b[i] - (a[i-1]*c[i-1])/b[i-1];
    +     f[i] = g[i] - (a[i-1]*f[i-1])/b[i-1];
    +  }
    +  x[n-1] = f[n-1] / b[n-1];
    +  // Backwards substitution                                                           
    +  for (int i = n-2; i >= 0; i--) {
    +     f[i] = f[i] - c[i]*f[i+1]/b[i+1];
    +     x[i] = f[i]/b[i];
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Example: Transpose of a matrix

    + + + +
    +
    +
    +
    +
    +
    #include <cstdlib>
    +#include <iostream>
    +#include <cmath>
    +#include <iomanip>
    +#include "time.h"
    +
    +using namespace std; // note use of namespace
    +int main (int argc, char* argv[])
    +{
    +  // read in dimension of square matrix
    +  int n = atoi(argv[1]);
    +  double **A, **B;
    +  // Allocate space for the two matrices
    +  A = new double*[n]; B = new double*[n];
    +  for (int i = 0; i < n; i++){
    +    A[i] = new double[n];
    +    B[i] = new double[n];
    +  }
    +  // Set up values for matrix A
    +  for (int i = 0; i < n; i++){
    +    for (int j = 0; j < n; j++) {
    +      A[i][j] =  cos(i*1.0)*sin(j*3.0);
    +    }
    +  }
    +  clock_t start, finish;
    +  start = clock();
    +  // Then compute the transpose
    +  for (int i = 0; i < n; i++){
    +    for (int j = 0; j < n; j++) {
    +      B[i][j]= A[j][i];
    +    }
    +  }
    +
    +  finish = clock();
    +  double timeused = (double) (finish - start)/(CLOCKS_PER_SEC );
    +  cout << setiosflags(ios::showpoint | ios::uppercase);
    +  cout << setprecision(10) << setw(20) << "Time used  for setting up transpose of matrix=" << timeused  << endl;
    +
    +  // Free up space
    +  for (int i = 0; i < n; i++){
    +    delete[] A[i];
    +    delete[] B[i];
    +  }
    +  delete[] A;
    +  delete[] B;
    +  return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Matrix-matrix multiplication

    +

    This the matrix-matrix multiplication code with plain c++ memory allocation. It computes at the end the Frobenius norm.

    + + + +
    +
    +
    +
    +
    +
    #include <cstdlib>
    +#include <iostream>
    +#include <cmath>
    +#include <iomanip>
    +#include "time.h"
    +
    +using namespace std; // note use of namespace
    +int main (int argc, char* argv[])
    +{
    +  // read in dimension of square matrix
    +  int n = atoi(argv[1]);
    +  double s = 1.0/sqrt( (double) n);
    +  double **A, **B, **C;
    +  // Start timing
    +  clock_t start, finish;
    +  start = clock();
    +  // Allocate space for the two matrices
    +  A = new double*[n]; B = new double*[n]; C = new double*[n];
    +  for (int i = 0; i < n; i++){
    +    A[i] = new double[n];
    +    B[i] = new double[n];
    +    C[i] = new double[n];
    +  }
    +  // Set up values for matrix A and B and zero matrix C
    +  for (int i = 0; i < n; i++){
    +    for (int j = 0; j < n; j++) {
    +      double angle = 2.0*M_PI*i*j/ (( double ) n);
    +      A[i][j] = s * ( sin ( angle ) + cos ( angle ) );
    +      B[j][i] =  A[i][j];
    +    }
    +  }
    +  // Then perform the matrix-matrix multiplication
    +  for (int i = 0; i < n; i++){
    +    for (int j = 0; j < n; j++) {
    +      double sum = 0.0;
    +       for (int k = 0; k < n; k++) {
    +           sum += B[i][k]*A[k][j];
    +       }
    +       C[i][j] = sum;
    +    }
    +  }
    +  // Compute now the Frobenius norm
    +  double Fsum = 0.0;
    +  for (int i = 0; i < n; i++){
    +    for (int j = 0; j < n; j++) {
    +      Fsum += C[i][j]*C[i][j];
    +    }
    +  }
    +  Fsum = sqrt(Fsum);
    +  finish = clock();
    +  double timeused = (double) (finish - start)/(CLOCKS_PER_SEC );
    +  cout << setiosflags(ios::showpoint | ios::uppercase);
    +  cout << setprecision(10) << setw(20) << "Time used  for matrix-matrix multiplication=" << timeused  << endl;
    +  cout << "  Frobenius norm  = " << Fsum << endl;
    +  // Free up space
    +  for (int i = 0; i < n; i++){
    +    delete[] A[i];
    +    delete[] B[i];
    +    delete[] C[i];
    +  }
    +  delete[] A;
    +  delete[] B;
    +  delete[] C;
    +  return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    How do we define speedup? Simplest form

    +
    + +

    +

    +
    + + +









    +

    How do we define speedup? Correct baseline

    +
    + +

    +

    The key is choosing the correct baseline for comparison

    + +
    + + +









    +

    Parallel speedup

    +
    + +

    +

    For parallel applications, speedup is typically defined as

    + +

    Here \( T_1 \) is the time on one processor and \( T_p \) is the time using \( p \) processors.

    + +
    + + +









    +

    Speedup and memory

    +
    + +

    +

    The speedup on \( p \) processors can +be greater than \( p \) if memory usage is optimal! +Consider the case of a memorybound computation with \( M \) words of memory +

    + +
    + + +









    +

    Upper bounds on speedup

    +
    + +

    +

    Assume that almost all parts of a code are perfectly +parallelizable (fraction \( f \)). The remainder, +fraction \( (1-f) \) cannot be parallelized at all. +

    + +

    That is, there is work that takes time \( W \) on one process; a fraction \( f \) of that work will take +time \( Wf/p \) on \( p \) processors. +

    + +
    + + +









    +

    Amdahl's law

    +
    + +

    +

    On one processor we have

    +$$ +T_1 = (1-f)W + fW = W +$$ + +

    On \( p \) processors we have

    +$$ +T_p = (1-f)W + \frac{fW}{p}, +$$ + +

    resulting in a speedup of

    +$$ +\frac{T_1}{T_p} = \frac{W}{(1-f)W+fW/p} +$$ + +

    As \( p \) goes to infinity, \( fW/p \) goes to zero, and the maximum speedup is

    +$$ +\frac{1}{1-f}, +$$ + +

    meaning that if +if \( f = 0.99 \) (all but \( 1\% \) parallelizable), the maximum speedup +is \( 1/(1-.99)=100 \)! +

    +
    + + +









    +

    How much is parallelizable

    +
    + +

    +

    If any non-parallel code slips into the +application, the parallel +performance is limited. +

    + +

    In many simulations, however, the fraction of non-parallelizable work +is \( 10^{-6} \) or less due to large arrays or objects that are perfectly parallelizable. +

    +
    + + +









    +

    Today's situation of parallel computing

    +
    + +

    + +

    +

    Our lectures will focus on both MPI and OpenMP.

    +
    + + +









    +

    Overhead present in parallel computing

    +
    + +

    + +

    +

    Due to the above overhead and that certain parts of a sequential +algorithm cannot be parallelized we may not achieve an optimal parallelization. +

    +
    + + +









    +

    Parallelizing a sequential algorithm

    +
    + +

    + +

    +
    + + +









    +

    Strategies

    +
    + +

    +

    +
    + + +









    +

    How do I run MPI on a PC/Laptop? MPI

    +
    + +

    +

    To install MPI is rather easy on hardware running unix/linux as operating systems, follow simply the instructions from the OpenMPI website. See also subsequent slides. +When you have made sure you have installed MPI on your PC/laptop, +

    + + + +
    +
    +
    +
    +
    +
      # Compile and link
    +  mpic++ -O3 -o nameofprog.x nameofprog.cpp
    +  #  run code with for example 8 processes using mpirun/mpiexec
    +  mpiexec -n 8 ./nameofprog.x
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Can I do it on my own PC/laptop? OpenMP installation

    +
    + +

    +

    If you wish to install MPI and OpenMP +on your laptop/PC, we recommend the following: +

    + + + + +
    +
    +
    +
    +
    +
      brew install libomp
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    and compile and link as

    + + +
    +
    +
    +
    +
    +
    c++ -o <name executable> <name program.cpp>  -lomp
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Installing MPI

    +
    + +

    +

    For linux/ubuntu users, you need to install two packages (alternatively use the synaptic package manager)

    + + +
    +
    +
    +
    +
    +
      sudo apt-get install libopenmpi-dev
    +  sudo apt-get install openmpi-bin
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    For OS X users, install brew (after having installed xcode and gcc, needed for the +gfortran compiler of openmpi) and then install with brew +

    + + +
    +
    +
    +
    +
    +
       brew install openmpi
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    When running an executable (code.x), run as

    + + +
    +
    +
    +
    +
    +
      mpirun -n 10 ./code.x
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    where we indicate that we want the number of processes to be 10.

    +
    + + +









    +

    Installing MPI and using Qt

    +
    + +

    +

    With openmpi installed, when using Qt, add to your .pro file the instructions here

    + +

    You may need to tell Qt where openmpi is stored.

    +
    + + +









    +

    What is Message Passing Interface (MPI)?

    +
    + +

    + +

    MPI is a library, not a language. It specifies the names, calling sequences and results of functions +or subroutines to be called from C/C++ or Fortran programs, and the classes and methods that make up the MPI C++ +library. The programs that users write in Fortran, C or C++ are compiled with ordinary compilers and linked +with the MPI library. +

    + +

    MPI programs should be able to run +on all possible machines and run all MPI implementetations without change. +

    + +

    An MPI computation is a collection of processes communicating with messages.

    +
    + +









    +

    Going Parallel with MPI

    +
    + +

    +

    Task parallelism: the work of a global problem can be divided +into a number of independent tasks, which rarely need to synchronize. +Monte Carlo simulations or numerical integration are examples of this. +

    + +

    MPI is a message-passing library where all the routines +have corresponding C/C++-binding +

    + + +
    +
    +
    +
    +
    +
       MPI_Command_name
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    and Fortran-binding (routine names are in uppercase, but can also be in lower case)

    + + +
    +
    +
    +
    +
    +
       MPI_COMMAND_NAME
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    MPI is a library

    +
    + +

    +

    MPI is a library specification for the message passing interface, +proposed as a standard. +

    + + +

    A message passing standard for portability and ease-of-use. +Designed for high performance. +

    + +

    Insert communication and synchronization functions where necessary.

    +
    + + +









    +

    Bindings to MPI routines

    +
    + +

    + +

    MPI is a message-passing library where all the routines +have corresponding C/C++-binding +

    + + +
    +
    +
    +
    +
    +
       MPI_Command_name
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    and Fortran-binding (routine names are in uppercase, but can also be in lower case)

    + + +
    +
    +
    +
    +
    +
       MPI_COMMAND_NAME
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The discussion in these slides focuses on the C++ binding.

    +
    + + +









    +

    Communicator

    +
    + +

    +

    + + +
    +
    +
    +
    +
    +
      MPI_COMM_WORLD 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
    + + +









    +

    Some of the most important MPI functions

    +
    + +

    + +

    +
    + + +









    +

    The first MPI C/C++ program

    +
    + +

    + +

    Let every process write "Hello world" (oh not this program again!!) on the standard output.

    + + +
    +
    +
    +
    +
    +
    using namespace std;
    +#include <mpi.h>
    +#include <iostream>
    +int main (int nargs, char* args[])
    +{
    +int numprocs, my_rank;
    +//   MPI initializations
    +MPI_Init (&nargs, &args);
    +MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
    +MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);
    +cout << "Hello world, I have  rank " << my_rank << " out of " 
    +     << numprocs << endl;
    +//  End MPI
    +MPI_Finalize ();
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    The Fortran program

    +
    + +

    + + +

    +
    +
    +
    +
    +
    PROGRAM hello
    +INCLUDE "mpif.h"
    +INTEGER:: size, my_rank, ierr
    +
    +CALL  MPI_INIT(ierr)
    +CALL MPI_COMM_SIZE(MPI_COMM_WORLD, size, ierr)
    +CALL MPI_COMM_RANK(MPI_COMM_WORLD, my_rank, ierr)
    +WRITE(*,*)"Hello world, I've rank ",my_rank," out of ",size
    +CALL MPI_FINALIZE(ierr)
    +
    +END PROGRAM hello
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Note 1

    +
    + +

    + +

    +
    + + +









    +

    Ordered output with MPIBarrier

    +
    + +

    + + + +

    +
    +
    +
    +
    +
    int main (int nargs, char* args[])
    +{
    + int numprocs, my_rank, i;
    + MPI_Init (&nargs, &args);
    + MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
    + MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);
    + for (i = 0; i < numprocs; i++) {}
    + MPI_Barrier (MPI_COMM_WORLD);
    + if (i == my_rank) {
    + cout << "Hello world, I have  rank " << my_rank << 
    +        " out of " << numprocs << endl;}
    +      MPI_Finalize ();
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Note 2

    +
    + +

    +

    +

    However, this is slightly more time-consuming since the processes synchronize between themselves as many times as there +are processes. In the next Hello world example we use the send and receive functions in order to a have a synchronized +action. +

    +
    + + +









    +

    Ordered output

    +
    + +

    + + + +

    +
    +
    +
    +
    +
    .....
    +int numprocs, my_rank, flag;
    +MPI_Status status;
    +MPI_Init (&nargs, &args);
    +MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
    +MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);
    +if (my_rank > 0)
    +MPI_Recv (&flag, 1, MPI_INT, my_rank-1, 100, 
    +           MPI_COMM_WORLD, &status);
    +cout << "Hello world, I have  rank " << my_rank << " out of " 
    +<< numprocs << endl;
    +if (my_rank < numprocs-1)
    +MPI_Send (&my_rank, 1, MPI_INT, my_rank+1, 
    +          100, MPI_COMM_WORLD);
    +MPI_Finalize ();
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Note 3

    +
    + +

    + +

    The basic sending of messages is given by the function \( MPI\_SEND \), which in C/C++ +is defined as +

    + + +
    +
    +
    +
    +
    +
    int MPI_Send(void *buf, int count, 
    +             MPI_Datatype datatype, 
    +             int dest, int tag, MPI_Comm comm)}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    This single command allows the passing of any kind of variable, even a large array, to any group of tasks. +The variable buf is the variable we wish to send while count +is the number of variables we are passing. If we are passing only a single value, this should be 1. +

    + +

    If we transfer an array, it is the overall size of the array. +For example, if we want to send a 10 by 10 array, count would be \( 10\times 10=100 \) +since we are actually passing 100 values. +

    +
    + + +









    +

    Note 4

    +
    + +

    + +

    Once you have sent a message, you must receive it on another task. The function \( MPI\_RECV \) +is similar to the send call. +

    + + +
    +
    +
    +
    +
    +
    int MPI_Recv( void *buf, int count, MPI_Datatype datatype, 
    +            int source, 
    +            int tag, MPI_Comm comm, MPI_Status *status )
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The arguments that are different from those in MPI\_SEND are +buf which is the name of the variable where you will be storing the received data, +source which replaces the destination in the send command. This is the return ID of the sender. +

    + +

    Finally, we have used \( MPI\_Status\_status \), +where one can check if the receive was completed. +

    + +

    The output of this code is the same as the previous example, but now +process 0 sends a message to process 1, which forwards it further +to process 2, and so forth. +

    +
    + + +









    +

    Numerical integration in parallel

    +
    +Integrating \( \pi \) +

    + +

    +$$ + I=\int_a^bf(x) dx\approx h\left(f(a)/2 + f(a+h) +f(a+2h)+\dots +f(b-h)+ f(b)/2\right). +$$ + +

    Click on this link for the full program.

    +
    + + +









    +

    Dissection of trapezoidal rule with \( MPI\_reduce \)

    +
    + +

    + + + +

    +
    +
    +
    +
    +
    //    Trapezoidal rule and numerical integration usign MPI
    +using namespace std;
    +#include <mpi.h>
    +#include <iostream>
    +
    +//     Here we define various functions called by the main program
    +
    +double int_function(double );
    +double trapezoidal_rule(double , double , int , double (*)(double));
    +
    +//   Main function begins here
    +int main (int nargs, char* args[])
    +{
    +  int n, local_n, numprocs, my_rank; 
    +  double a, b, h, local_a, local_b, total_sum, local_sum;   
    +  double  time_start, time_end, total_time;
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Dissection of trapezoidal rule

    +
    + +

    + + + +

    +
    +
    +
    +
    +
      //  MPI initializations
    +  MPI_Init (&nargs, &args);
    +  MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
    +  MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);
    +  time_start = MPI_Wtime();
    +  //  Fixed values for a, b and n 
    +  a = 0.0 ; b = 1.0;  n = 1000;
    +  h = (b-a)/n;    // h is the same for all processes 
    +  local_n = n/numprocs;  
    +  // make sure n > numprocs, else integer division gives zero
    +  // Length of each process' interval of
    +  // integration = local_n*h.  
    +  local_a = a + my_rank*local_n*h;
    +  local_b = local_a + local_n*h;
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Integrating with MPI

    +
    + +

    + + + +

    +
    +
    +
    +
    +
      total_sum = 0.0;
    +  local_sum = trapezoidal_rule(local_a, local_b, local_n, 
    +                               &int_function); 
    +  MPI_Reduce(&local_sum, &total_sum, 1, MPI_DOUBLE, 
    +              MPI_SUM, 0, MPI_COMM_WORLD);
    +  time_end = MPI_Wtime();
    +  total_time = time_end-time_start;
    +  if ( my_rank == 0) {
    +    cout << "Trapezoidal rule = " <<  total_sum << endl;
    +    cout << "Time = " <<  total_time  
    +         << " on number of processors: "  << numprocs  << endl;
    +  }
    +  // End MPI
    +  MPI_Finalize ();  
    +  return 0;
    +}  // end of main program
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    How do I use \( MPI\_reduce \)?

    +
    + +

    + +

    Here we have used

    + + +
    +
    +
    +
    +
    +
    MPI_reduce( void *senddata, void* resultdata, int count, 
    +     MPI_Datatype datatype, MPI_Op, int root, MPI_Comm comm)
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The two variables \( senddata \) and \( resultdata \) are obvious, besides the fact that one sends the address +of the variable or the first element of an array. If they are arrays they need to have the same size. +The variable \( count \) represents the total dimensionality, 1 in case of just one variable, +while \( MPI\_Datatype \) +defines the type of variable which is sent and received. +

    + +

    The new feature is \( MPI\_Op \). It defines the type +of operation we want to do. +

    +
    + + +









    +

    More on \( MPI\_Reduce \)

    +
    + +

    +

    In our case, since we are summing +the rectangle contributions from every process we define \( MPI\_Op = MPI\_SUM \). +If we have an array or matrix we can search for the largest og smallest element by sending either \( MPI\_MAX \) or +\( MPI\_MIN \). If we want the location as well (which array element) we simply transfer +\( MPI\_MAXLOC \) or \( MPI\_MINOC \). If we want the product we write \( MPI\_PROD \). +

    + +

    \( MPI\_Allreduce \) is defined as

    + + +
    +
    +
    +
    +
    +
    MPI_Allreduce( void *senddata, void* resultdata, int count, 
    +          MPI_Datatype datatype, MPI_Op, MPI_Comm comm)        
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Dissection of trapezoidal rule

    +
    + +

    + +

    We use \( MPI\_reduce \) to collect data from each process. Note also the use of the function +\( MPI\_Wtime \). +

    + + +
    +
    +
    +
    +
    +
    //  this function defines the function to integrate
    +double int_function(double x)
    +{
    +  double value = 4./(1.+x*x);
    +  return value;
    +} // end of function to evaluate
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Dissection of trapezoidal rule

    +
    + +

    + + +

    +
    +
    +
    +
    +
    //  this function defines the trapezoidal rule
    +double trapezoidal_rule(double a, double b, int n, 
    +                         double (*func)(double))
    +{
    +  double trapez_sum;
    +  double fa, fb, x, step;
    +  int    j;
    +  step=(b-a)/((double) n);
    +  fa=(*func)(a)/2. ;
    +  fb=(*func)(b)/2. ;
    +  trapez_sum=0.;
    +  for (j=1; j <= n-1; j++){
    +    x=j*step+a;
    +    trapez_sum+=(*func)(x);
    +  }
    +  trapez_sum=(trapez_sum+fb+fa)*step;
    +  return trapez_sum;
    +}  // end trapezoidal_rule 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    The quantum dot program for two electrons

    +
    + +

    + + +

    +
    +
    +
    +
    +
    // Variational Monte Carlo for atoms with importance sampling, slater det
    +// Test case for 2-electron quantum dot, no classes using Mersenne-Twister RNG
    +#include "mpi.h"
    +#include <cmath>
    +#include <random>
    +#include <string>
    +#include <iostream>
    +#include <fstream>
    +#include <iomanip>
    +#include "vectormatrixclass.h"
    +
    +using namespace  std;
    +// output file as global variable
    +ofstream ofile;  
    +// the step length and its squared inverse for the second derivative 
    +//  Here we define global variables  used in various functions
    +//  These can be changed by using classes
    +int Dimension = 2; 
    +int NumberParticles  = 2;  //  we fix also the number of electrons to be 2
    +
    +// declaration of functions 
    +
    +// The Mc sampling for the variational Monte Carlo 
    +void  MonteCarloSampling(int, double &, double &, Vector &);
    +
    +// The variational wave function
    +double  WaveFunction(Matrix &, Vector &);
    +
    +// The local energy 
    +double  LocalEnergy(Matrix &, Vector &);
    +
    +// The quantum force
    +void  QuantumForce(Matrix &, Matrix &, Vector &);
    +
    +
    +// inline function for single-particle wave function
    +inline double SPwavefunction(double r, double alpha) { 
    +   return exp(-alpha*r*0.5);
    +}
    +
    +// inline function for derivative of single-particle wave function
    +inline double DerivativeSPwavefunction(double r, double alpha) { 
    +  return -r*alpha;
    +}
    +
    +// function for absolute value of relative distance
    +double RelativeDistance(Matrix &r, int i, int j) { 
    +      double r_ij = 0;  
    +      for (int k = 0; k < Dimension; k++) { 
    +	r_ij += (r(i,k)-r(j,k))*(r(i,k)-r(j,k));
    +      }
    +      return sqrt(r_ij); 
    +}
    +
    +// inline function for derivative of Jastrow factor
    +inline double JastrowDerivative(Matrix &r, double beta, int i, int j, int k){
    +  return (r(i,k)-r(j,k))/(RelativeDistance(r, i, j)*pow(1.0+beta*RelativeDistance(r, i, j),2));
    +}
    +
    +// function for square of position of single particle
    +double singleparticle_pos2(Matrix &r, int i) { 
    +    double r_single_particle = 0;
    +    for (int j = 0; j < Dimension; j++) { 
    +      r_single_particle  += r(i,j)*r(i,j);
    +    }
    +    return r_single_particle;
    +}
    +
    +void lnsrch(int n, Vector &xold, double fold, Vector &g, Vector &p, Vector &x,
    +		 double *f, double stpmax, int *check, double (*func)(Vector &p));
    +
    +void dfpmin(Vector &p, int n, double gtol, int *iter, double *fret,
    +	    double(*func)(Vector &p), void (*dfunc)(Vector &p, Vector &g));
    +
    +static double sqrarg;
    +#define SQR(a) ((sqrarg=(a)) == 0.0 ? 0.0 : sqrarg*sqrarg)
    +
    +
    +static double maxarg1,maxarg2;
    +#define FMAX(a,b) (maxarg1=(a),maxarg2=(b),(maxarg1) > (maxarg2) ?\
    +        (maxarg1) : (maxarg2))
    +
    +
    +// Begin of main program   
    +
    +int main(int argc, char* argv[])
    +{
    +
    +  //  MPI initializations
    +  int NumberProcesses, MyRank, NumberMCsamples;
    +  MPI_Init (&argc, &argv);
    +  MPI_Comm_size (MPI_COMM_WORLD, &NumberProcesses);
    +  MPI_Comm_rank (MPI_COMM_WORLD, &MyRank);
    +  double StartTime = MPI_Wtime();
    +  if (MyRank == 0 && argc <= 1) {
    +    cout << "Bad Usage: " << argv[0] << 
    +      " Read also output file on same line and number of Monte Carlo cycles" << endl;
    +  }
    +  // Read filename and number of Monte Carlo cycles from the command line
    +  if (MyRank == 0 && argc > 2) {
    +    string filename = argv[1]; // first command line argument after name of program
    +    NumberMCsamples  = atoi(argv[2]);
    +    string fileout = filename;
    +    string argument = to_string(NumberMCsamples);
    +    // Final filename as filename+NumberMCsamples
    +    fileout.append(argument);
    +    ofile.open(fileout);
    +  }
    +  // broadcast the number of  Monte Carlo samples
    +  MPI_Bcast (&NumberMCsamples, 1, MPI_INT, 0, MPI_COMM_WORLD);
    +  // Two variational parameters only
    +  Vector VariationalParameters(2);
    +  int TotalNumberMCsamples = NumberMCsamples*NumberProcesses; 
    +  // Loop over variational parameters
    +  for (double alpha = 0.5; alpha <= 1.5; alpha +=0.1){
    +    for (double beta = 0.1; beta <= 0.5; beta +=0.05){
    +      VariationalParameters(0) = alpha;  // value of alpha
    +      VariationalParameters(1) = beta;  // value of beta
    +      //  Do the mc sampling  and accumulate data with MPI_Reduce
    +      double TotalEnergy, TotalEnergySquared, LocalProcessEnergy, LocalProcessEnergy2;
    +      LocalProcessEnergy = LocalProcessEnergy2 = 0.0;
    +      MonteCarloSampling(NumberMCsamples, LocalProcessEnergy, LocalProcessEnergy2, VariationalParameters);
    +      //  Collect data in total averages
    +      MPI_Reduce(&LocalProcessEnergy, &TotalEnergy, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
    +      MPI_Reduce(&LocalProcessEnergy2, &TotalEnergySquared, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
    +      // Print out results  in case of Master node, set to MyRank = 0
    +      if ( MyRank == 0) {
    +	double Energy = TotalEnergy/( (double)NumberProcesses);
    +	double Variance = TotalEnergySquared/( (double)NumberProcesses)-Energy*Energy;
    +	double StandardDeviation = sqrt(Variance/((double)TotalNumberMCsamples)); // over optimistic error
    +	ofile << setiosflags(ios::showpoint | ios::uppercase);
    +	ofile << setw(15) << setprecision(8) << VariationalParameters(0);
    +	ofile << setw(15) << setprecision(8) << VariationalParameters(1);
    +	ofile << setw(15) << setprecision(8) << Energy;
    +	ofile << setw(15) << setprecision(8) << Variance;
    +	ofile << setw(15) << setprecision(8) << StandardDeviation << endl;
    +      }
    +    }
    +  }
    +  double EndTime = MPI_Wtime();
    +  double TotalTime = EndTime-StartTime;
    +  if ( MyRank == 0 )  cout << "Time = " <<  TotalTime  << " on number of processors: "  << NumberProcesses  << endl;
    +  if (MyRank == 0)  ofile.close();  // close output file
    +  // End MPI
    +  MPI_Finalize ();  
    +  return 0;
    +}  //  end of main function
    +
    +
    +// Monte Carlo sampling with the Metropolis algorithm  
    +
    +void MonteCarloSampling(int NumberMCsamples, double &cumulative_e, double &cumulative_e2, Vector &VariationalParameters)
    +{
    +
    + // Initialize the seed and call the Mersienne algo
    +  std::random_device rd;
    +  std::mt19937_64 gen(rd());
    +  // Set up the uniform distribution for x \in [[0, 1]
    +  std::uniform_real_distribution<double> UniformNumberGenerator(0.0,1.0);
    +  std::normal_distribution<double> Normaldistribution(0.0,1.0);
    +  // diffusion constant from Schroedinger equation
    +  double D = 0.5; 
    +  double timestep = 0.05;  //  we fix the time step  for the gaussian deviate
    +  // allocate matrices which contain the position of the particles  
    +  Matrix OldPosition( NumberParticles, Dimension), NewPosition( NumberParticles, Dimension);
    +  Matrix OldQuantumForce(NumberParticles, Dimension), NewQuantumForce(NumberParticles, Dimension);
    +  double Energy = 0.0; double EnergySquared = 0.0; double DeltaE = 0.0;
    +  //  initial trial positions
    +  for (int i = 0; i < NumberParticles; i++) { 
    +    for (int j = 0; j < Dimension; j++) {
    +      OldPosition(i,j) = Normaldistribution(gen)*sqrt(timestep);
    +    }
    +  }
    +  double OldWaveFunction = WaveFunction(OldPosition, VariationalParameters);
    +  QuantumForce(OldPosition, OldQuantumForce, VariationalParameters);
    +  // loop over monte carlo cycles 
    +  for (int cycles = 1; cycles <= NumberMCsamples; cycles++){ 
    +    // new position 
    +    for (int i = 0; i < NumberParticles; i++) { 
    +      for (int j = 0; j < Dimension; j++) {
    +	// gaussian deviate to compute new positions using a given timestep
    +	NewPosition(i,j) = OldPosition(i,j) + Normaldistribution(gen)*sqrt(timestep)+OldQuantumForce(i,j)*timestep*D;
    +	//	NewPosition(i,j) = OldPosition(i,j) + gaussian_deviate(&idum)*sqrt(timestep)+OldQuantumForce(i,j)*timestep*D;
    +      }  
    +      //  for the other particles we need to set the position to the old position since
    +      //  we move only one particle at the time
    +      for (int k = 0; k < NumberParticles; k++) {
    +	if ( k != i) {
    +	  for (int j = 0; j < Dimension; j++) {
    +	    NewPosition(k,j) = OldPosition(k,j);
    +	  }
    +	} 
    +      }
    +      double NewWaveFunction = WaveFunction(NewPosition, VariationalParameters); 
    +      QuantumForce(NewPosition, NewQuantumForce, VariationalParameters);
    +      //  we compute the log of the ratio of the greens functions to be used in the 
    +      //  Metropolis-Hastings algorithm
    +      double GreensFunction = 0.0;            
    +      for (int j = 0; j < Dimension; j++) {
    +	GreensFunction += 0.5*(OldQuantumForce(i,j)+NewQuantumForce(i,j))*
    +	  (D*timestep*0.5*(OldQuantumForce(i,j)-NewQuantumForce(i,j))-NewPosition(i,j)+OldPosition(i,j));
    +      }
    +      GreensFunction = exp(GreensFunction);
    +      // The Metropolis test is performed by moving one particle at the time
    +      if(UniformNumberGenerator(gen) <= GreensFunction*NewWaveFunction*NewWaveFunction/OldWaveFunction/OldWaveFunction ) { 
    +	for (int  j = 0; j < Dimension; j++) {
    +	  OldPosition(i,j) = NewPosition(i,j);
    +	  OldQuantumForce(i,j) = NewQuantumForce(i,j);
    +	}
    +	OldWaveFunction = NewWaveFunction;
    +      }
    +    }  //  end of loop over particles
    +    // compute local energy  
    +    double DeltaE = LocalEnergy(OldPosition, VariationalParameters);
    +    // update energies
    +    Energy += DeltaE;
    +    EnergySquared += DeltaE*DeltaE;
    +  }   // end of loop over MC trials   
    +  // update the energy average and its squared 
    +  cumulative_e = Energy/NumberMCsamples;
    +  cumulative_e2 = EnergySquared/NumberMCsamples;
    +}   // end MonteCarloSampling function  
    +
    +
    +// Function to compute the squared wave function and the quantum force
    +
    +double  WaveFunction(Matrix &r, Vector &VariationalParameters)
    +{
    +  double wf = 0.0;
    +  // full Slater determinant for two particles, replace with Slater det for more particles 
    +  wf  = SPwavefunction(singleparticle_pos2(r, 0), VariationalParameters(0))*SPwavefunction(singleparticle_pos2(r, 1),VariationalParameters(0));
    +  // contribution from Jastrow factor
    +  for (int i = 0; i < NumberParticles-1; i++) { 
    +    for (int j = i+1; j < NumberParticles; j++) {
    +      wf *= exp(RelativeDistance(r, i, j)/((1.0+VariationalParameters(1)*RelativeDistance(r, i, j))));
    +    }
    +  }
    +  return wf;
    +}
    +
    +// Function to calculate the local energy without numerical derivation of kinetic energy
    +
    +double  LocalEnergy(Matrix &r, Vector &VariationalParameters)
    +{
    +
    +  // compute the kinetic and potential energy from the single-particle part
    +  // for a many-electron system this has to be replaced by a Slater determinant
    +  // The absolute value of the interparticle length
    +  Matrix length( NumberParticles, NumberParticles);
    +  // Set up interparticle distance
    +  for (int i = 0; i < NumberParticles-1; i++) { 
    +    for(int j = i+1; j < NumberParticles; j++){
    +      length(i,j) = RelativeDistance(r, i, j);
    +      length(j,i) =  length(i,j);
    +    }
    +  }
    +  double KineticEnergy = 0.0;
    +  // Set up kinetic energy from Slater and Jastrow terms
    +  for (int i = 0; i < NumberParticles; i++) { 
    +    for (int k = 0; k < Dimension; k++) {
    +      double sum1 = 0.0; 
    +      for(int j = 0; j < NumberParticles; j++){
    +	if ( j != i) {
    +	  sum1 += JastrowDerivative(r, VariationalParameters(1), i, j, k);
    +	}
    +      }
    +      KineticEnergy += (sum1+DerivativeSPwavefunction(r(i,k),VariationalParameters(0)))*(sum1+DerivativeSPwavefunction(r(i,k),VariationalParameters(0)));
    +    }
    +  }
    +  KineticEnergy += -2*VariationalParameters(0)*NumberParticles;
    +  for (int i = 0; i < NumberParticles-1; i++) {
    +      for (int j = i+1; j < NumberParticles; j++) {
    +        KineticEnergy += 2.0/(pow(1.0 + VariationalParameters(1)*length(i,j),2))*(1.0/length(i,j)-2*VariationalParameters(1)/(1+VariationalParameters(1)*length(i,j)) );
    +      }
    +  }
    +  KineticEnergy *= -0.5;
    +  // Set up potential energy, external potential + eventual electron-electron repulsion
    +  double PotentialEnergy = 0;
    +  for (int i = 0; i < NumberParticles; i++) { 
    +    double DistanceSquared = singleparticle_pos2(r, i);
    +    PotentialEnergy += 0.5*DistanceSquared;  // sp energy HO part, note it has the oscillator frequency set to 1!
    +  }
    +  // Add the electron-electron repulsion
    +  for (int i = 0; i < NumberParticles-1; i++) { 
    +    for (int j = i+1; j < NumberParticles; j++) {
    +      PotentialEnergy += 1.0/length(i,j);          
    +    }
    +  }
    +  double LocalE = KineticEnergy+PotentialEnergy;
    +  return LocalE;
    +}
    +
    +// Compute the analytical expression for the quantum force
    +void  QuantumForce(Matrix &r, Matrix &qforce, Vector &VariationalParameters)
    +{
    +  // compute the first derivative 
    +  for (int i = 0; i < NumberParticles; i++) {
    +    for (int k = 0; k < Dimension; k++) {
    +      // single-particle part, replace with Slater det for larger systems
    +      double sppart = DerivativeSPwavefunction(r(i,k),VariationalParameters(0));
    +      //  Jastrow factor contribution
    +      double Jsum = 0.0;
    +      for (int j = 0; j < NumberParticles; j++) {
    +	if ( j != i) {
    +	  Jsum += JastrowDerivative(r, VariationalParameters(1), i, j, k);
    +	}
    +      }
    +      qforce(i,k) = 2.0*(Jsum+sppart);
    +    }
    +  }
    +} // end of QuantumForce function
    +
    +
    +#define ITMAX 200
    +#define EPS 3.0e-8
    +#define TOLX (4*EPS)
    +#define STPMX 100.0
    +
    +void dfpmin(Vector &p, int n, double gtol, int *iter, double *fret,
    +	    double(*func)(Vector &p), void (*dfunc)(Vector &p, Vector &g))
    +{
    +
    +  int check,i,its,j;
    +  double den,fac,fad,fae,fp,stpmax,sum=0.0,sumdg,sumxi,temp,test;
    +  Vector dg(n), g(n), hdg(n), pnew(n), xi(n);
    +  Matrix hessian(n,n);
    +
    +  fp=(*func)(p);
    +  (*dfunc)(p,g);
    +  for (i = 0;i < n;i++) {
    +    for (j = 0; j< n;j++) hessian(i,j)=0.0;
    +    hessian(i,i)=1.0;
    +    xi(i) = -g(i);
    +    sum += p(i)*p(i);
    +  }
    +  stpmax=STPMX*FMAX(sqrt(sum),(double)n);
    +  for (its=1;its<=ITMAX;its++) {
    +    *iter=its;
    +    lnsrch(n,p,fp,g,xi,pnew,fret,stpmax,&check,func);
    +    fp = *fret;
    +    for (i = 0; i< n;i++) {
    +      xi(i)=pnew(i)-p(i);
    +      p(i)=pnew(i);
    +    }
    +    test=0.0;
    +    for (i = 0;i< n;i++) {
    +      temp=fabs(xi(i))/FMAX(fabs(p(i)),1.0);
    +      if (temp > test) test=temp;
    +    }
    +    if (test < TOLX) {
    +      return;
    +    }
    +    for (i=0;i<n;i++) dg(i)=g(i);
    +    (*dfunc)(p,g);
    +    test=0.0;
    +    den=FMAX(*fret,1.0);
    +    for (i=0;i<n;i++) {
    +      temp=fabs(g(i))*FMAX(fabs(p(i)),1.0)/den;
    +      if (temp > test) test=temp;
    +    }
    +    if (test < gtol) {
    +      return;
    +    }
    +    for (i=0;i<n;i++) dg(i)=g(i)-dg(i);
    +    for (i=0;i<n;i++) {
    +      hdg(i)=0.0;
    +      for (j=0;j<n;j++) hdg(i) += hessian(i,j)*dg(j);
    +    }
    +    fac=fae=sumdg=sumxi=0.0;
    +    for (i=0;i<n;i++) {
    +      fac += dg(i)*xi(i);
    +      fae += dg(i)*hdg(i);
    +      sumdg += SQR(dg(i));
    +      sumxi += SQR(xi(i));
    +    }
    +    if (fac*fac > EPS*sumdg*sumxi) {
    +      fac=1.0/fac;
    +      fad=1.0/fae;
    +      for (i=0;i<n;i++) dg(i)=fac*xi(i)-fad*hdg(i);
    +      for (i=0;i<n;i++) {
    +	for (j=0;j<n;j++) {
    +	  hessian(i,j) += fac*xi(i)*xi(j)
    +	    -fad*hdg(i)*hdg(j)+fae*dg(i)*dg(j);
    +	}
    +      }
    +    }
    +    for (i=0;i<n;i++) {
    +      xi(i)=0.0;
    +      for (j=0;j<n;j++) xi(i) -= hessian(i,j)*g(j);
    +    }
    +  }
    +  cout << "too many iterations in dfpmin" << endl;
    +}
    +#undef ITMAX
    +#undef EPS
    +#undef TOLX
    +#undef STPMX
    +
    +#define ALF 1.0e-4
    +#define TOLX 1.0e-7
    +
    +void lnsrch(int n, Vector &xold, double fold, Vector &g, Vector &p, Vector &x,
    +	    double *f, double stpmax, int *check, double (*func)(Vector &p))
    +{
    +  int i;
    +  double a,alam,alam2,alamin,b,disc,f2,fold2,rhs1,rhs2,slope,sum,temp,
    +    test,tmplam;
    +
    +  *check=0;
    +  for (sum=0.0,i=0;i<n;i++) sum += p(i)*p(i);
    +  sum=sqrt(sum);
    +  if (sum > stpmax)
    +    for (i=0;i<n;i++) p(i) *= stpmax/sum;
    +  for (slope=0.0,i=0;i<n;i++)
    +    slope += g(i)*p(i);
    +  test=0.0;
    +  for (i=0;i<n;i++) {
    +    temp=fabs(p(i))/FMAX(fabs(xold(i)),1.0);
    +    if (temp > test) test=temp;
    +  }
    +  alamin=TOLX/test;
    +  alam=1.0;
    +  for (;;) {
    +    for (i=0;i<n;i++) x(i)=xold(i)+alam*p(i);
    +    *f=(*func)(x);
    +    if (alam < alamin) {
    +      for (i=0;i<n;i++) x(i)=xold(i);
    +      *check=1;
    +      return;
    +    } else if (*f <= fold+ALF*alam*slope) return;
    +    else {
    +      if (alam == 1.0)
    +	tmplam = -slope/(2.0*(*f-fold-slope));
    +      else {
    +	rhs1 = *f-fold-alam*slope;
    +	rhs2=f2-fold2-alam2*slope;
    +	a=(rhs1/(alam*alam)-rhs2/(alam2*alam2))/(alam-alam2);
    +	b=(-alam2*rhs1/(alam*alam)+alam*rhs2/(alam2*alam2))/(alam-alam2);
    +	if (a == 0.0) tmplam = -slope/(2.0*b);
    +	else {
    +	  disc=b*b-3.0*a*slope;
    +	  if (disc<0.0) cout << "Roundoff problem in lnsrch." << endl;
    +	  else tmplam=(-b+sqrt(disc))/(3.0*a);
    +	}
    +	if (tmplam>0.5*alam)
    +	  tmplam=0.5*alam;
    +      }
    +    }
    +    alam2=alam;
    +    f2 = *f;
    +    fold2=fold;
    +    alam=FMAX(tmplam,0.1*alam);
    +  }
    +}
    +#undef ALF
    +#undef TOLX
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    What is OpenMP

    +
    + +

    +

    +

    Many good tutorials online and excellent textbook

    +
      +
    1. Using OpenMP, by B. Chapman, G. Jost, and A. van der Pas
    2. +
    3. Many tutorials online like OpenMP official site
    4. +
    +
    + + +









    +

    Getting started, things to remember

    +
    + +

    +

    + + +
    +
    +
    +
    +
    +
    #include <omp.h>
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + +
    +
    +
    +
    +
    +
    #pragma omp...
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
    + + +









    +

    OpenMP syntax

    + + + +
    +
    +
    +
    +
    +
    #pragma omp construct [ clause ...]
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + +
    +
    +
    +
    +
    +
    #include <omp.h>
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Different OpenMP styles of parallelism

    +

    OpenMP supports several different ways to specify thread parallelism

    + + +









    +

    General code structure

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #include <omp.h>
    +main ()
    +{
    +int var1, var2, var3;
    +/* serial code */
    +/* ... */
    +/* start of a parallel region */
    +#pragma omp parallel private(var1, var2) shared(var3)
    +{
    +/* ... */
    +}
    +/* more serial code */
    +/* ... */
    +/* another parallel region */
    +#pragma omp parallel
    +{
    +/* ... */
    +}
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Parallel region

    +
    + +

    +

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel { ... }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
    + + +









    +

    Hello world, not again, please!

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #include <omp.h>
    +#include <cstdio>
    +int main (int argc, char *argv[])
    +{
    +int th_id, nthreads;
    +#pragma omp parallel private(th_id) shared(nthreads)
    +{
    +th_id = omp_get_thread_num();
    +printf("Hello World from thread %d\n", th_id);
    +#pragma omp barrier
    +if ( th_id == 0 ) {
    +nthreads = omp_get_num_threads();
    +printf("There are %d threads\n",nthreads);
    +}
    +}
    +return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Hello world, yet another variant

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #include <cstdio>
    +#include <omp.h>
    +int main(int argc, char *argv[]) 
    +{
    + omp_set_num_threads(4); 
    +#pragma omp parallel
    + {
    +   int id = omp_get_thread_num();
    +   int nproc = omp_get_num_threads(); 
    +   cout << "Hello world with id number and processes " <<  id <<  nproc << endl;
    + } 
    +return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Variables declared outside of the parallel region are shared by all threads +If a variable like id is declared outside of the +

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel, 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    it would have been shared by various the threads, possibly causing erroneous output

    + +
    + + +









    +

    Important OpenMP library routines

    +
    + +

    + +

    +
    + + +









    +

    Private variables

    +
    + +

    +

    Private clause can be used to make thread- private versions of such variables:

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel private(id)
    +{
    + int id = omp_get_thread_num();
    + cout << "My thread num" << id << endl; 
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
    + + +









    +

    Master region

    +
    + +

    +

    It is often useful to have only one thread execute some of the code in a parallel region. I/O statements are a common example

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel 
    +{
    +  #pragma omp master
    +   {
    +      int id = omp_get_thread_num();
    +      cout << "My thread num" << id << endl; 
    +   } 
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Parallel for loop

    +
    + +

    +

    + + +
    +
    +
    +
    +
    +
    #pragma omp for
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
    + + +









    +

    Parallel computations and loops

    + +
    + +

    +

    OpenMP provides an easy way to parallelize a loop

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel for
    +  for (i=0; i<n; i++) c[i] = a[i];
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    OpenMP handles index variable (no need to declare in for loop or make private)

    + +

    Which thread does which values? Several options.

    +
    + + +









    +

    Scheduling of loop computations

    + +
    + +

    +

    We can let the OpenMP runtime decide. The decision is about how the loop iterates are scheduled +and OpenMP defines three choices of loop scheduling: +

    +
      +
    1. Static: Predefined at compile time. Lowest overhead, predictable
    2. +
    3. Dynamic: Selection made at runtime
    4. +
    5. Guided: Special case of dynamic; attempts to reduce overhead
    6. +
    +
    + + +









    +

    Example code for loop scheduling

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #include <omp.h>
    +#define CHUNKSIZE 100
    +#define N 1000
    +int main (int argc, char *argv[])
    +{
    +int i, chunk;
    +float a[N], b[N], c[N];
    +for (i=0; i < N; i++) a[i] = b[i] = i * 1.0;
    +chunk = CHUNKSIZE;
    +#pragma omp parallel shared(a,b,c,chunk) private(i)
    +{
    +#pragma omp for schedule(dynamic,chunk)
    +for (i=0; i < N; i++) c[i] = a[i] + b[i];
    +} /* end of parallel region */
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Example code for loop scheduling, guided instead of dynamic

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #include <omp.h>
    +#define CHUNKSIZE 100
    +#define N 1000
    +int main (int argc, char *argv[])
    +{
    +int i, chunk;
    +float a[N], b[N], c[N];
    +for (i=0; i < N; i++) a[i] = b[i] = i * 1.0;
    +chunk = CHUNKSIZE;
    +#pragma omp parallel shared(a,b,c,chunk) private(i)
    +{
    +#pragma omp for schedule(guided,chunk)
    +for (i=0; i < N; i++) c[i] = a[i] + b[i];
    +} /* end of parallel region */
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    More on Parallel for loop

    +
    + +

    +

    + + +
    +
    +
    +
    +
    +
    // #pragma omp parallel and #pragma omp for
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    can be combined into

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel for
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    What can happen with this loop?

    + +
    + +

    +

    What happens with code like this

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel for
    +for (i=0; i<n; i++) sum += a[i]*a[i];
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    All threads can access the sum variable, but the addition is not atomic! It is important to avoid race between threads. So-called reductions in OpenMP are thus important for performance and for obtaining correct results. OpenMP lets us indicate that a variable is used for a reduction with a particular operator. The above code becomes

    + + +
    +
    +
    +
    +
    +
    sum = 0.0;
    +#pragma omp parallel for reduction(+:sum)
    +for (i=0; i<n; i++) sum += a[i]*a[i];
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Inner product

    +
    + +

    +$$ +\sum_{i=0}^{n-1} a_ib_i +$$ + + + +

    +
    +
    +
    +
    +
    int i;
    +double sum = 0.;
    +/* allocating and initializing arrays */
    +/* ... */
    +#pragma omp parallel for default(shared) private(i) reduction(+:sum)
    + for (i=0; i<N; i++) sum += a[i]*b[i];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Different threads do different tasks

    +
    + +

    + +

    Different threads do different tasks independently, each section is executed by one thread.

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel
    +{
    +#pragma omp sections
    +{
    +#pragma omp section
    +funcA ();
    +#pragma omp section
    +funcB ();
    +#pragma omp section
    +funcC ();
    +}
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Single execution

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #pragma omp single { ... }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The code is executed by one thread only, no guarantee which thread

    + +

    Can introduce an implicit barrier at the end

    + + +
    +
    +
    +
    +
    +
    #pragma omp master { ... }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Code executed by the master thread, guaranteed and no implicit barrier at the end.

    +
    + + +









    +

    Coordination and synchronization

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #pragma omp barrier
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Synchronization, must be encountered by all threads in a team (or none)

    + + +
    +
    +
    +
    +
    +
    #pragma omp ordered { a block of codes }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    is another form of synchronization (in sequential order). +The form +

    + + +
    +
    +
    +
    +
    +
    #pragma omp critical { a block of codes }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    and

    + + +
    +
    +
    +
    +
    +
    #pragma omp atomic { single assignment statement }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    is more efficient than

    + + +
    +
    +
    +
    +
    +
    #pragma omp critical
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Data scope

    +
    + +

    +

    +

    What are the purposes of these attributes

    + +
    + + +









    +

    Some remarks

    +
    + +

    + +

    +
    + + +









    +

    Parallelizing nested for-loops

    +
    + +

    + +

    + + +
    +
    +
    +
    +
    +
    for (i=0; i<100; i++)
    +    for (j=0; j<100; j++)
    +        a[i][j] = b[i][j] + c[i][j];
    +    }
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + + +
    +
    +
    +
    +
    +
    #pragma omp parallel for private(j)
    +for (i=0; i<100; i++)
    +    for (j=0; j<100; j++)
    +       a[i][j] = b[i][j] + c[i][j];
    +    }
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + +
    + + +









    +

    Nested parallelism

    +
    + +

    +

    When a thread in a parallel region encounters another parallel construct, it +may create a new team of threads and become the master of the new +team. +

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel num_threads(4)
    +{
    +/* .... */
    +#pragma omp parallel num_threads(2)
    +{
    +//  
    +}
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Parallel tasks

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #pragma omp task 
    +#pragma omp parallel shared(p_vec) private(i)
    +{
    +#pragma omp single
    +{
    +for (i=0; i<N; i++) {
    +  double r = random_number();
    +  if (p_vec[i] > r) {
    +#pragma omp task
    +   do_work (p_vec[i]);
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Common mistakes

    +
    + +

    +

    Race condition

    + + +
    +
    +
    +
    +
    +
    int nthreads;
    +#pragma omp parallel shared(nthreads)
    +{
    +nthreads = omp_get_num_threads();
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Deadlock

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel
    +{
    +...
    +#pragma omp critical
    +{
    +...
    +#pragma omp barrier
    +}
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + +

    Not all computations are simple

    +
    + +

    +

    Not all computations are simple loops where the data can be evenly +divided among threads without any dependencies between threads +

    + +

    An example is finding the location and value of the largest element in an array

    + + +
    +
    +
    +
    +
    +
    for (i=0; i<n; i++) { 
    +   if (x[i] > maxval) {
    +      maxval = x[i];
    +      maxloc = i; 
    +   }
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + +

    Not all computations are simple, competing threads

    +
    + +

    +

    All threads are potentially accessing and changing the same values, maxloc and maxval.

    +
      +
    1. OpenMP provides several ways to coordinate access to shared values
    2. +
    + + +
    +
    +
    +
    +
    +
    #pragma omp atomic
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +
    1. Only one thread at a time can execute the following statement (not block). We can use the critical option
    2. +
    + + +
    +
    +
    +
    +
    +
    #pragma omp critical
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +
    1. Only one thread at a time can execute the following block
    2. +
    +

    Atomic may be faster than critical but depends on hardware

    +
    + + +









    +

    How to find the max value using OpenMP

    +
    + +

    +

    Write down the simplest algorithm and look carefully for race conditions. How would you handle them? +The first step would be to parallelize as +

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel for
    + for (i=0; i<n; i++) {
    +    if (x[i] > maxval) {
    +      maxval = x[i];
    +      maxloc = i; 
    +    }
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Then deal with the race conditions

    +
    + +

    +

    Write down the simplest algorithm and look carefully for race conditions. How would you handle them? +The first step would be to parallelize as +

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel for
    + for (i=0; i<n; i++) {
    +#pragma omp critical
    +  {
    +     if (x[i] > maxval) {
    +       maxval = x[i];
    +       maxloc = i; 
    +     }
    +  }
    +} 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Exercise: write a code which implements this and give an estimate on performance. Perform several runs, +with a serial code only with and without vectorization and compare the serial code with the one that uses OpenMP. Run on different archictectures if you can. +

    +
    + +









    +

    What can slow down OpenMP performance?

    +

    Give it a thought!

    + +









    +

    What can slow down OpenMP performance?

    +
    + +

    +

    Performance poor because we insisted on keeping track of the maxval and location during the execution of the loop.

    + +

    This is a common source of performance issues, namely the description of the method used to compute a value imposes additional, unnecessary requirements or properties

    + +Idea: Have each thread find the maxloc in its own data, then combine and use temporary arrays indexed by thread number to hold the values found by each thread +
    + + +









    +

    Find the max location for each thread

    +
    + +

    + + +

    +
    +
    +
    +
    +
    int maxloc[MAX_THREADS], mloc;
    +double maxval[MAX_THREADS], mval; 
    +#pragma omp parallel shared(maxval,maxloc)
    +{
    +  int id = omp_get_thread_num(); 
    +  maxval[id] = -1.0e30;
    +#pragma omp for
    +   for (int i=0; i<n; i++) {
    +       if (x[i] > maxval[id]) { 
    +           maxloc[id] = i;
    +           maxval[id] = x[i]; 
    +       }
    +    }
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Combine the values from each thread

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #pragma omp flush (maxloc,maxval)
    +#pragma omp master
    +  {
    +    int nt = omp_get_num_threads(); 
    +    mloc = maxloc[0]; 
    +    mval = maxval[0]; 
    +    for (int i=1; i<nt; i++) {
    +        if (maxval[i] > mval) { 
    +           mval = maxval[i]; 
    +           mloc = maxloc[i];
    +        } 
    +     }
    +   }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Note that we let the master process perform the last operation.

    +
    + +









    +

    Matrix-matrix multiplication

    +

    This code computes the norm of a vector using OpenMp

    + + +
    +
    +
    +
    +
    +
    //  OpenMP program to compute vector norm by adding two other vectors
    +#include <cstdlib>
    +#include <iostream>
    +#include <cmath>
    +#include <iomanip>
    +#include  <omp.h>
    +# include <ctime>
    +
    +using namespace std; // note use of namespace
    +int main (int argc, char* argv[])
    +{
    +  // read in dimension of vector
    +  int n = atoi(argv[1]);
    +  double *a, *b, *c;
    +  int i;
    +  int thread_num;
    +  double wtime, Norm2, s, angle;
    +  cout << "  Perform addition of two vectors and compute the norm-2." << endl;
    +  omp_set_num_threads(4);
    +  thread_num = omp_get_max_threads ();
    +  cout << "  The number of processors available = " << omp_get_num_procs () << endl ;
    +  cout << "  The number of threads available    = " << thread_num <<  endl;
    +  cout << "  The matrix order n                 = " << n << endl;
    +
    +  s = 1.0/sqrt( (double) n);
    +  wtime = omp_get_wtime ( );
    +  // Allocate space for the vectors to be used
    +  a = new double [n]; b = new double [n]; c = new double [n];
    +  // Define parallel region
    +# pragma omp parallel for default(shared) private (angle, i) reduction(+:Norm2)
    +  // Set up values for vectors  a and b
    +  for (i = 0; i < n; i++){
    +      angle = 2.0*M_PI*i/ (( double ) n);
    +      a[i] = s*(sin(angle) + cos(angle));
    +      b[i] =  s*sin(2.0*angle);
    +      c[i] = 0.0;
    +  }
    +  // Then perform the vector addition
    +  for (i = 0; i < n; i++){
    +     c[i] += a[i]+b[i];
    +  }
    +  // Compute now the norm-2
    +  Norm2 = 0.0;
    +  for (i = 0; i < n; i++){
    +     Norm2  += c[i]*c[i];
    +  }
    +// end parallel region
    +  wtime = omp_get_wtime ( ) - wtime;
    +  cout << setiosflags(ios::showpoint | ios::uppercase);
    +  cout << setprecision(10) << setw(20) << "Time used  for norm-2 computation=" << wtime  << endl;
    +  cout << " Norm-2  = " << Norm2 << endl;
    +  // Free up space
    +  delete[] a;
    +  delete[] b;
    +  delete[] c;
    +  return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Matrix-matrix multiplication

    +

    This the matrix-matrix multiplication code with plain c++ memory allocation using OpenMP

    + + + +
    +
    +
    +
    +
    +
    //  Matrix-matrix multiplication and Frobenius norm of a matrix with OpenMP
    +#include <cstdlib>
    +#include <iostream>
    +#include <cmath>
    +#include <iomanip>
    +#include  <omp.h>
    +# include <ctime>
    +
    +using namespace std; // note use of namespace
    +int main (int argc, char* argv[])
    +{
    +  // read in dimension of square matrix
    +  int n = atoi(argv[1]);
    +  double **A, **B, **C;
    +  int i, j, k;
    +  int thread_num;
    +  double wtime, Fsum, s, angle;
    +  cout << "  Compute matrix product C = A * B and Frobenius norm." << endl;
    +  omp_set_num_threads(4);
    +  thread_num = omp_get_max_threads ();
    +  cout << "  The number of processors available = " << omp_get_num_procs () << endl ;
    +  cout << "  The number of threads available    = " << thread_num <<  endl;
    +  cout << "  The matrix order n                 = " << n << endl;
    +
    +  s = 1.0/sqrt( (double) n);
    +  wtime = omp_get_wtime ( );
    +  // Allocate space for the two matrices
    +  A = new double*[n]; B = new double*[n]; C = new double*[n];
    +  for (i = 0; i < n; i++){
    +    A[i] = new double[n];
    +    B[i] = new double[n];
    +    C[i] = new double[n];
    +  }
    +  // Define parallel region
    +# pragma omp parallel for default(shared) private (angle, i, j, k) reduction(+:Fsum)
    +  // Set up values for matrix A and B and zero matrix C
    +  for (i = 0; i < n; i++){
    +    for (j = 0; j < n; j++) {
    +      angle = 2.0*M_PI*i*j/ (( double ) n);
    +      A[i][j] = s * ( sin ( angle ) + cos ( angle ) );
    +      B[j][i] =  A[i][j];
    +    }
    +  }
    +  // Then perform the matrix-matrix multiplication
    +  for (i = 0; i < n; i++){
    +    for (j = 0; j < n; j++) {
    +       C[i][j] =  0.0;    
    +       for (k = 0; k < n; k++) {
    +            C[i][j] += A[i][k]*B[k][j];
    +       }
    +    }
    +  }
    +  // Compute now the Frobenius norm
    +  Fsum = 0.0;
    +  for (i = 0; i < n; i++){
    +    for (j = 0; j < n; j++) {
    +      Fsum += C[i][j]*C[i][j];
    +    }
    +  }
    +  Fsum = sqrt(Fsum);
    +// end parallel region and letting only one thread perform I/O
    +  wtime = omp_get_wtime ( ) - wtime;
    +  cout << setiosflags(ios::showpoint | ios::uppercase);
    +  cout << setprecision(10) << setw(20) << "Time used  for matrix-matrix multiplication=" << wtime  << endl;
    +  cout << "  Frobenius norm  = " << Fsum << endl;
    +  // Free up space
    +  for (int i = 0; i < n; i++){
    +    delete[] A[i];
    +    delete[] B[i];
    +    delete[] C[i];
    +  }
    +  delete[] A;
    +  delete[] B;
    +  delete[] C;
    +  return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    © 1999-2024, Morten Hjorth-Jensen Email morten.hjorth-jensen@fys.uio.no. Released under CC Attribution-NonCommercial 4.0 license diff --git a/doc/pub/week9/html/week9.html b/doc/pub/week9/html/week9.html index e26205e1..98929dfe 100644 --- a/doc/pub/week9/html/week9.html +++ b/doc/pub/week9/html/week9.html @@ -183,11 +183,388 @@ 2, None, 'blocking-transformations-final-expressions'), + ('More on the blocking method', + 2, + None, + 'more-on-the-blocking-method'), ('Example code form last week', 2, None, 'example-code-form-last-week'), - ('Resampling analysis', 2, None, 'resampling-analysis')]} + ('Resampling analysis', 2, None, 'resampling-analysis'), + ('Content', 2, None, 'content'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('More on optimization', 2, None, 'more-on-optimization'), + ('Optimization and profiling', + 2, + None, + 'optimization-and-profiling'), + ('Optimization and debugging', + 2, + None, + 'optimization-and-debugging'), + ('Other hints', 2, None, 'other-hints'), + ('Vectorization and the basic idea behind parallel computing', + 2, + None, + 'vectorization-and-the-basic-idea-behind-parallel-computing'), + ('A rough classification of hardware models', + 2, + None, + 'a-rough-classification-of-hardware-models'), + ('Shared memory and distributed memory', + 2, + None, + 'shared-memory-and-distributed-memory'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('Different parallel programming paradigms', + 2, + None, + 'different-parallel-programming-paradigms'), + ('What is vectorization?', 2, None, 'what-is-vectorization'), + ('Number of elements that can acted upon', + 2, + None, + 'number-of-elements-that-can-acted-upon'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Operation counts for scalar operation', + 2, + None, + 'operation-counts-for-scalar-operation'), + ('Number of elements that can acted upon, examples', + 2, + None, + 'number-of-elements-that-can-acted-upon-examples'), + ('Number of operations when vectorized', + 2, + None, + 'number-of-operations-when-vectorized'), + ('"A simple test case with and without ' + 'vectorization":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp"', + 2, + None, + 'a-simple-test-case-with-and-without-vectorization-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program7-cpp'), + ('Compiling with and without vectorization', + 2, + None, + 'compiling-with-and-without-vectorization'), + ('Compiling with and without vectorization using clang', + 2, + None, + 'compiling-with-and-without-vectorization-using-clang'), + ('Automatic vectorization and vectorization inhibitors, criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-criteria'), + ('Automatic vectorization and vectorization inhibitors, exit ' + 'criteria', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-exit-criteria'), + ('Automatic vectorization and vectorization inhibitors, ' + 'straight-line code', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-straight-line-code'), + ('Automatic vectorization and vectorization inhibitors, nested ' + 'loops', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-nested-loops'), + ('Automatic vectorization and vectorization inhibitors, function ' + 'calls', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-function-calls'), + ('Automatic vectorization and vectorization inhibitors, data ' + 'dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, more ' + 'data dependencies', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-more-data-dependencies'), + ('Automatic vectorization and vectorization inhibitors, memory ' + 'stride', + 2, + None, + 'automatic-vectorization-and-vectorization-inhibitors-memory-stride'), + ('Memory management', 2, None, 'memory-management'), + ('Memory and communication', 2, None, 'memory-and-communication'), + ('Measuring performance', 2, None, 'measuring-performance'), + ('Problems with measuring time', + 2, + None, + 'problems-with-measuring-time'), + ('Problems with cold start', 2, None, 'problems-with-cold-start'), + ('Problems with smart compilers', + 2, + None, + 'problems-with-smart-compilers'), + ('Problems with interference', + 2, + None, + 'problems-with-interference'), + ('Problems with measuring performance', + 2, + None, + 'problems-with-measuring-performance'), + ('Thomas algorithm for tridiagonal linear algebra equations', + 2, + None, + 'thomas-algorithm-for-tridiagonal-linear-algebra-equations'), + ('Thomas algorithm, forward substitution', + 2, + None, + 'thomas-algorithm-forward-substitution'), + ('Thomas algorithm, backward substitution', + 2, + None, + 'thomas-algorithm-backward-substitution'), + ('Thomas algorithm and counting of operations (floating point ' + 'and memory)', + 2, + None, + 'thomas-algorithm-and-counting-of-operations-floating-point-and-memory'), + ('"Example: Transpose of a ' + 'matrix":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp"', + 2, + None, + 'example-transpose-of-a-matrix-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program8-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-lectureprograms-programs-classes-cpp-program9-cpp'), + ('How do we define speedup? Simplest form', + 2, + None, + 'how-do-we-define-speedup-simplest-form'), + ('How do we define speedup? Correct baseline', + 2, + None, + 'how-do-we-define-speedup-correct-baseline'), + ('Parallel speedup', 2, None, 'parallel-speedup'), + ('Speedup and memory', 2, None, 'speedup-and-memory'), + ('Upper bounds on speedup', 2, None, 'upper-bounds-on-speedup'), + ("Amdahl's law", 2, None, 'amdahl-s-law'), + ('How much is parallelizable', + 2, + None, + 'how-much-is-parallelizable'), + ("Today's situation of parallel computing", + 2, + None, + 'today-s-situation-of-parallel-computing'), + ('Overhead present in parallel computing', + 2, + None, + 'overhead-present-in-parallel-computing'), + ('Parallelizing a sequential algorithm', + 2, + None, + 'parallelizing-a-sequential-algorithm'), + ('Strategies', 2, None, 'strategies'), + ('How do I run MPI on a PC/Laptop? MPI', + 2, + None, + 'how-do-i-run-mpi-on-a-pc-laptop-mpi'), + ('Can I do it on my own PC/laptop? OpenMP installation', + 2, + None, + 'can-i-do-it-on-my-own-pc-laptop-openmp-installation'), + ('Installing MPI', 2, None, 'installing-mpi'), + ('Installing MPI and using Qt', + 2, + None, + 'installing-mpi-and-using-qt'), + ('What is Message Passing Interface (MPI)?', + 2, + None, + 'what-is-message-passing-interface-mpi'), + ('Going Parallel with MPI', 2, None, 'going-parallel-with-mpi'), + ('MPI is a library', 2, None, 'mpi-is-a-library'), + ('Bindings to MPI routines', 2, None, 'bindings-to-mpi-routines'), + ('Communicator', 2, None, 'communicator'), + ('Some of the most important MPI functions', + 2, + None, + 'some-of-the-most-important-mpi-functions'), + ('"The first MPI C/C++ ' + 'program":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp"', + 2, + None, + 'the-first-mpi-c-c-program-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program2-cpp'), + ('The Fortran program', 2, None, 'the-fortran-program'), + ('Note 1', 2, None, 'note-1'), + ('"Ordered output with ' + 'MPIBarrier":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp"', + 2, + None, + 'ordered-output-with-mpibarrier-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program3-cpp'), + ('Note 2', 2, None, 'note-2'), + ('"Ordered ' + 'output":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp"', + 2, + None, + 'ordered-output-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program4-cpp'), + ('Note 3', 2, None, 'note-3'), + ('Note 4', 2, None, 'note-4'), + ('"Numerical integration in ' + 'parallel":"https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp"', + 2, + None, + 'numerical-integration-in-parallel-https-github-com-compphysics-computationalphysics2-blob-gh-pages-doc-programs-lectureprograms-programs-mpi-chapter07-program6-cpp'), + ('Dissection of trapezoidal rule with $MPI\\_reduce$', + 2, + None, + 'dissection-of-trapezoidal-rule-with-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Integrating with _MPI_', 2, None, 'integrating-with-mpi'), + ('How do I use $MPI\\_reduce$?', + 2, + None, + 'how-do-i-use-mpi-reduce'), + ('More on $MPI\\_Reduce$', 2, None, 'more-on-mpi-reduce'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('Dissection of trapezoidal rule', + 2, + None, + 'dissection-of-trapezoidal-rule'), + ('"The quantum dot program for two ' + 'electrons":"https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp"', + 2, + None, + 'the-quantum-dot-program-for-two-electrons-https-github-com-compphysics-computationalphysics2-blob-master-doc-programs-parallelizationmpi-mpivmcqdot-cpp'), + ('What is OpenMP', 2, None, 'what-is-openmp'), + ('Getting started, things to remember', + 2, + None, + 'getting-started-things-to-remember'), + ('OpenMP syntax', 2, None, 'openmp-syntax'), + ('Different OpenMP styles of parallelism', + 2, + None, + 'different-openmp-styles-of-parallelism'), + ('General code structure', 2, None, 'general-code-structure'), + ('Parallel region', 2, None, 'parallel-region'), + ('Hello world, not again, please!', + 2, + None, + 'hello-world-not-again-please'), + ('Hello world, yet another variant', + 2, + None, + 'hello-world-yet-another-variant'), + ('Important OpenMP library routines', + 2, + None, + 'important-openmp-library-routines'), + ('Private variables', 2, None, 'private-variables'), + ('Master region', 2, None, 'master-region'), + ('Parallel for loop', 2, None, 'parallel-for-loop'), + ('Parallel computations and loops', + 2, + None, + 'parallel-computations-and-loops'), + ('Scheduling of loop computations', + 2, + None, + 'scheduling-of-loop-computations'), + ('Example code for loop scheduling', + 2, + None, + 'example-code-for-loop-scheduling'), + ('Example code for loop scheduling, guided instead of dynamic', + 2, + None, + 'example-code-for-loop-scheduling-guided-instead-of-dynamic'), + ('More on Parallel for loop', + 2, + None, + 'more-on-parallel-for-loop'), + ('What can happen with this loop?', + 2, + None, + 'what-can-happen-with-this-loop'), + ('Inner product', 2, None, 'inner-product'), + ('Different threads do different tasks', + 2, + None, + 'different-threads-do-different-tasks'), + ('Single execution', 2, None, 'single-execution'), + ('Coordination and synchronization', + 2, + None, + 'coordination-and-synchronization'), + ('Data scope', 2, None, 'data-scope'), + ('Some remarks', 2, None, 'some-remarks'), + ('Parallelizing nested for-loops', + 2, + None, + 'parallelizing-nested-for-loops'), + ('Nested parallelism', 2, None, 'nested-parallelism'), + ('Parallel tasks', 2, None, 'parallel-tasks'), + ('Common mistakes', 2, None, 'common-mistakes'), + ('Not all computations are simple', + 2, + None, + 'not-all-computations-are-simple'), + ('Not all computations are simple, competing threads', + 2, + None, + 'not-all-computations-are-simple-competing-threads'), + ('How to find the max value using OpenMP', + 2, + None, + 'how-to-find-the-max-value-using-openmp'), + ('Then deal with the race conditions', + 2, + None, + 'then-deal-with-the-race-conditions'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('What can slow down OpenMP performance?', + 2, + None, + 'what-can-slow-down-openmp-performance'), + ('Find the max location for each thread', + 2, + None, + 'find-the-max-location-for-each-thread'), + ('Combine the values from each thread', + 2, + None, + 'combine-the-values-from-each-thread'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpvectornorm-cpp'), + ('"Matrix-matrix ' + 'multiplication":"https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp"', + 2, + None, + 'matrix-matrix-multiplication-https-github-com-compphysics-computationalphysicsmsu-blob-master-doc-programs-parallelizationopenmp-openmpmatrixmatrixmult-cpp')]} end of tocinfo --> @@ -245,15 +622,7 @@

    Overview of week 11, March 11-15

    -
    -Teaching Material, videos and written material -

    -

    -
    - +

    Note, these notes contain additional material om optimization and parallelization. Parts of this material will be discussed this week.











    Why resampling methods ?

    @@ -439,9 +808,9 @@

    Introducing the correlation functi

    Resampling methods: Blocking

    The blocking method was made popular by Flyvbjerg and Pedersen (1989) -and has become one of the standard ways to estimate -\( V(\widehat{\theta}) \) for exactly one \( \widehat{\theta} \), namely -\( \widehat{\theta} = \overline{X} \). +and has become one of the standard ways to estimate the variance +\( \mathrm{var}(\widehat{\theta}) \) for exactly one estimator \( \widehat{\theta} \), namely +\( \widehat{\theta} = \overline{X} \), the mean value.

    Assume \( n = 2^d \) for some integer \( d>1 \) and \( X_1,X_2,\cdots, X_n \) is a stationary time series to begin with. @@ -564,10 +933,14 @@

    Blocking Transformations, fi \end{align} $$ + +









    +

    More on the blocking method

    +

    Flyvbjerg and Petersen demonstrated that the sequence \( \{e_k\}_{k=0}^{d-1} \) is decreasing, and conjecture that the term \( e_k \) can be made as small as we would like by making \( k \) (and hence -\( d \)) sufficiently large. The sequence is decreasing (Master of Science thesis by Marius Jonsson, UiO 2018). +\( d \)) sufficiently large. The sequence is decreasing. It means we can apply blocking transformations until \( e_k \) is sufficiently small, and then estimate \( \mathrm{var}(\overline{X}) \) by \( \widehat{\sigma}^2_k/n_k \). @@ -902,6 +1275,5073 @@

    Resampling analysis

    +









    +

    Content

    + +









    +

    Optimization and profiling

    +
    + +

    + +

    Till now we have not paid much attention to speed and possible optimization possibilities +inherent in the various compilers. We have compiled and linked as +

    + + +
    +
    +
    +
    +
    +
    c++  -c  mycode.cpp
    +c++  -o  mycode.exe  mycode.o
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    For Fortran replace with for example gfortran or ifort. +This is what we call a flat compiler option and should be used when we develop the code. +It produces normally a very large and slow code when translated to machine instructions. +We use this option for debugging and for establishing the correct program output because +every operation is done precisely as the user specified it. +

    + +

    It is instructive to look up the compiler manual for further instructions by writing

    + + +
    +
    +
    +
    +
    +
    man c++
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +









    +

    More on optimization

    +
    + +

    +

    We have additional compiler options for optimization. These may include procedure inlining where +performance may be improved, moving constants inside loops outside the loop, +identify potential parallelism, include automatic vectorization or replace a division with a reciprocal +and a multiplication if this speeds up the code. +

    + + +
    +
    +
    +
    +
    +
    c++  -O3 -c  mycode.cpp
    +c++  -O3 -o  mycode.exe  mycode.o
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    This (other options are -O2 or -Ofast) is the recommended option.

    +
    + +









    +

    Optimization and profiling

    +
    + +

    +

    It is also useful to profile your program under the development stage. +You would then compile with +

    + + +
    +
    +
    +
    +
    +
    c++  -pg -O3 -c  mycode.cpp
    +c++  -pg -O3 -o  mycode.exe  mycode.o
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    After you have run the code you can obtain the profiling information via

    + + +
    +
    +
    +
    +
    +
    gprof mycode.exe >  ProfileOutput
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    When you have profiled properly your code, you must take out this option as it +slows down performance. +For memory tests use valgrind. An excellent environment for all these aspects, and much more, is Qt creator. +

    +
    + + +









    +

    Optimization and debugging

    +
    + +

    +

    Adding debugging options is a very useful alternative under the development stage of a program. +You would then compile with +

    + + +
    +
    +
    +
    +
    +
    c++  -g -O0 -c  mycode.cpp
    +c++  -g -O0 -o  mycode.exe  mycode.o
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    This option generates debugging information allowing you to trace for example if an array is properly allocated. Some compilers work best with the no optimization option -O0.

    +
    + +
    +Other optimization flags +

    +

    Depending on the compiler, one can add flags which generate code that catches integer overflow errors. +The flag -ftrapv does this for the CLANG compiler on OS X operating systems. +

    +
    + + +









    +

    Other hints

    +
    + +

    +

    In general, irrespective of compiler options, it is useful to

    +
      +
    • avoid if tests or call to functions inside loops, if possible.
    • +
    • avoid multiplication with constants inside loops if possible
    • +
    +

    Here is an example of a part of a program where specific operations lead to a slower code

    + + +
    +
    +
    +
    +
    +
    k = n-1;
    +for (i = 0; i < n; i++){
    +    a[i] = b[i] +c*d;
    +    e = g[k];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    A better code is

    + + +
    +
    +
    +
    +
    +
    temp = c*d;
    +for (i = 0; i < n; i++){
    +    a[i] = b[i] + temp;
    +}
    +e = g[n-1];
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Here we avoid a repeated multiplication inside a loop. +Most compilers, depending on compiler flags, identify and optimize such bottlenecks on their own, without requiring any particular action by the programmer. However, it is always useful to single out and avoid code examples like the first one discussed here. +

    +
    + + +









    +

    Vectorization and the basic idea behind parallel computing

    +
    + +

    +

    Present CPUs are highly parallel processors with varying levels of parallelism. The typical situation can be described via the following three statements.

    +
      +
    • Pursuit of shorter computation time and larger simulation size gives rise to parallel computing.
    • +
    • Multiple processors are involved to solve a global problem.
    • +
    • The essence is to divide the entire computation evenly among collaborative processors. Divide and conquer.
    • +
    +

    Before we proceed with a more detailed discussion of topics like vectorization and parallelization, we need to remind ourselves about some basic features of different hardware models.

    +
    + + +









    +

    A rough classification of hardware models

    +
    + +

    + +

      +
    • Conventional single-processor computers are named SISD (single-instruction-single-data) machines.
    • +
    • SIMD (single-instruction-multiple-data) machines incorporate the idea of parallel processing, using a large number of processing units to execute the same instruction on different data.
    • +
    • Modern parallel computers are so-called MIMD (multiple-instruction-multiple-data) machines and can execute different instruction streams in parallel on different data.
    • +
    +
    + +









    +

    Shared memory and distributed memory

    +
    + +

    +

    One way of categorizing modern parallel computers is to look at the memory configuration.

    +
      +
    • In shared memory systems the CPUs share the same address space. Any CPU can access any data in the global memory.
    • +
    • In distributed memory systems each CPU has its own memory.
    • +
    +

    The CPUs are connected by some network and may exchange messages.

    +
    + + +









    +

    Different parallel programming paradigms

    +
    + +

    + +

      +
    • Task parallelism: the work of a global problem can be divided into a number of independent tasks, which rarely need to synchronize. Monte Carlo simulations represent a typical situation. Integration is another. However this paradigm is of limited use.
    • +
    • Data parallelism: use of multiple threads (e.g. one or more threads per processor) to dissect loops over arrays etc. Communication and synchronization between processors are often hidden, thus easy to program. However, the user surrenders much control to a specialized compiler. Examples of data parallelism are compiler-based parallelization and OpenMP directives.
    • +
    +
    + +









    +

    Different parallel programming paradigms

    +
    + +

    + +

      +
    • Message passing: all involved processors have an independent memory address space. The user is responsible for partitioning the data/work of a global problem and distributing the subproblems to the processors. Collaboration between processors is achieved by explicit message passing, which is used for data transfer plus synchronization.
    • +
    • This paradigm is the most general one where the user has full control. Better parallel efficiency is usually achieved by explicit message passing. However, message-passing programming is more difficult.
    • +
    +
    + + + +

    What is vectorization?

    +

    Vectorization is a special +case of Single Instructions Multiple Data (SIMD) to denote a single +instruction stream capable of operating on multiple data elements in +parallel. +We can think of vectorization as the unrolling of loops accompanied with SIMD instructions. +

    + +

    Vectorization is the process of converting an algorithm that performs scalar operations +(typically one operation at the time) to vector operations where a single operation can refer to many simultaneous operations. +Consider the following example +

    + + +
    +
    +
    +
    +
    +
    for (i = 0; i < n; i++){
    +    a[i] = b[i] + c[i];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    If the code is not vectorized, the compiler will simply start with the first element and +then perform subsequent additions operating on one address in memory at the time. +

    + + +

    Number of elements that can acted upon

    +

    A SIMD instruction can operate on multiple data elements in one single instruction. +It uses the so-called 128-bit SIMD floating-point register. +In this sense, vectorization adds some form of parallelism since one instruction is applied +to many parts of say a vector. +

    + +

    The number of elements which can be operated on in parallel +range from four single-precision floating point data elements in so-called +Streaming SIMD Extensions and two double-precision floating-point data +elements in Streaming SIMD Extensions 2 to sixteen byte operations in +a 128-bit register in Streaming SIMD Extensions 2. Thus, vector-length +ranges from 2 to 16, depending on the instruction extensions used and +on the data type. +

    + +

    IN summary, our instructions operate on 128 bit (16 byte) operands

    + + +

    Number of elements that can acted upon, examples

    +

    We start with the simple scalar operations given by

    + + +
    +
    +
    +
    +
    +
    for (i = 0; i < n; i++){
    +    a[i] = b[i] + c[i];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    If the code is not vectorized and we have a 128-bit register to store a 32 bits floating point number, +it means that we have \( 3\times 32 \) bits that are not used. +

    + +

    We have thus unused space in our SIMD registers. These registers could hold three additional integers.

    + + +

    Operation counts for scalar operation

    +

    The code

    + + +
    +
    +
    +
    +
    +
    for (i = 0; i < n; i++){
    +    a[i] = b[i] + c[i];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    has for \( n \) repeats

    +
      +
    1. one load for \( c[i] \) in address 1
    2. +
    3. one load for \( b[i] \) in address 2
    4. +
    5. add \( c[i] \) and \( b[i] \) to give \( a[i] \)
    6. +
    7. store \( a[i] \) in address 2
    8. +
    + +

    Number of elements that can acted upon, examples

    +

    If we vectorize the code, we can perform, with a 128-bit register four simultaneous operations, that is +we have +

    + + +
    +
    +
    +
    +
    +
    for (i = 0; i < n; i+=4){
    +    a[i] = b[i] + c[i];
    +    a[i+1] = b[i+1] + c[i+1];
    +    a[i+2] = b[i+2] + c[i+2];
    +    a[i+3] = b[i+3] + c[i+3];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Four additions are now done in a single step.

    + + +

    Number of operations when vectorized

    +

    For \( n/4 \) repeats assuming floats or integers

    +
      +
    1. one vector load for \( c[i] \) in address 1
    2. +
    3. one load for \( b[i] \) in address 2
    4. +
    5. add \( c[i] \) and \( b[i] \) to give \( a[i] \)
    6. +
    7. store \( a[i] \) in address 2
    8. +
    +









    +

    A simple test case with and without vectorization

    +

    We implement these operations in a simple c++ program that computes at the end the norm of a vector.

    + + + +
    +
    +
    +
    +
    +
    #include <cstdlib>
    +#include <iostream>
    +#include <cmath>
    +#include <iomanip>
    +#include "time.h"
    +
    +using namespace std; // note use of namespace
    +int main (int argc, char* argv[])
    +{
    +  // read in dimension of square matrix
    +  int n = atoi(argv[1]);
    +  double s = 1.0/sqrt( (double) n);
    +  double *a, *b, *c;
    +  // Start timing
    +  clock_t start, finish;
    +  start = clock();
    +// Allocate space for the vectors to be used
    +    a = new double [n]; b = new double [n]; c = new double [n];
    +  // Define parallel region
    +  // Set up values for vectors  a and b
    +  for (int i = 0; i < n; i++){
    +    double angle = 2.0*M_PI*i/ (( double ) n);
    +    a[i] = s*(sin(angle) + cos(angle));
    +    b[i] =  s*sin(2.0*angle);
    +    c[i] = 0.0;
    +  }
    +  // Then perform the vector addition
    +  for (int i = 0; i < n; i++){
    +    c[i] += a[i]+b[i];
    +  }
    +  // Compute now the norm-2
    +  double Norm2 = 0.0;
    +  for (int i = 0; i < n; i++){
    +    Norm2  += c[i]*c[i];
    +  }
    +  finish = clock();
    +  double timeused = (double) (finish - start)/(CLOCKS_PER_SEC );
    +  cout << setiosflags(ios::showpoint | ios::uppercase);
    +  cout << setprecision(10) << setw(20) << "Time used  for norm computation=" << timeused  << endl;
    +  cout << "  Norm-2  = " << Norm2 << endl;
    +  // Free up space
    +  delete[] a;
    +  delete[] b;
    +  delete[] c;
    +  return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + +

    Compiling with and without vectorization

    +

    We can compile and link without vectorization using the clang c++ compiler

    + + +
    +
    +
    +
    +
    +
    clang -o novec.x vecexample.cpp
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    and with vectorization (and additional optimizations)

    + + +
    +
    +
    +
    +
    +
    clang++ -O3 -Rpass=loop-vectorize -o  vec.x vecexample.cpp 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The speedup depends on the size of the vectors. In the example here we have run with \( 10^7 \) elements. +The example here was run on an IMac17.1 with OSX El Capitan (10.11.4) as operating system and an Intel i5 3.3 GHz CPU. +

    + + +
    +
    +
    +
    +
    +
    Compphys:~ hjensen$ ./vec.x 10000000
    +Time used  for norm computation=0.04720500000
    +Compphys:~ hjensen$ ./novec.x 10000000
    +Time used  for norm computation=0.03311700000
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    This particular C++ compiler speeds up the above loop operations with a factor of 1.5 +Performing the same operations for \( 10^9 \) elements results in a smaller speedup since reading from main memory is required. The non-vectorized code is seemingly faster. +

    + + +
    +
    +
    +
    +
    +
    Compphys:~ hjensen$ ./vec.x 1000000000
    +Time used  for norm computation=58.41391100
    +Compphys:~ hjensen$ ./novec.x 1000000000
    +Time used  for norm computation=46.51295300
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    We will discuss these issues further in the next slides.

    + + +

    Compiling with and without vectorization using clang

    +

    We can compile and link without vectorization with clang compiler

    + + +
    +
    +
    +
    +
    +
    clang++ -o -fno-vectorize novec.x vecexample.cpp
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    and with vectorization

    + + +
    +
    +
    +
    +
    +
    clang++ -O3 -Rpass=loop-vectorize -o  vec.x vecexample.cpp 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    We can also add vectorization analysis, see for example

    + + +
    +
    +
    +
    +
    +
    clang++ -O3 -Rpass-analysis=loop-vectorize -o  vec.x vecexample.cpp 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    or figure out if vectorization was missed

    + + +
    +
    +
    +
    +
    +
    clang++ -O3 -Rpass-missed=loop-vectorize -o  vec.x vecexample.cpp 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Automatic vectorization and vectorization inhibitors, criteria

    + +

    Not all loops can be vectorized, as discussed in Intel's guide to vectorization

    + +

    An important criteria is that the loop counter \( n \) is known at the entry of the loop.

    + + +
    +
    +
    +
    +
    +
      for (int j = 0; j < n; j++) {
    +    a[j] = cos(j*1.0);
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The variable \( n \) does need to be known at compile time. However, this variable must stay the same for the entire duration of the loop. It implies that an exit statement inside the loop cannot be data dependent.

    + +









    +

    Automatic vectorization and vectorization inhibitors, exit criteria

    + +

    An exit statement should in general be avoided. +If the exit statement contains data-dependent conditions, the loop cannot be vectorized. +The following is an example of a non-vectorizable loop +

    + + +
    +
    +
    +
    +
    +
      for (int j = 0; j < n; j++) {
    +    a[j] = cos(j*1.0);
    +    if (a[j] < 0 ) break;
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Avoid loop termination conditions and opt for a single entry loop variable \( n \). The lower and upper bounds have to be kept fixed within the loop.

    + +









    +

    Automatic vectorization and vectorization inhibitors, straight-line code

    + +

    SIMD instructions perform the same type of operations multiple times. +A switch statement leads thus to a non-vectorizable loop since different statemens cannot branch. +The following code can however be vectorized since the if statement is implemented as a masked assignment. +

    + + +
    +
    +
    +
    +
    +
      for (int j = 0; j < n; j++) {
    +    double x  = cos(j*1.0);
    +    if (x > 0 ) {
    +       a[j] =  x*sin(j*2.0); 
    +    }
    +    else {
    +       a[j] = 0.0;
    +    }
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    These operations can be performed for all data elements but only those elements which the mask evaluates as true are stored. In general, one should avoid branches such as switch, go to, or return statements or if constructs that cannot be treated as masked assignments.

    + +









    +

    Automatic vectorization and vectorization inhibitors, nested loops

    + +

    Only the innermost loop of the following example is vectorized

    + + +
    +
    +
    +
    +
    +
      for (int i = 0; i < n; i++) {
    +      for (int j = 0; j < n; j++) {
    +           a[i][j] += b[i][j];
    +      }  
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The exception is if an original outer loop is transformed into an inner loop as the result of compiler optimizations.

    + +









    +

    Automatic vectorization and vectorization inhibitors, function calls

    + +

    Calls to programmer defined functions ruin vectorization. However, calls to intrinsic functions like +\( \sin{x} \), \( \cos{x} \), \( \exp{x} \) etc are allowed since they are normally efficiently vectorized. +The following example is fully vectorizable +

    + + +
    +
    +
    +
    +
    +
      for (int i = 0; i < n; i++) {
    +      a[i] = log10(i)*cos(i);
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Similarly, inline functions defined by the programmer, allow for vectorization since the function statements are glued into the actual place where the function is called.

    + +









    +

    Automatic vectorization and vectorization inhibitors, data dependencies

    + +

    One has to keep in mind that vectorization changes the order of operations inside a loop. A so-called +read-after-write statement with an explicit flow dependency cannot be vectorized. The following code +

    + + +
    +
    +
    +
    +
    +
      double b = 15.;
    +  for (int i = 1; i < n; i++) {
    +      a[i] = a[i-1] + b;
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    is an example of flow dependency and results in wrong numerical results if vectorized. For a scalar operation, the value \( a[i-1] \) computed during the iteration is loaded into the right-hand side and the results are fine. In vector mode however, with a vector length of four, the values \( a[0] \), \( a[1] \), \( a[2] \) and \( a[3] \) from the previous loop will be loaded into the right-hand side and produce wrong results. That is, we have

    + + +
    +
    +
    +
    +
    +
       a[1] = a[0] + b;
    +   a[2] = a[1] + b;
    +   a[3] = a[2] + b;
    +   a[4] = a[3] + b;
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    and if the two first iterations are executed at the same by the SIMD instruction, the value of say \( a[1] \) could be used by the second iteration before it has been calculated by the first iteration, leading thereby to wrong results.

    + +









    +

    Automatic vectorization and vectorization inhibitors, more data dependencies

    + +

    On the other hand, a so-called +write-after-read statement can be vectorized. The following code +

    + + +
    +
    +
    +
    +
    +
      double b = 15.;
    +  for (int i = 1; i < n; i++) {
    +      a[i-1] = a[i] + b;
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    is an example of flow dependency that can be vectorized since no iteration with a higher value of \( i \) +can complete before an iteration with a lower value of \( i \). However, such code leads to problems with parallelization. +

    + +









    +

    Automatic vectorization and vectorization inhibitors, memory stride

    + +

    For C++ programmers it is also worth keeping in mind that an array notation is preferred to the more compact use of pointers to access array elements. The compiler can often not tell if it is safe to vectorize the code.

    + +

    When dealing with arrays, you should also avoid memory stride, since this slows down considerably vectorization. When you access array element, write for example the inner loop to vectorize using unit stride, that is, access successively the next array element in memory, as shown here

    + + +
    +
    +
    +
    +
    +
      for (int i = 0; i < n; i++) {
    +      for (int j = 0; j < n; j++) {
    +           a[i][j] += b[i][j];
    +      }  
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Memory management

    +

    The main memory contains the program data

    +
      +
    1. Cache memory contains a copy of the main memory data
    2. +
    3. Cache is faster but consumes more space and power. It is normally assumed to be much faster than main memory
    4. +
    5. Registers contain working data only
    6. +
        +
      • Modern CPUs perform most or all operations only on data in register
      • +
      +
    7. Multiple Cache memories contain a copy of the main memory data
    8. +
        +
      • Cache items accessed by their address in main memory
      • +
      • L1 cache is the fastest but has the least capacity
      • +
      • L2, L3 provide intermediate performance/size tradeoffs
      • +
      +
    +

    Loads and stores to memory can be as important as floating point operations when we measure performance.

    + +









    +

    Memory and communication

    + +
      +
    1. Most communication in a computer is carried out in chunks, blocks of bytes of data that move together
    2. +
    3. In the memory hierarchy, data moves between memory and cache, and between different levels of cache, in groups called lines
    4. +
        +
      • Lines are typically 64-128 bytes, or 8-16 double precision words
      • +
      • Even if you do not use the data, it is moved and occupies space in the cache
      • +
      +
    +

    Many of these performance features are not captured in most programming languages.

    + +









    +

    Measuring performance

    + +

    How do we measure performance? What is wrong with this code to time a loop?

    + + +
    +
    +
    +
    +
    +
      clock_t start, finish;
    +  start = clock();
    +  for (int j = 0; j < i; j++) {
    +    a[j] = b[j]+b[j]*c[j];
    +  }
    +  finish = clock();
    +  double timeused = (double) (finish - start)/(CLOCKS_PER_SEC );
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Problems with measuring time

    +
      +
    1. Timers are not infinitely accurate
    2. +
    3. All clocks have a granularity, the minimum time that they can measure
    4. +
    5. The error in a time measurement, even if everything is perfect, may be the size of this granularity (sometimes called a clock tick)
    6. +
    7. Always know what your clock granularity is
    8. +
    9. Ensure that your measurement is for a long enough duration (say 100 times the tick)
    10. +
    +









    +

    Problems with cold start

    + +

    What happens when the code is executed? The assumption is that the code is ready to +execute. But +

    +
      +
    1. Code may still be on disk, and not even read into memory.
    2. +
    3. Data may be in slow memory rather than fast (which may be wrong or right for what you are measuring)
    4. +
    5. Multiple tests often necessary to ensure that cold start effects are not present
    6. +
    7. Special effort often required to ensure data in the intended part of the memory hierarchy.
    8. +
    +









    +

    Problems with smart compilers

    + +
      +
    1. If the result of the computation is not used, the compiler may eliminate the code
    2. +
    3. Performance will look impossibly fantastic
    4. +
    5. Even worse, eliminate some of the code so the performance looks plausible
    6. +
    7. Ensure that the results are (or may be) used.
    8. +
    +









    +

    Problems with interference

    +
      +
    1. Other activities are sharing your processor
    2. +
        +
      • Operating system, system demons, other users
      • +
      • Some parts of the hardware do not always perform with exactly the same performance
      • +
      +
    3. Make multiple tests and report
    4. +
    5. Easy choices include
    6. +
        +
      • Average tests represent what users might observe over time
      • +
      +
    +









    +

    Problems with measuring performance

    +
      +
    1. Accurate, reproducible performance measurement is hard
    2. +
    3. Think carefully about your experiment:
    4. +
    5. What is it, precisely, that you want to measure?
    6. +
    7. How representative is your test to the situation that you are trying to measure?
    8. +
    +









    +

    Thomas algorithm for tridiagonal linear algebra equations

    +
    + +

    +$$ +\left( \begin{array}{ccccc} + b_0 & c_0 & & & \\ + a_0 & b_1 & c_1 & & \\ + & & \ddots & & \\ + & & a_{m-3} & b_{m-2} & c_{m-2} \\ + & & & a_{m-2} & b_{m-1} + \end{array} \right) +\left( \begin{array}{c} + x_0 \\ + x_1 \\ + \vdots \\ + x_{m-2} \\ + x_{m-1} + \end{array} \right)=\left( \begin{array}{c} + f_0 \\ + f_1 \\ + \vdots \\ + f_{m-2} \\ + f_{m-1} \\ + \end{array} \right) +$$ +

    + + +









    +

    Thomas algorithm, forward substitution

    +
    + +

    +

    The first step is to multiply the first row by \( a_0/b_0 \) and subtract it from the second row. This is known as the forward substitution step. We obtain then

    +$$ + a_i = 0, +$$ + + +$$ + b_i = b_i - \frac{a_{i-1}}{b_{i-1}}c_{i-1}, +$$ + +

    and

    +$$ + f_i = f_i - \frac{a_{i-1}}{b_{i-1}}f_{i-1}. +$$ + +

    At this point the simplified equation, with only an upper triangular matrix takes the form

    +$$ +\left( \begin{array}{ccccc} + b_0 & c_0 & & & \\ + & b_1 & c_1 & & \\ + & & \ddots & & \\ + & & & b_{m-2} & c_{m-2} \\ + & & & & b_{m-1} + \end{array} \right)\left( \begin{array}{c} + x_0 \\ + x_1 \\ + \vdots \\ + x_{m-2} \\ + x_{m-1} + \end{array} \right)=\left( \begin{array}{c} + f_0 \\ + f_1 \\ + \vdots \\ + f_{m-2} \\ + f_{m-1} \\ + \end{array} \right) +$$ +
    + + +









    +

    Thomas algorithm, backward substitution

    +
    + +

    +

    The next step is the backward substitution step. The last row is multiplied by \( c_{N-3}/b_{N-2} \) and subtracted from the second to last row, thus eliminating \( c_{N-3} \) from the last row. The general backward substitution procedure is

    +$$ + c_i = 0, +$$ + +

    and

    +$$ + f_{i-1} = f_{i-1} - \frac{c_{i-1}}{b_i}f_i +$$ + +

    All that ramains to be computed is the solution, which is the very straight forward process of

    +$$ +x_i = \frac{f_i}{b_i} +$$ +
    + + +









    +

    Thomas algorithm and counting of operations (floating point and memory)

    +
    + +

    + +

    We have in specific case the following operations with the floating operations

    + +
      +
    • Memory Reads: \( 14(N-2) \);
    • +
    • Memory Writes: \( 4(N-2) \);
    • +
    • Subtractions: \( 3(N-2) \);
    • +
    • Multiplications: \( 3(N-2) \);
    • +
    • Divisions: \( 4(N-2) \).
    • +
    +
    + + +
    + +

    + + +

    +
    +
    +
    +
    +
    // Forward substitution    
    +// Note that we can simplify by precalculating a[i-1]/b[i-1]
    +  for (int i=1; i < n; i++) {
    +     b[i] = b[i] - (a[i-1]*c[i-1])/b[i-1];
    +     f[i] = g[i] - (a[i-1]*f[i-1])/b[i-1];
    +  }
    +  x[n-1] = f[n-1] / b[n-1];
    +  // Backwards substitution                                                           
    +  for (int i = n-2; i >= 0; i--) {
    +     f[i] = f[i] - c[i]*f[i+1]/b[i+1];
    +     x[i] = f[i]/b[i];
    +  }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Example: Transpose of a matrix

    + + + +
    +
    +
    +
    +
    +
    #include <cstdlib>
    +#include <iostream>
    +#include <cmath>
    +#include <iomanip>
    +#include "time.h"
    +
    +using namespace std; // note use of namespace
    +int main (int argc, char* argv[])
    +{
    +  // read in dimension of square matrix
    +  int n = atoi(argv[1]);
    +  double **A, **B;
    +  // Allocate space for the two matrices
    +  A = new double*[n]; B = new double*[n];
    +  for (int i = 0; i < n; i++){
    +    A[i] = new double[n];
    +    B[i] = new double[n];
    +  }
    +  // Set up values for matrix A
    +  for (int i = 0; i < n; i++){
    +    for (int j = 0; j < n; j++) {
    +      A[i][j] =  cos(i*1.0)*sin(j*3.0);
    +    }
    +  }
    +  clock_t start, finish;
    +  start = clock();
    +  // Then compute the transpose
    +  for (int i = 0; i < n; i++){
    +    for (int j = 0; j < n; j++) {
    +      B[i][j]= A[j][i];
    +    }
    +  }
    +
    +  finish = clock();
    +  double timeused = (double) (finish - start)/(CLOCKS_PER_SEC );
    +  cout << setiosflags(ios::showpoint | ios::uppercase);
    +  cout << setprecision(10) << setw(20) << "Time used  for setting up transpose of matrix=" << timeused  << endl;
    +
    +  // Free up space
    +  for (int i = 0; i < n; i++){
    +    delete[] A[i];
    +    delete[] B[i];
    +  }
    +  delete[] A;
    +  delete[] B;
    +  return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Matrix-matrix multiplication

    +

    This the matrix-matrix multiplication code with plain c++ memory allocation. It computes at the end the Frobenius norm.

    + + + +
    +
    +
    +
    +
    +
    #include <cstdlib>
    +#include <iostream>
    +#include <cmath>
    +#include <iomanip>
    +#include "time.h"
    +
    +using namespace std; // note use of namespace
    +int main (int argc, char* argv[])
    +{
    +  // read in dimension of square matrix
    +  int n = atoi(argv[1]);
    +  double s = 1.0/sqrt( (double) n);
    +  double **A, **B, **C;
    +  // Start timing
    +  clock_t start, finish;
    +  start = clock();
    +  // Allocate space for the two matrices
    +  A = new double*[n]; B = new double*[n]; C = new double*[n];
    +  for (int i = 0; i < n; i++){
    +    A[i] = new double[n];
    +    B[i] = new double[n];
    +    C[i] = new double[n];
    +  }
    +  // Set up values for matrix A and B and zero matrix C
    +  for (int i = 0; i < n; i++){
    +    for (int j = 0; j < n; j++) {
    +      double angle = 2.0*M_PI*i*j/ (( double ) n);
    +      A[i][j] = s * ( sin ( angle ) + cos ( angle ) );
    +      B[j][i] =  A[i][j];
    +    }
    +  }
    +  // Then perform the matrix-matrix multiplication
    +  for (int i = 0; i < n; i++){
    +    for (int j = 0; j < n; j++) {
    +      double sum = 0.0;
    +       for (int k = 0; k < n; k++) {
    +           sum += B[i][k]*A[k][j];
    +       }
    +       C[i][j] = sum;
    +    }
    +  }
    +  // Compute now the Frobenius norm
    +  double Fsum = 0.0;
    +  for (int i = 0; i < n; i++){
    +    for (int j = 0; j < n; j++) {
    +      Fsum += C[i][j]*C[i][j];
    +    }
    +  }
    +  Fsum = sqrt(Fsum);
    +  finish = clock();
    +  double timeused = (double) (finish - start)/(CLOCKS_PER_SEC );
    +  cout << setiosflags(ios::showpoint | ios::uppercase);
    +  cout << setprecision(10) << setw(20) << "Time used  for matrix-matrix multiplication=" << timeused  << endl;
    +  cout << "  Frobenius norm  = " << Fsum << endl;
    +  // Free up space
    +  for (int i = 0; i < n; i++){
    +    delete[] A[i];
    +    delete[] B[i];
    +    delete[] C[i];
    +  }
    +  delete[] A;
    +  delete[] B;
    +  delete[] C;
    +  return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    How do we define speedup? Simplest form

    +
    + +

    +

      +
    • Speedup measures the ratio of performance between two objects
    • +
    • Versions of same code, with different number of processors
    • +
    • Serial and vector versions
    • +
    • Try different programing languages, c++ and Fortran
    • +
    • Two algorithms computing the same result
    • +
    +
    + + +









    +

    How do we define speedup? Correct baseline

    +
    + +

    +

    The key is choosing the correct baseline for comparison

    +
      +
    • For our serial vs. vectorization examples, using compiler-provided vectorization, the baseline is simple; the same code, with vectorization turned off
    • +
        +
      • For parallel applications, this is much harder:
      • +
          +
        • Choice of algorithm, decomposition, performance of baseline case etc.
        • +
        +
      +
    +
    + + +









    +

    Parallel speedup

    +
    + +

    +

    For parallel applications, speedup is typically defined as

    +
      +
    • Speedup \( =T_1/T_p \)
    • +
    +

    Here \( T_1 \) is the time on one processor and \( T_p \) is the time using \( p \) processors.

    +
      +
    • Can the speedup become larger than \( p \)? That means using \( p \) processors is more than \( p \) times faster than using one processor.
    • +
    +
    + + +









    +

    Speedup and memory

    +
    + +

    +

    The speedup on \( p \) processors can +be greater than \( p \) if memory usage is optimal! +Consider the case of a memorybound computation with \( M \) words of memory +

    +
      +
    • If \( M/p \) fits into cache while \( M \) does not, the time to access memory will be different in the two cases:
    • +
    • \( T_1 \) uses the main memory bandwidth
    • +
    • \( T_p \) uses the appropriate cache bandwidth
    • +
    +
    + + +









    +

    Upper bounds on speedup

    +
    + +

    +

    Assume that almost all parts of a code are perfectly +parallelizable (fraction \( f \)). The remainder, +fraction \( (1-f) \) cannot be parallelized at all. +

    + +

    That is, there is work that takes time \( W \) on one process; a fraction \( f \) of that work will take +time \( Wf/p \) on \( p \) processors. +

    +
      +
    • What is the maximum possible speedup as a function of \( f \)?
    • +
    +
    + + +









    +

    Amdahl's law

    +
    + +

    +

    On one processor we have

    +$$ +T_1 = (1-f)W + fW = W +$$ + +

    On \( p \) processors we have

    +$$ +T_p = (1-f)W + \frac{fW}{p}, +$$ + +

    resulting in a speedup of

    +$$ +\frac{T_1}{T_p} = \frac{W}{(1-f)W+fW/p} +$$ + +

    As \( p \) goes to infinity, \( fW/p \) goes to zero, and the maximum speedup is

    +$$ +\frac{1}{1-f}, +$$ + +

    meaning that if +if \( f = 0.99 \) (all but \( 1\% \) parallelizable), the maximum speedup +is \( 1/(1-.99)=100 \)! +

    +
    + + +









    +

    How much is parallelizable

    +
    + +

    +

    If any non-parallel code slips into the +application, the parallel +performance is limited. +

    + +

    In many simulations, however, the fraction of non-parallelizable work +is \( 10^{-6} \) or less due to large arrays or objects that are perfectly parallelizable. +

    +
    + + +









    +

    Today's situation of parallel computing

    +
    + +

    + +

      +
    • Distributed memory is the dominant hardware configuration. There is a large diversity in these machines, from MPP (massively parallel processing) systems to clusters of off-the-shelf PCs, which are very cost-effective.
    • +
    • Message-passing is a mature programming paradigm and widely accepted. It often provides an efficient match to the hardware. It is primarily used for the distributed memory systems, but can also be used on shared memory systems.
    • +
    • Modern nodes have nowadays several cores, which makes it interesting to use both shared memory (the given node) and distributed memory (several nodes with communication). This leads often to codes which use both MPI and OpenMP.
    • +
    +

    Our lectures will focus on both MPI and OpenMP.

    +
    + + +









    +

    Overhead present in parallel computing

    +
    + +

    + +

      +
    • Uneven load balance: not all the processors can perform useful work at all time.
    • +
    • Overhead of synchronization
    • +
    • Overhead of communication
    • +
    • Extra computation due to parallelization
    • +
    +

    Due to the above overhead and that certain parts of a sequential +algorithm cannot be parallelized we may not achieve an optimal parallelization. +

    +
    + + +









    +

    Parallelizing a sequential algorithm

    +
    + +

    + +

      +
    • Identify the part(s) of a sequential algorithm that can be executed in parallel. This is the difficult part,
    • +
    • Distribute the global work and data among \( P \) processors.
    • +
    +
    + + +









    +

    Strategies

    +
    + +

    +

      +
    • Develop codes locally, run with some few processes and test your codes. Do benchmarking, timing and so forth on local nodes, for example your laptop or PC.
    • +
    • When you are convinced that your codes run correctly, you can start your production runs on available supercomputers.
    • +
    +
    + + +









    +

    How do I run MPI on a PC/Laptop? MPI

    +
    + +

    +

    To install MPI is rather easy on hardware running unix/linux as operating systems, follow simply the instructions from the OpenMPI website. See also subsequent slides. +When you have made sure you have installed MPI on your PC/laptop, +

    +
      +
    • Compile with mpicxx/mpic++ or mpif90
    • +
    + + +
    +
    +
    +
    +
    +
      # Compile and link
    +  mpic++ -O3 -o nameofprog.x nameofprog.cpp
    +  #  run code with for example 8 processes using mpirun/mpiexec
    +  mpiexec -n 8 ./nameofprog.x
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Can I do it on my own PC/laptop? OpenMP installation

    +
    + +

    +

    If you wish to install MPI and OpenMP +on your laptop/PC, we recommend the following: +

    + +
      +
    • For OpenMP, the compile option -fopenmp is included automatically in recent versions of the C++ compiler and Fortran compilers. For users of different Linux distributions, simply use the available C++ or Fortran compilers and add the above compiler instructions, see also code examples below.
    • +
    • For OS X users however, install libomp
    • +
    + + +
    +
    +
    +
    +
    +
      brew install libomp
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    and compile and link as

    + + +
    +
    +
    +
    +
    +
    c++ -o <name executable> <name program.cpp>  -lomp
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Installing MPI

    +
    + +

    +

    For linux/ubuntu users, you need to install two packages (alternatively use the synaptic package manager)

    + + +
    +
    +
    +
    +
    +
      sudo apt-get install libopenmpi-dev
    +  sudo apt-get install openmpi-bin
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    For OS X users, install brew (after having installed xcode and gcc, needed for the +gfortran compiler of openmpi) and then install with brew +

    + + +
    +
    +
    +
    +
    +
       brew install openmpi
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    When running an executable (code.x), run as

    + + +
    +
    +
    +
    +
    +
      mpirun -n 10 ./code.x
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    where we indicate that we want the number of processes to be 10.

    +
    + + +









    +

    Installing MPI and using Qt

    +
    + +

    +

    With openmpi installed, when using Qt, add to your .pro file the instructions here

    + +

    You may need to tell Qt where openmpi is stored.

    +
    + + +









    +

    What is Message Passing Interface (MPI)?

    +
    + +

    + +

    MPI is a library, not a language. It specifies the names, calling sequences and results of functions +or subroutines to be called from C/C++ or Fortran programs, and the classes and methods that make up the MPI C++ +library. The programs that users write in Fortran, C or C++ are compiled with ordinary compilers and linked +with the MPI library. +

    + +

    MPI programs should be able to run +on all possible machines and run all MPI implementetations without change. +

    + +

    An MPI computation is a collection of processes communicating with messages.

    +
    + +









    +

    Going Parallel with MPI

    +
    + +

    +

    Task parallelism: the work of a global problem can be divided +into a number of independent tasks, which rarely need to synchronize. +Monte Carlo simulations or numerical integration are examples of this. +

    + +

    MPI is a message-passing library where all the routines +have corresponding C/C++-binding +

    + + +
    +
    +
    +
    +
    +
       MPI_Command_name
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    and Fortran-binding (routine names are in uppercase, but can also be in lower case)

    + + +
    +
    +
    +
    +
    +
       MPI_COMMAND_NAME
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    MPI is a library

    +
    + +

    +

    MPI is a library specification for the message passing interface, +proposed as a standard. +

    + +
      +
    • independent of hardware;
    • +
    • not a language or compiler specification;
    • +
    • not a specific implementation or product.
    • +
    +

    A message passing standard for portability and ease-of-use. +Designed for high performance. +

    + +

    Insert communication and synchronization functions where necessary.

    +
    + + +









    +

    Bindings to MPI routines

    +
    + +

    + +

    MPI is a message-passing library where all the routines +have corresponding C/C++-binding +

    + + +
    +
    +
    +
    +
    +
       MPI_Command_name
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    and Fortran-binding (routine names are in uppercase, but can also be in lower case)

    + + +
    +
    +
    +
    +
    +
       MPI_COMMAND_NAME
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The discussion in these slides focuses on the C++ binding.

    +
    + + +









    +

    Communicator

    +
    + +

    +

      +
    • A group of MPI processes with a name (context).
    • +
    • Any process is identified by its rank. The rank is only meaningful within a particular communicator.
    • +
    • By default the communicator contains all the MPI processes.
    • +
    + + +
    +
    +
    +
    +
    +
      MPI_COMM_WORLD 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +
    • Mechanism to identify subset of processes.
    • +
    • Promotes modular design of parallel libraries.
    • +
    +
    + + +









    +

    Some of the most important MPI functions

    +
    + +

    + +

      +
    • \( MPI\_Init \) - initiate an MPI computation
    • +
    • \( MPI\_Finalize \) - terminate the MPI computation and clean up
    • +
    • \( MPI\_Comm\_size \) - how many processes participate in a given MPI communicator?
    • +
    • \( MPI\_Comm\_rank \) - which one am I? (A number between 0 and size-1.)
    • +
    • \( MPI\_Send \) - send a message to a particular process within an MPI communicator
    • +
    • \( MPI\_Recv \) - receive a message from a particular process within an MPI communicator
    • +
    • \( MPI\_reduce \) or \( MPI\_Allreduce \), send and receive messages
    • +
    +
    + + +









    +

    The first MPI C/C++ program

    +
    + +

    + +

    Let every process write "Hello world" (oh not this program again!!) on the standard output.

    + + +
    +
    +
    +
    +
    +
    using namespace std;
    +#include <mpi.h>
    +#include <iostream>
    +int main (int nargs, char* args[])
    +{
    +int numprocs, my_rank;
    +//   MPI initializations
    +MPI_Init (&nargs, &args);
    +MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
    +MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);
    +cout << "Hello world, I have  rank " << my_rank << " out of " 
    +     << numprocs << endl;
    +//  End MPI
    +MPI_Finalize ();
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    The Fortran program

    +
    + +

    + + +

    +
    +
    +
    +
    +
    PROGRAM hello
    +INCLUDE "mpif.h"
    +INTEGER:: size, my_rank, ierr
    +
    +CALL  MPI_INIT(ierr)
    +CALL MPI_COMM_SIZE(MPI_COMM_WORLD, size, ierr)
    +CALL MPI_COMM_RANK(MPI_COMM_WORLD, my_rank, ierr)
    +WRITE(*,*)"Hello world, I've rank ",my_rank," out of ",size
    +CALL MPI_FINALIZE(ierr)
    +
    +END PROGRAM hello
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Note 1

    +
    + +

    + +

      +
    • The output to screen is not ordered since all processes are trying to write to screen simultaneously.
    • +
    • It is the operating system which opts for an ordering.
    • +
    • If we wish to have an organized output, starting from the first process, we may rewrite our program as in the next example.
    • +
    +
    + + +









    +

    Ordered output with MPIBarrier

    +
    + +

    + + + +

    +
    +
    +
    +
    +
    int main (int nargs, char* args[])
    +{
    + int numprocs, my_rank, i;
    + MPI_Init (&nargs, &args);
    + MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
    + MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);
    + for (i = 0; i < numprocs; i++) {}
    + MPI_Barrier (MPI_COMM_WORLD);
    + if (i == my_rank) {
    + cout << "Hello world, I have  rank " << my_rank << 
    +        " out of " << numprocs << endl;}
    +      MPI_Finalize ();
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Note 2

    +
    + +

    +

      +
    • Here we have used the \( MPI\_Barrier \) function to ensure that that every process has completed its set of instructions in a particular order.
    • +
    • A barrier is a special collective operation that does not allow the processes to continue until all processes in the communicator (here \( MPI\_COMM\_WORLD \)) have called \( MPI\_Barrier \).
    • +
    • The barriers make sure that all processes have reached the same point in the code. Many of the collective operations like \( MPI\_ALLREDUCE \) to be discussed later, have the same property; that is, no process can exit the operation until all processes have started.
    • +
    +

    However, this is slightly more time-consuming since the processes synchronize between themselves as many times as there +are processes. In the next Hello world example we use the send and receive functions in order to a have a synchronized +action. +

    +
    + + +









    +

    Ordered output

    +
    + +

    + + + +

    +
    +
    +
    +
    +
    .....
    +int numprocs, my_rank, flag;
    +MPI_Status status;
    +MPI_Init (&nargs, &args);
    +MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
    +MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);
    +if (my_rank > 0)
    +MPI_Recv (&flag, 1, MPI_INT, my_rank-1, 100, 
    +           MPI_COMM_WORLD, &status);
    +cout << "Hello world, I have  rank " << my_rank << " out of " 
    +<< numprocs << endl;
    +if (my_rank < numprocs-1)
    +MPI_Send (&my_rank, 1, MPI_INT, my_rank+1, 
    +          100, MPI_COMM_WORLD);
    +MPI_Finalize ();
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Note 3

    +
    + +

    + +

    The basic sending of messages is given by the function \( MPI\_SEND \), which in C/C++ +is defined as +

    + + +
    +
    +
    +
    +
    +
    int MPI_Send(void *buf, int count, 
    +             MPI_Datatype datatype, 
    +             int dest, int tag, MPI_Comm comm)}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    This single command allows the passing of any kind of variable, even a large array, to any group of tasks. +The variable buf is the variable we wish to send while count +is the number of variables we are passing. If we are passing only a single value, this should be 1. +

    + +

    If we transfer an array, it is the overall size of the array. +For example, if we want to send a 10 by 10 array, count would be \( 10\times 10=100 \) +since we are actually passing 100 values. +

    +
    + + +









    +

    Note 4

    +
    + +

    + +

    Once you have sent a message, you must receive it on another task. The function \( MPI\_RECV \) +is similar to the send call. +

    + + +
    +
    +
    +
    +
    +
    int MPI_Recv( void *buf, int count, MPI_Datatype datatype, 
    +            int source, 
    +            int tag, MPI_Comm comm, MPI_Status *status )
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The arguments that are different from those in MPI\_SEND are +buf which is the name of the variable where you will be storing the received data, +source which replaces the destination in the send command. This is the return ID of the sender. +

    + +

    Finally, we have used \( MPI\_Status\_status \), +where one can check if the receive was completed. +

    + +

    The output of this code is the same as the previous example, but now +process 0 sends a message to process 1, which forwards it further +to process 2, and so forth. +

    +
    + + +









    +

    Numerical integration in parallel

    +
    +Integrating \( \pi \) +

    + +

      +
    • The code example computes \( \pi \) using the trapezoidal rules.
    • +
    • The trapezoidal rule
    • +
    +$$ + I=\int_a^bf(x) dx\approx h\left(f(a)/2 + f(a+h) +f(a+2h)+\dots +f(b-h)+ f(b)/2\right). +$$ + +

    Click on this link for the full program.

    +
    + + +









    +

    Dissection of trapezoidal rule with \( MPI\_reduce \)

    +
    + +

    + + + +

    +
    +
    +
    +
    +
    //    Trapezoidal rule and numerical integration usign MPI
    +using namespace std;
    +#include <mpi.h>
    +#include <iostream>
    +
    +//     Here we define various functions called by the main program
    +
    +double int_function(double );
    +double trapezoidal_rule(double , double , int , double (*)(double));
    +
    +//   Main function begins here
    +int main (int nargs, char* args[])
    +{
    +  int n, local_n, numprocs, my_rank; 
    +  double a, b, h, local_a, local_b, total_sum, local_sum;   
    +  double  time_start, time_end, total_time;
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Dissection of trapezoidal rule

    +
    + +

    + + + +

    +
    +
    +
    +
    +
      //  MPI initializations
    +  MPI_Init (&nargs, &args);
    +  MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
    +  MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);
    +  time_start = MPI_Wtime();
    +  //  Fixed values for a, b and n 
    +  a = 0.0 ; b = 1.0;  n = 1000;
    +  h = (b-a)/n;    // h is the same for all processes 
    +  local_n = n/numprocs;  
    +  // make sure n > numprocs, else integer division gives zero
    +  // Length of each process' interval of
    +  // integration = local_n*h.  
    +  local_a = a + my_rank*local_n*h;
    +  local_b = local_a + local_n*h;
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Integrating with MPI

    +
    + +

    + + + +

    +
    +
    +
    +
    +
      total_sum = 0.0;
    +  local_sum = trapezoidal_rule(local_a, local_b, local_n, 
    +                               &int_function); 
    +  MPI_Reduce(&local_sum, &total_sum, 1, MPI_DOUBLE, 
    +              MPI_SUM, 0, MPI_COMM_WORLD);
    +  time_end = MPI_Wtime();
    +  total_time = time_end-time_start;
    +  if ( my_rank == 0) {
    +    cout << "Trapezoidal rule = " <<  total_sum << endl;
    +    cout << "Time = " <<  total_time  
    +         << " on number of processors: "  << numprocs  << endl;
    +  }
    +  // End MPI
    +  MPI_Finalize ();  
    +  return 0;
    +}  // end of main program
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    How do I use \( MPI\_reduce \)?

    +
    + +

    + +

    Here we have used

    + + +
    +
    +
    +
    +
    +
    MPI_reduce( void *senddata, void* resultdata, int count, 
    +     MPI_Datatype datatype, MPI_Op, int root, MPI_Comm comm)
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The two variables \( senddata \) and \( resultdata \) are obvious, besides the fact that one sends the address +of the variable or the first element of an array. If they are arrays they need to have the same size. +The variable \( count \) represents the total dimensionality, 1 in case of just one variable, +while \( MPI\_Datatype \) +defines the type of variable which is sent and received. +

    + +

    The new feature is \( MPI\_Op \). It defines the type +of operation we want to do. +

    +
    + + +









    +

    More on \( MPI\_Reduce \)

    +
    + +

    +

    In our case, since we are summing +the rectangle contributions from every process we define \( MPI\_Op = MPI\_SUM \). +If we have an array or matrix we can search for the largest og smallest element by sending either \( MPI\_MAX \) or +\( MPI\_MIN \). If we want the location as well (which array element) we simply transfer +\( MPI\_MAXLOC \) or \( MPI\_MINOC \). If we want the product we write \( MPI\_PROD \). +

    + +

    \( MPI\_Allreduce \) is defined as

    + + +
    +
    +
    +
    +
    +
    MPI_Allreduce( void *senddata, void* resultdata, int count, 
    +          MPI_Datatype datatype, MPI_Op, MPI_Comm comm)        
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Dissection of trapezoidal rule

    +
    + +

    + +

    We use \( MPI\_reduce \) to collect data from each process. Note also the use of the function +\( MPI\_Wtime \). +

    + + +
    +
    +
    +
    +
    +
    //  this function defines the function to integrate
    +double int_function(double x)
    +{
    +  double value = 4./(1.+x*x);
    +  return value;
    +} // end of function to evaluate
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Dissection of trapezoidal rule

    +
    + +

    + + +

    +
    +
    +
    +
    +
    //  this function defines the trapezoidal rule
    +double trapezoidal_rule(double a, double b, int n, 
    +                         double (*func)(double))
    +{
    +  double trapez_sum;
    +  double fa, fb, x, step;
    +  int    j;
    +  step=(b-a)/((double) n);
    +  fa=(*func)(a)/2. ;
    +  fb=(*func)(b)/2. ;
    +  trapez_sum=0.;
    +  for (j=1; j <= n-1; j++){
    +    x=j*step+a;
    +    trapez_sum+=(*func)(x);
    +  }
    +  trapez_sum=(trapez_sum+fb+fa)*step;
    +  return trapez_sum;
    +}  // end trapezoidal_rule 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    The quantum dot program for two electrons

    +
    + +

    + + +

    +
    +
    +
    +
    +
    // Variational Monte Carlo for atoms with importance sampling, slater det
    +// Test case for 2-electron quantum dot, no classes using Mersenne-Twister RNG
    +#include "mpi.h"
    +#include <cmath>
    +#include <random>
    +#include <string>
    +#include <iostream>
    +#include <fstream>
    +#include <iomanip>
    +#include "vectormatrixclass.h"
    +
    +using namespace  std;
    +// output file as global variable
    +ofstream ofile;  
    +// the step length and its squared inverse for the second derivative 
    +//  Here we define global variables  used in various functions
    +//  These can be changed by using classes
    +int Dimension = 2; 
    +int NumberParticles  = 2;  //  we fix also the number of electrons to be 2
    +
    +// declaration of functions 
    +
    +// The Mc sampling for the variational Monte Carlo 
    +void  MonteCarloSampling(int, double &, double &, Vector &);
    +
    +// The variational wave function
    +double  WaveFunction(Matrix &, Vector &);
    +
    +// The local energy 
    +double  LocalEnergy(Matrix &, Vector &);
    +
    +// The quantum force
    +void  QuantumForce(Matrix &, Matrix &, Vector &);
    +
    +
    +// inline function for single-particle wave function
    +inline double SPwavefunction(double r, double alpha) { 
    +   return exp(-alpha*r*0.5);
    +}
    +
    +// inline function for derivative of single-particle wave function
    +inline double DerivativeSPwavefunction(double r, double alpha) { 
    +  return -r*alpha;
    +}
    +
    +// function for absolute value of relative distance
    +double RelativeDistance(Matrix &r, int i, int j) { 
    +      double r_ij = 0;  
    +      for (int k = 0; k < Dimension; k++) { 
    +	r_ij += (r(i,k)-r(j,k))*(r(i,k)-r(j,k));
    +      }
    +      return sqrt(r_ij); 
    +}
    +
    +// inline function for derivative of Jastrow factor
    +inline double JastrowDerivative(Matrix &r, double beta, int i, int j, int k){
    +  return (r(i,k)-r(j,k))/(RelativeDistance(r, i, j)*pow(1.0+beta*RelativeDistance(r, i, j),2));
    +}
    +
    +// function for square of position of single particle
    +double singleparticle_pos2(Matrix &r, int i) { 
    +    double r_single_particle = 0;
    +    for (int j = 0; j < Dimension; j++) { 
    +      r_single_particle  += r(i,j)*r(i,j);
    +    }
    +    return r_single_particle;
    +}
    +
    +void lnsrch(int n, Vector &xold, double fold, Vector &g, Vector &p, Vector &x,
    +		 double *f, double stpmax, int *check, double (*func)(Vector &p));
    +
    +void dfpmin(Vector &p, int n, double gtol, int *iter, double *fret,
    +	    double(*func)(Vector &p), void (*dfunc)(Vector &p, Vector &g));
    +
    +static double sqrarg;
    +#define SQR(a) ((sqrarg=(a)) == 0.0 ? 0.0 : sqrarg*sqrarg)
    +
    +
    +static double maxarg1,maxarg2;
    +#define FMAX(a,b) (maxarg1=(a),maxarg2=(b),(maxarg1) > (maxarg2) ?\
    +        (maxarg1) : (maxarg2))
    +
    +
    +// Begin of main program   
    +
    +int main(int argc, char* argv[])
    +{
    +
    +  //  MPI initializations
    +  int NumberProcesses, MyRank, NumberMCsamples;
    +  MPI_Init (&argc, &argv);
    +  MPI_Comm_size (MPI_COMM_WORLD, &NumberProcesses);
    +  MPI_Comm_rank (MPI_COMM_WORLD, &MyRank);
    +  double StartTime = MPI_Wtime();
    +  if (MyRank == 0 && argc <= 1) {
    +    cout << "Bad Usage: " << argv[0] << 
    +      " Read also output file on same line and number of Monte Carlo cycles" << endl;
    +  }
    +  // Read filename and number of Monte Carlo cycles from the command line
    +  if (MyRank == 0 && argc > 2) {
    +    string filename = argv[1]; // first command line argument after name of program
    +    NumberMCsamples  = atoi(argv[2]);
    +    string fileout = filename;
    +    string argument = to_string(NumberMCsamples);
    +    // Final filename as filename+NumberMCsamples
    +    fileout.append(argument);
    +    ofile.open(fileout);
    +  }
    +  // broadcast the number of  Monte Carlo samples
    +  MPI_Bcast (&NumberMCsamples, 1, MPI_INT, 0, MPI_COMM_WORLD);
    +  // Two variational parameters only
    +  Vector VariationalParameters(2);
    +  int TotalNumberMCsamples = NumberMCsamples*NumberProcesses; 
    +  // Loop over variational parameters
    +  for (double alpha = 0.5; alpha <= 1.5; alpha +=0.1){
    +    for (double beta = 0.1; beta <= 0.5; beta +=0.05){
    +      VariationalParameters(0) = alpha;  // value of alpha
    +      VariationalParameters(1) = beta;  // value of beta
    +      //  Do the mc sampling  and accumulate data with MPI_Reduce
    +      double TotalEnergy, TotalEnergySquared, LocalProcessEnergy, LocalProcessEnergy2;
    +      LocalProcessEnergy = LocalProcessEnergy2 = 0.0;
    +      MonteCarloSampling(NumberMCsamples, LocalProcessEnergy, LocalProcessEnergy2, VariationalParameters);
    +      //  Collect data in total averages
    +      MPI_Reduce(&LocalProcessEnergy, &TotalEnergy, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
    +      MPI_Reduce(&LocalProcessEnergy2, &TotalEnergySquared, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
    +      // Print out results  in case of Master node, set to MyRank = 0
    +      if ( MyRank == 0) {
    +	double Energy = TotalEnergy/( (double)NumberProcesses);
    +	double Variance = TotalEnergySquared/( (double)NumberProcesses)-Energy*Energy;
    +	double StandardDeviation = sqrt(Variance/((double)TotalNumberMCsamples)); // over optimistic error
    +	ofile << setiosflags(ios::showpoint | ios::uppercase);
    +	ofile << setw(15) << setprecision(8) << VariationalParameters(0);
    +	ofile << setw(15) << setprecision(8) << VariationalParameters(1);
    +	ofile << setw(15) << setprecision(8) << Energy;
    +	ofile << setw(15) << setprecision(8) << Variance;
    +	ofile << setw(15) << setprecision(8) << StandardDeviation << endl;
    +      }
    +    }
    +  }
    +  double EndTime = MPI_Wtime();
    +  double TotalTime = EndTime-StartTime;
    +  if ( MyRank == 0 )  cout << "Time = " <<  TotalTime  << " on number of processors: "  << NumberProcesses  << endl;
    +  if (MyRank == 0)  ofile.close();  // close output file
    +  // End MPI
    +  MPI_Finalize ();  
    +  return 0;
    +}  //  end of main function
    +
    +
    +// Monte Carlo sampling with the Metropolis algorithm  
    +
    +void MonteCarloSampling(int NumberMCsamples, double &cumulative_e, double &cumulative_e2, Vector &VariationalParameters)
    +{
    +
    + // Initialize the seed and call the Mersienne algo
    +  std::random_device rd;
    +  std::mt19937_64 gen(rd());
    +  // Set up the uniform distribution for x \in [[0, 1]
    +  std::uniform_real_distribution<double> UniformNumberGenerator(0.0,1.0);
    +  std::normal_distribution<double> Normaldistribution(0.0,1.0);
    +  // diffusion constant from Schroedinger equation
    +  double D = 0.5; 
    +  double timestep = 0.05;  //  we fix the time step  for the gaussian deviate
    +  // allocate matrices which contain the position of the particles  
    +  Matrix OldPosition( NumberParticles, Dimension), NewPosition( NumberParticles, Dimension);
    +  Matrix OldQuantumForce(NumberParticles, Dimension), NewQuantumForce(NumberParticles, Dimension);
    +  double Energy = 0.0; double EnergySquared = 0.0; double DeltaE = 0.0;
    +  //  initial trial positions
    +  for (int i = 0; i < NumberParticles; i++) { 
    +    for (int j = 0; j < Dimension; j++) {
    +      OldPosition(i,j) = Normaldistribution(gen)*sqrt(timestep);
    +    }
    +  }
    +  double OldWaveFunction = WaveFunction(OldPosition, VariationalParameters);
    +  QuantumForce(OldPosition, OldQuantumForce, VariationalParameters);
    +  // loop over monte carlo cycles 
    +  for (int cycles = 1; cycles <= NumberMCsamples; cycles++){ 
    +    // new position 
    +    for (int i = 0; i < NumberParticles; i++) { 
    +      for (int j = 0; j < Dimension; j++) {
    +	// gaussian deviate to compute new positions using a given timestep
    +	NewPosition(i,j) = OldPosition(i,j) + Normaldistribution(gen)*sqrt(timestep)+OldQuantumForce(i,j)*timestep*D;
    +	//	NewPosition(i,j) = OldPosition(i,j) + gaussian_deviate(&idum)*sqrt(timestep)+OldQuantumForce(i,j)*timestep*D;
    +      }  
    +      //  for the other particles we need to set the position to the old position since
    +      //  we move only one particle at the time
    +      for (int k = 0; k < NumberParticles; k++) {
    +	if ( k != i) {
    +	  for (int j = 0; j < Dimension; j++) {
    +	    NewPosition(k,j) = OldPosition(k,j);
    +	  }
    +	} 
    +      }
    +      double NewWaveFunction = WaveFunction(NewPosition, VariationalParameters); 
    +      QuantumForce(NewPosition, NewQuantumForce, VariationalParameters);
    +      //  we compute the log of the ratio of the greens functions to be used in the 
    +      //  Metropolis-Hastings algorithm
    +      double GreensFunction = 0.0;            
    +      for (int j = 0; j < Dimension; j++) {
    +	GreensFunction += 0.5*(OldQuantumForce(i,j)+NewQuantumForce(i,j))*
    +	  (D*timestep*0.5*(OldQuantumForce(i,j)-NewQuantumForce(i,j))-NewPosition(i,j)+OldPosition(i,j));
    +      }
    +      GreensFunction = exp(GreensFunction);
    +      // The Metropolis test is performed by moving one particle at the time
    +      if(UniformNumberGenerator(gen) <= GreensFunction*NewWaveFunction*NewWaveFunction/OldWaveFunction/OldWaveFunction ) { 
    +	for (int  j = 0; j < Dimension; j++) {
    +	  OldPosition(i,j) = NewPosition(i,j);
    +	  OldQuantumForce(i,j) = NewQuantumForce(i,j);
    +	}
    +	OldWaveFunction = NewWaveFunction;
    +      }
    +    }  //  end of loop over particles
    +    // compute local energy  
    +    double DeltaE = LocalEnergy(OldPosition, VariationalParameters);
    +    // update energies
    +    Energy += DeltaE;
    +    EnergySquared += DeltaE*DeltaE;
    +  }   // end of loop over MC trials   
    +  // update the energy average and its squared 
    +  cumulative_e = Energy/NumberMCsamples;
    +  cumulative_e2 = EnergySquared/NumberMCsamples;
    +}   // end MonteCarloSampling function  
    +
    +
    +// Function to compute the squared wave function and the quantum force
    +
    +double  WaveFunction(Matrix &r, Vector &VariationalParameters)
    +{
    +  double wf = 0.0;
    +  // full Slater determinant for two particles, replace with Slater det for more particles 
    +  wf  = SPwavefunction(singleparticle_pos2(r, 0), VariationalParameters(0))*SPwavefunction(singleparticle_pos2(r, 1),VariationalParameters(0));
    +  // contribution from Jastrow factor
    +  for (int i = 0; i < NumberParticles-1; i++) { 
    +    for (int j = i+1; j < NumberParticles; j++) {
    +      wf *= exp(RelativeDistance(r, i, j)/((1.0+VariationalParameters(1)*RelativeDistance(r, i, j))));
    +    }
    +  }
    +  return wf;
    +}
    +
    +// Function to calculate the local energy without numerical derivation of kinetic energy
    +
    +double  LocalEnergy(Matrix &r, Vector &VariationalParameters)
    +{
    +
    +  // compute the kinetic and potential energy from the single-particle part
    +  // for a many-electron system this has to be replaced by a Slater determinant
    +  // The absolute value of the interparticle length
    +  Matrix length( NumberParticles, NumberParticles);
    +  // Set up interparticle distance
    +  for (int i = 0; i < NumberParticles-1; i++) { 
    +    for(int j = i+1; j < NumberParticles; j++){
    +      length(i,j) = RelativeDistance(r, i, j);
    +      length(j,i) =  length(i,j);
    +    }
    +  }
    +  double KineticEnergy = 0.0;
    +  // Set up kinetic energy from Slater and Jastrow terms
    +  for (int i = 0; i < NumberParticles; i++) { 
    +    for (int k = 0; k < Dimension; k++) {
    +      double sum1 = 0.0; 
    +      for(int j = 0; j < NumberParticles; j++){
    +	if ( j != i) {
    +	  sum1 += JastrowDerivative(r, VariationalParameters(1), i, j, k);
    +	}
    +      }
    +      KineticEnergy += (sum1+DerivativeSPwavefunction(r(i,k),VariationalParameters(0)))*(sum1+DerivativeSPwavefunction(r(i,k),VariationalParameters(0)));
    +    }
    +  }
    +  KineticEnergy += -2*VariationalParameters(0)*NumberParticles;
    +  for (int i = 0; i < NumberParticles-1; i++) {
    +      for (int j = i+1; j < NumberParticles; j++) {
    +        KineticEnergy += 2.0/(pow(1.0 + VariationalParameters(1)*length(i,j),2))*(1.0/length(i,j)-2*VariationalParameters(1)/(1+VariationalParameters(1)*length(i,j)) );
    +      }
    +  }
    +  KineticEnergy *= -0.5;
    +  // Set up potential energy, external potential + eventual electron-electron repulsion
    +  double PotentialEnergy = 0;
    +  for (int i = 0; i < NumberParticles; i++) { 
    +    double DistanceSquared = singleparticle_pos2(r, i);
    +    PotentialEnergy += 0.5*DistanceSquared;  // sp energy HO part, note it has the oscillator frequency set to 1!
    +  }
    +  // Add the electron-electron repulsion
    +  for (int i = 0; i < NumberParticles-1; i++) { 
    +    for (int j = i+1; j < NumberParticles; j++) {
    +      PotentialEnergy += 1.0/length(i,j);          
    +    }
    +  }
    +  double LocalE = KineticEnergy+PotentialEnergy;
    +  return LocalE;
    +}
    +
    +// Compute the analytical expression for the quantum force
    +void  QuantumForce(Matrix &r, Matrix &qforce, Vector &VariationalParameters)
    +{
    +  // compute the first derivative 
    +  for (int i = 0; i < NumberParticles; i++) {
    +    for (int k = 0; k < Dimension; k++) {
    +      // single-particle part, replace with Slater det for larger systems
    +      double sppart = DerivativeSPwavefunction(r(i,k),VariationalParameters(0));
    +      //  Jastrow factor contribution
    +      double Jsum = 0.0;
    +      for (int j = 0; j < NumberParticles; j++) {
    +	if ( j != i) {
    +	  Jsum += JastrowDerivative(r, VariationalParameters(1), i, j, k);
    +	}
    +      }
    +      qforce(i,k) = 2.0*(Jsum+sppart);
    +    }
    +  }
    +} // end of QuantumForce function
    +
    +
    +#define ITMAX 200
    +#define EPS 3.0e-8
    +#define TOLX (4*EPS)
    +#define STPMX 100.0
    +
    +void dfpmin(Vector &p, int n, double gtol, int *iter, double *fret,
    +	    double(*func)(Vector &p), void (*dfunc)(Vector &p, Vector &g))
    +{
    +
    +  int check,i,its,j;
    +  double den,fac,fad,fae,fp,stpmax,sum=0.0,sumdg,sumxi,temp,test;
    +  Vector dg(n), g(n), hdg(n), pnew(n), xi(n);
    +  Matrix hessian(n,n);
    +
    +  fp=(*func)(p);
    +  (*dfunc)(p,g);
    +  for (i = 0;i < n;i++) {
    +    for (j = 0; j< n;j++) hessian(i,j)=0.0;
    +    hessian(i,i)=1.0;
    +    xi(i) = -g(i);
    +    sum += p(i)*p(i);
    +  }
    +  stpmax=STPMX*FMAX(sqrt(sum),(double)n);
    +  for (its=1;its<=ITMAX;its++) {
    +    *iter=its;
    +    lnsrch(n,p,fp,g,xi,pnew,fret,stpmax,&check,func);
    +    fp = *fret;
    +    for (i = 0; i< n;i++) {
    +      xi(i)=pnew(i)-p(i);
    +      p(i)=pnew(i);
    +    }
    +    test=0.0;
    +    for (i = 0;i< n;i++) {
    +      temp=fabs(xi(i))/FMAX(fabs(p(i)),1.0);
    +      if (temp > test) test=temp;
    +    }
    +    if (test < TOLX) {
    +      return;
    +    }
    +    for (i=0;i<n;i++) dg(i)=g(i);
    +    (*dfunc)(p,g);
    +    test=0.0;
    +    den=FMAX(*fret,1.0);
    +    for (i=0;i<n;i++) {
    +      temp=fabs(g(i))*FMAX(fabs(p(i)),1.0)/den;
    +      if (temp > test) test=temp;
    +    }
    +    if (test < gtol) {
    +      return;
    +    }
    +    for (i=0;i<n;i++) dg(i)=g(i)-dg(i);
    +    for (i=0;i<n;i++) {
    +      hdg(i)=0.0;
    +      for (j=0;j<n;j++) hdg(i) += hessian(i,j)*dg(j);
    +    }
    +    fac=fae=sumdg=sumxi=0.0;
    +    for (i=0;i<n;i++) {
    +      fac += dg(i)*xi(i);
    +      fae += dg(i)*hdg(i);
    +      sumdg += SQR(dg(i));
    +      sumxi += SQR(xi(i));
    +    }
    +    if (fac*fac > EPS*sumdg*sumxi) {
    +      fac=1.0/fac;
    +      fad=1.0/fae;
    +      for (i=0;i<n;i++) dg(i)=fac*xi(i)-fad*hdg(i);
    +      for (i=0;i<n;i++) {
    +	for (j=0;j<n;j++) {
    +	  hessian(i,j) += fac*xi(i)*xi(j)
    +	    -fad*hdg(i)*hdg(j)+fae*dg(i)*dg(j);
    +	}
    +      }
    +    }
    +    for (i=0;i<n;i++) {
    +      xi(i)=0.0;
    +      for (j=0;j<n;j++) xi(i) -= hessian(i,j)*g(j);
    +    }
    +  }
    +  cout << "too many iterations in dfpmin" << endl;
    +}
    +#undef ITMAX
    +#undef EPS
    +#undef TOLX
    +#undef STPMX
    +
    +#define ALF 1.0e-4
    +#define TOLX 1.0e-7
    +
    +void lnsrch(int n, Vector &xold, double fold, Vector &g, Vector &p, Vector &x,
    +	    double *f, double stpmax, int *check, double (*func)(Vector &p))
    +{
    +  int i;
    +  double a,alam,alam2,alamin,b,disc,f2,fold2,rhs1,rhs2,slope,sum,temp,
    +    test,tmplam;
    +
    +  *check=0;
    +  for (sum=0.0,i=0;i<n;i++) sum += p(i)*p(i);
    +  sum=sqrt(sum);
    +  if (sum > stpmax)
    +    for (i=0;i<n;i++) p(i) *= stpmax/sum;
    +  for (slope=0.0,i=0;i<n;i++)
    +    slope += g(i)*p(i);
    +  test=0.0;
    +  for (i=0;i<n;i++) {
    +    temp=fabs(p(i))/FMAX(fabs(xold(i)),1.0);
    +    if (temp > test) test=temp;
    +  }
    +  alamin=TOLX/test;
    +  alam=1.0;
    +  for (;;) {
    +    for (i=0;i<n;i++) x(i)=xold(i)+alam*p(i);
    +    *f=(*func)(x);
    +    if (alam < alamin) {
    +      for (i=0;i<n;i++) x(i)=xold(i);
    +      *check=1;
    +      return;
    +    } else if (*f <= fold+ALF*alam*slope) return;
    +    else {
    +      if (alam == 1.0)
    +	tmplam = -slope/(2.0*(*f-fold-slope));
    +      else {
    +	rhs1 = *f-fold-alam*slope;
    +	rhs2=f2-fold2-alam2*slope;
    +	a=(rhs1/(alam*alam)-rhs2/(alam2*alam2))/(alam-alam2);
    +	b=(-alam2*rhs1/(alam*alam)+alam*rhs2/(alam2*alam2))/(alam-alam2);
    +	if (a == 0.0) tmplam = -slope/(2.0*b);
    +	else {
    +	  disc=b*b-3.0*a*slope;
    +	  if (disc<0.0) cout << "Roundoff problem in lnsrch." << endl;
    +	  else tmplam=(-b+sqrt(disc))/(3.0*a);
    +	}
    +	if (tmplam>0.5*alam)
    +	  tmplam=0.5*alam;
    +      }
    +    }
    +    alam2=alam;
    +    f2 = *f;
    +    fold2=fold;
    +    alam=FMAX(tmplam,0.1*alam);
    +  }
    +}
    +#undef ALF
    +#undef TOLX
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    What is OpenMP

    +
    + +

    +

      +
    • OpenMP provides high-level thread programming
    • +
    • Multiple cooperating threads are allowed to run simultaneously
    • +
    • Threads are created and destroyed dynamically in a fork-join pattern
    • +
        +
      • An OpenMP program consists of a number of parallel regions
      • +
      • Between two parallel regions there is only one master thread
      • +
      • In the beginning of a parallel region, a team of new threads is spawned
      • +
      +
    • The newly spawned threads work simultaneously with the master thread
    • +
    • At the end of a parallel region, the new threads are destroyed
    • +
    +

    Many good tutorials online and excellent textbook

    +
      +
    1. Using OpenMP, by B. Chapman, G. Jost, and A. van der Pas
    2. +
    3. Many tutorials online like OpenMP official site
    4. +
    +
    + + +









    +

    Getting started, things to remember

    +
    + +

    +

      +
    • Remember the header file
    • +
    + + +
    +
    +
    +
    +
    +
    #include <omp.h>
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +
    • Insert compiler directives in C++ syntax as
    • +
    + + +
    +
    +
    +
    +
    +
    #pragma omp...
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +
    • Compile with for example c++ -fopenmp code.cpp
    • +
    • Execute
    • +
        +
      • Remember to assign the environment variable OMP NUM THREADS
      • +
      • It specifies the total number of threads inside a parallel region, if not otherwise overwritten
      • +
      +
    +
    + + +









    +

    OpenMP syntax

    + + + +
    +
    +
    +
    +
    +
    #pragma omp construct [ clause ...]
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + +
    +
    +
    +
    +
    +
    #include <omp.h>
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Different OpenMP styles of parallelism

    +

    OpenMP supports several different ways to specify thread parallelism

    + + +









    +

    General code structure

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #include <omp.h>
    +main ()
    +{
    +int var1, var2, var3;
    +/* serial code */
    +/* ... */
    +/* start of a parallel region */
    +#pragma omp parallel private(var1, var2) shared(var3)
    +{
    +/* ... */
    +}
    +/* more serial code */
    +/* ... */
    +/* another parallel region */
    +#pragma omp parallel
    +{
    +/* ... */
    +}
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Parallel region

    +
    + +

    +

      +
    • A parallel region is a block of code that is executed by a team of threads
    • +
    • The following compiler directive creates a parallel region
    • +
    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel { ... }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +
    • Clauses can be added at the end of the directive
    • +
    • Most often used clauses:
    • +
        +
      • default(shared) or default(none)
      • +
      • public(list of variables)
      • +
      • private(list of variables)
      • +
      +
    +
    + + +









    +

    Hello world, not again, please!

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #include <omp.h>
    +#include <cstdio>
    +int main (int argc, char *argv[])
    +{
    +int th_id, nthreads;
    +#pragma omp parallel private(th_id) shared(nthreads)
    +{
    +th_id = omp_get_thread_num();
    +printf("Hello World from thread %d\n", th_id);
    +#pragma omp barrier
    +if ( th_id == 0 ) {
    +nthreads = omp_get_num_threads();
    +printf("There are %d threads\n",nthreads);
    +}
    +}
    +return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Hello world, yet another variant

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #include <cstdio>
    +#include <omp.h>
    +int main(int argc, char *argv[]) 
    +{
    + omp_set_num_threads(4); 
    +#pragma omp parallel
    + {
    +   int id = omp_get_thread_num();
    +   int nproc = omp_get_num_threads(); 
    +   cout << "Hello world with id number and processes " <<  id <<  nproc << endl;
    + } 
    +return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Variables declared outside of the parallel region are shared by all threads +If a variable like id is declared outside of the +

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel, 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    it would have been shared by various the threads, possibly causing erroneous output

    +
      +
    • Why? What would go wrong? Why do we add possibly?
    • +
    +
    + + +









    +

    Important OpenMP library routines

    +
    + +

    + +

      +
    • int omp get num threads (), returns the number of threads inside a parallel region
    • +
    • int omp get thread num (), returns the a thread for each thread inside a parallel region
    • +
    • void omp set num threads (int), sets the number of threads to be used
    • +
    • void omp set nested (int), turns nested parallelism on/off
    • +
    +
    + + +









    +

    Private variables

    +
    + +

    +

    Private clause can be used to make thread- private versions of such variables:

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel private(id)
    +{
    + int id = omp_get_thread_num();
    + cout << "My thread num" << id << endl; 
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +
    • What is their value on entry? Exit?
    • +
    • OpenMP provides ways to control that
    • +
    • Can use default(none) to require the sharing of each variable to be described
    • +
    +
    + + +









    +

    Master region

    +
    + +

    +

    It is often useful to have only one thread execute some of the code in a parallel region. I/O statements are a common example

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel 
    +{
    +  #pragma omp master
    +   {
    +      int id = omp_get_thread_num();
    +      cout << "My thread num" << id << endl; 
    +   } 
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Parallel for loop

    +
    + +

    +

      +
    • Inside a parallel region, the following compiler directive can be used to parallelize a for-loop:
    • +
    + + +
    +
    +
    +
    +
    +
    #pragma omp for
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +
    • Clauses can be added, such as
    • +
        +
      • schedule(static, chunk size)
      • +
      • schedule(dynamic, chunk size)
      • +
      • schedule(guided, chunk size) (non-deterministic allocation)
      • +
      • schedule(runtime)
      • +
      • private(list of variables)
      • +
      • reduction(operator:variable)
      • +
      • nowait
      • +
      +
    +
    + + +









    +

    Parallel computations and loops

    + +
    + +

    +

    OpenMP provides an easy way to parallelize a loop

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel for
    +  for (i=0; i<n; i++) c[i] = a[i];
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    OpenMP handles index variable (no need to declare in for loop or make private)

    + +

    Which thread does which values? Several options.

    +
    + + +









    +

    Scheduling of loop computations

    + +
    + +

    +

    We can let the OpenMP runtime decide. The decision is about how the loop iterates are scheduled +and OpenMP defines three choices of loop scheduling: +

    +
      +
    1. Static: Predefined at compile time. Lowest overhead, predictable
    2. +
    3. Dynamic: Selection made at runtime
    4. +
    5. Guided: Special case of dynamic; attempts to reduce overhead
    6. +
    +
    + + +









    +

    Example code for loop scheduling

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #include <omp.h>
    +#define CHUNKSIZE 100
    +#define N 1000
    +int main (int argc, char *argv[])
    +{
    +int i, chunk;
    +float a[N], b[N], c[N];
    +for (i=0; i < N; i++) a[i] = b[i] = i * 1.0;
    +chunk = CHUNKSIZE;
    +#pragma omp parallel shared(a,b,c,chunk) private(i)
    +{
    +#pragma omp for schedule(dynamic,chunk)
    +for (i=0; i < N; i++) c[i] = a[i] + b[i];
    +} /* end of parallel region */
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Example code for loop scheduling, guided instead of dynamic

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #include <omp.h>
    +#define CHUNKSIZE 100
    +#define N 1000
    +int main (int argc, char *argv[])
    +{
    +int i, chunk;
    +float a[N], b[N], c[N];
    +for (i=0; i < N; i++) a[i] = b[i] = i * 1.0;
    +chunk = CHUNKSIZE;
    +#pragma omp parallel shared(a,b,c,chunk) private(i)
    +{
    +#pragma omp for schedule(guided,chunk)
    +for (i=0; i < N; i++) c[i] = a[i] + b[i];
    +} /* end of parallel region */
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    More on Parallel for loop

    +
    + +

    +

      +
    • The number of loop iterations cannot be non-deterministic; break, return, exit, goto not allowed inside the for-loop
    • +
    • The loop index is private to each thread
    • +
    • A reduction variable is special
    • +
        +
      • During the for-loop there is a local private copy in each thread
      • +
      • At the end of the for-loop, all the local copies are combined together by the reduction operation
      • +
      +
    • Unless the nowait clause is used, an implicit barrier synchronization will be added at the end by the compiler
    • +
    + + +
    +
    +
    +
    +
    +
    // #pragma omp parallel and #pragma omp for
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    can be combined into

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel for
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    What can happen with this loop?

    + +
    + +

    +

    What happens with code like this

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel for
    +for (i=0; i<n; i++) sum += a[i]*a[i];
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    All threads can access the sum variable, but the addition is not atomic! It is important to avoid race between threads. So-called reductions in OpenMP are thus important for performance and for obtaining correct results. OpenMP lets us indicate that a variable is used for a reduction with a particular operator. The above code becomes

    + + +
    +
    +
    +
    +
    +
    sum = 0.0;
    +#pragma omp parallel for reduction(+:sum)
    +for (i=0; i<n; i++) sum += a[i]*a[i];
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Inner product

    +
    + +

    +$$ +\sum_{i=0}^{n-1} a_ib_i +$$ + + + +

    +
    +
    +
    +
    +
    int i;
    +double sum = 0.;
    +/* allocating and initializing arrays */
    +/* ... */
    +#pragma omp parallel for default(shared) private(i) reduction(+:sum)
    + for (i=0; i<N; i++) sum += a[i]*b[i];
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Different threads do different tasks

    +
    + +

    + +

    Different threads do different tasks independently, each section is executed by one thread.

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel
    +{
    +#pragma omp sections
    +{
    +#pragma omp section
    +funcA ();
    +#pragma omp section
    +funcB ();
    +#pragma omp section
    +funcC ();
    +}
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Single execution

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #pragma omp single { ... }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    The code is executed by one thread only, no guarantee which thread

    + +

    Can introduce an implicit barrier at the end

    + + +
    +
    +
    +
    +
    +
    #pragma omp master { ... }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Code executed by the master thread, guaranteed and no implicit barrier at the end.

    +
    + + +









    +

    Coordination and synchronization

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #pragma omp barrier
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Synchronization, must be encountered by all threads in a team (or none)

    + + +
    +
    +
    +
    +
    +
    #pragma omp ordered { a block of codes }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    is another form of synchronization (in sequential order). +The form +

    + + +
    +
    +
    +
    +
    +
    #pragma omp critical { a block of codes }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    and

    + + +
    +
    +
    +
    +
    +
    #pragma omp atomic { single assignment statement }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    is more efficient than

    + + +
    +
    +
    +
    +
    +
    #pragma omp critical
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Data scope

    +
    + +

    +

      +
    • OpenMP data scope attribute clauses:
    • +
        +
      • shared
      • +
      • private
      • +
      • firstprivate
      • +
      • lastprivate
      • +
      • reduction
      • +
      +
    +

    What are the purposes of these attributes

    +
      +
    • define how and which variables are transferred to a parallel region (and back)
    • +
    • define which variables are visible to all threads in a parallel region, and which variables are privately allocated to each thread
    • +
    +
    + + +









    +

    Some remarks

    +
    + +

    + +

      +
    • When entering a parallel region, the private clause ensures each thread having its own new variable instances. The new variables are assumed to be uninitialized.
    • +
    • A shared variable exists in only one memory location and all threads can read and write to that address. It is the programmer's responsibility to ensure that multiple threads properly access a shared variable.
    • +
    • The firstprivate clause combines the behavior of the private clause with automatic initialization.
    • +
    • The lastprivate clause combines the behavior of the private clause with a copy back (from the last loop iteration or section) to the original variable outside the parallel region.
    • +
    +
    + + +









    +

    Parallelizing nested for-loops

    +
    + +

    + +

      +
    • Serial code
    • +
    + + +
    +
    +
    +
    +
    +
    for (i=0; i<100; i++)
    +    for (j=0; j<100; j++)
    +        a[i][j] = b[i][j] + c[i][j];
    +    }
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
      +
    • Parallelization
    • +
    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel for private(j)
    +for (i=0; i<100; i++)
    +    for (j=0; j<100; j++)
    +       a[i][j] = b[i][j] + c[i][j];
    +    }
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
      +
    • Why not parallelize the inner loop? to save overhead of repeated thread forks-joins
    • +
    • Why must j be private? To avoid race condition among the threads
    • +
    +
    + + +









    +

    Nested parallelism

    +
    + +

    +

    When a thread in a parallel region encounters another parallel construct, it +may create a new team of threads and become the master of the new +team. +

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel num_threads(4)
    +{
    +/* .... */
    +#pragma omp parallel num_threads(2)
    +{
    +//  
    +}
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Parallel tasks

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #pragma omp task 
    +#pragma omp parallel shared(p_vec) private(i)
    +{
    +#pragma omp single
    +{
    +for (i=0; i<N; i++) {
    +  double r = random_number();
    +  if (p_vec[i] > r) {
    +#pragma omp task
    +   do_work (p_vec[i]);
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Common mistakes

    +
    + +

    +

    Race condition

    + + +
    +
    +
    +
    +
    +
    int nthreads;
    +#pragma omp parallel shared(nthreads)
    +{
    +nthreads = omp_get_num_threads();
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Deadlock

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel
    +{
    +...
    +#pragma omp critical
    +{
    +...
    +#pragma omp barrier
    +}
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + +

    Not all computations are simple

    +
    + +

    +

    Not all computations are simple loops where the data can be evenly +divided among threads without any dependencies between threads +

    + +

    An example is finding the location and value of the largest element in an array

    + + +
    +
    +
    +
    +
    +
    for (i=0; i<n; i++) { 
    +   if (x[i] > maxval) {
    +      maxval = x[i];
    +      maxloc = i; 
    +   }
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + +

    Not all computations are simple, competing threads

    +
    + +

    +

    All threads are potentially accessing and changing the same values, maxloc and maxval.

    +
      +
    1. OpenMP provides several ways to coordinate access to shared values
    2. +
    + + +
    +
    +
    +
    +
    +
    #pragma omp atomic
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +
    1. Only one thread at a time can execute the following statement (not block). We can use the critical option
    2. +
    + + +
    +
    +
    +
    +
    +
    #pragma omp critical
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
      +
    1. Only one thread at a time can execute the following block
    2. +
    +

    Atomic may be faster than critical but depends on hardware

    +
    + + +









    +

    How to find the max value using OpenMP

    +
    + +

    +

    Write down the simplest algorithm and look carefully for race conditions. How would you handle them? +The first step would be to parallelize as +

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel for
    + for (i=0; i<n; i++) {
    +    if (x[i] > maxval) {
    +      maxval = x[i];
    +      maxloc = i; 
    +    }
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Then deal with the race conditions

    +
    + +

    +

    Write down the simplest algorithm and look carefully for race conditions. How would you handle them? +The first step would be to parallelize as +

    + + +
    +
    +
    +
    +
    +
    #pragma omp parallel for
    + for (i=0; i<n; i++) {
    +#pragma omp critical
    +  {
    +     if (x[i] > maxval) {
    +       maxval = x[i];
    +       maxloc = i; 
    +     }
    +  }
    +} 
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Exercise: write a code which implements this and give an estimate on performance. Perform several runs, +with a serial code only with and without vectorization and compare the serial code with the one that uses OpenMP. Run on different archictectures if you can. +

    +
    + +









    +

    What can slow down OpenMP performance?

    +

    Give it a thought!

    + +









    +

    What can slow down OpenMP performance?

    +
    + +

    +

    Performance poor because we insisted on keeping track of the maxval and location during the execution of the loop.

    +
      +
    • We do not care about the value during the execution of the loop, just the value at the end.
    • +
    +

    This is a common source of performance issues, namely the description of the method used to compute a value imposes additional, unnecessary requirements or properties

    + +Idea: Have each thread find the maxloc in its own data, then combine and use temporary arrays indexed by thread number to hold the values found by each thread +
    + + +









    +

    Find the max location for each thread

    +
    + +

    + + +

    +
    +
    +
    +
    +
    int maxloc[MAX_THREADS], mloc;
    +double maxval[MAX_THREADS], mval; 
    +#pragma omp parallel shared(maxval,maxloc)
    +{
    +  int id = omp_get_thread_num(); 
    +  maxval[id] = -1.0e30;
    +#pragma omp for
    +   for (int i=0; i<n; i++) {
    +       if (x[i] > maxval[id]) { 
    +           maxloc[id] = i;
    +           maxval[id] = x[i]; 
    +       }
    +    }
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Combine the values from each thread

    +
    + +

    + + +

    +
    +
    +
    +
    +
    #pragma omp flush (maxloc,maxval)
    +#pragma omp master
    +  {
    +    int nt = omp_get_num_threads(); 
    +    mloc = maxloc[0]; 
    +    mval = maxval[0]; 
    +    for (int i=1; i<nt; i++) {
    +        if (maxval[i] > mval) { 
    +           mval = maxval[i]; 
    +           mloc = maxloc[i];
    +        } 
    +     }
    +   }
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    Note that we let the master process perform the last operation.

    +
    + +









    +

    Matrix-matrix multiplication

    +

    This code computes the norm of a vector using OpenMp

    + + +
    +
    +
    +
    +
    +
    //  OpenMP program to compute vector norm by adding two other vectors
    +#include <cstdlib>
    +#include <iostream>
    +#include <cmath>
    +#include <iomanip>
    +#include  <omp.h>
    +# include <ctime>
    +
    +using namespace std; // note use of namespace
    +int main (int argc, char* argv[])
    +{
    +  // read in dimension of vector
    +  int n = atoi(argv[1]);
    +  double *a, *b, *c;
    +  int i;
    +  int thread_num;
    +  double wtime, Norm2, s, angle;
    +  cout << "  Perform addition of two vectors and compute the norm-2." << endl;
    +  omp_set_num_threads(4);
    +  thread_num = omp_get_max_threads ();
    +  cout << "  The number of processors available = " << omp_get_num_procs () << endl ;
    +  cout << "  The number of threads available    = " << thread_num <<  endl;
    +  cout << "  The matrix order n                 = " << n << endl;
    +
    +  s = 1.0/sqrt( (double) n);
    +  wtime = omp_get_wtime ( );
    +  // Allocate space for the vectors to be used
    +  a = new double [n]; b = new double [n]; c = new double [n];
    +  // Define parallel region
    +# pragma omp parallel for default(shared) private (angle, i) reduction(+:Norm2)
    +  // Set up values for vectors  a and b
    +  for (i = 0; i < n; i++){
    +      angle = 2.0*M_PI*i/ (( double ) n);
    +      a[i] = s*(sin(angle) + cos(angle));
    +      b[i] =  s*sin(2.0*angle);
    +      c[i] = 0.0;
    +  }
    +  // Then perform the vector addition
    +  for (i = 0; i < n; i++){
    +     c[i] += a[i]+b[i];
    +  }
    +  // Compute now the norm-2
    +  Norm2 = 0.0;
    +  for (i = 0; i < n; i++){
    +     Norm2  += c[i]*c[i];
    +  }
    +// end parallel region
    +  wtime = omp_get_wtime ( ) - wtime;
    +  cout << setiosflags(ios::showpoint | ios::uppercase);
    +  cout << setprecision(10) << setw(20) << "Time used  for norm-2 computation=" << wtime  << endl;
    +  cout << " Norm-2  = " << Norm2 << endl;
    +  // Free up space
    +  delete[] a;
    +  delete[] b;
    +  delete[] c;
    +  return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +









    +

    Matrix-matrix multiplication

    +

    This the matrix-matrix multiplication code with plain c++ memory allocation using OpenMP

    + + + +
    +
    +
    +
    +
    +
    //  Matrix-matrix multiplication and Frobenius norm of a matrix with OpenMP
    +#include <cstdlib>
    +#include <iostream>
    +#include <cmath>
    +#include <iomanip>
    +#include  <omp.h>
    +# include <ctime>
    +
    +using namespace std; // note use of namespace
    +int main (int argc, char* argv[])
    +{
    +  // read in dimension of square matrix
    +  int n = atoi(argv[1]);
    +  double **A, **B, **C;
    +  int i, j, k;
    +  int thread_num;
    +  double wtime, Fsum, s, angle;
    +  cout << "  Compute matrix product C = A * B and Frobenius norm." << endl;
    +  omp_set_num_threads(4);
    +  thread_num = omp_get_max_threads ();
    +  cout << "  The number of processors available = " << omp_get_num_procs () << endl ;
    +  cout << "  The number of threads available    = " << thread_num <<  endl;
    +  cout << "  The matrix order n                 = " << n << endl;
    +
    +  s = 1.0/sqrt( (double) n);
    +  wtime = omp_get_wtime ( );
    +  // Allocate space for the two matrices
    +  A = new double*[n]; B = new double*[n]; C = new double*[n];
    +  for (i = 0; i < n; i++){
    +    A[i] = new double[n];
    +    B[i] = new double[n];
    +    C[i] = new double[n];
    +  }
    +  // Define parallel region
    +# pragma omp parallel for default(shared) private (angle, i, j, k) reduction(+:Fsum)
    +  // Set up values for matrix A and B and zero matrix C
    +  for (i = 0; i < n; i++){
    +    for (j = 0; j < n; j++) {
    +      angle = 2.0*M_PI*i*j/ (( double ) n);
    +      A[i][j] = s * ( sin ( angle ) + cos ( angle ) );
    +      B[j][i] =  A[i][j];
    +    }
    +  }
    +  // Then perform the matrix-matrix multiplication
    +  for (i = 0; i < n; i++){
    +    for (j = 0; j < n; j++) {
    +       C[i][j] =  0.0;    
    +       for (k = 0; k < n; k++) {
    +            C[i][j] += A[i][k]*B[k][j];
    +       }
    +    }
    +  }
    +  // Compute now the Frobenius norm
    +  Fsum = 0.0;
    +  for (i = 0; i < n; i++){
    +    for (j = 0; j < n; j++) {
    +      Fsum += C[i][j]*C[i][j];
    +    }
    +  }
    +  Fsum = sqrt(Fsum);
    +// end parallel region and letting only one thread perform I/O
    +  wtime = omp_get_wtime ( ) - wtime;
    +  cout << setiosflags(ios::showpoint | ios::uppercase);
    +  cout << setprecision(10) << setw(20) << "Time used  for matrix-matrix multiplication=" << wtime  << endl;
    +  cout << "  Frobenius norm  = " << Fsum << endl;
    +  // Free up space
    +  for (int i = 0; i < n; i++){
    +    delete[] A[i];
    +    delete[] B[i];
    +    delete[] C[i];
    +  }
    +  delete[] A;
    +  delete[] B;
    +  delete[] C;
    +  return 0;
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    © 1999-2024, Morten Hjorth-Jensen Email morten.hjorth-jensen@fys.uio.no. Released under CC Attribution-NonCommercial 4.0 license diff --git a/doc/pub/week9/ipynb/ipynb-week9-src.tar.gz b/doc/pub/week9/ipynb/ipynb-week9-src.tar.gz index 1d0273241f695951ebfb6ef175975094f3224a99..e1f7c1fa7945f925de479e13d994ff314abd6fb6 100644 GIT binary patch literal 191 zcmV;w06_mAiwFQU4)bLI1MSaE3c@fD1>mlGia9}H0K`Kj`QWe~Ys#PGGPG*oR tGQ$KkU0b0Exa@%Exv)Y>e(@WvPMX&y@PB=a<2a7)Hs diff --git a/doc/pub/week9/ipynb/week9.ipynb b/doc/pub/week9/ipynb/week9.ipynb index 2a9be3da..e6cc2d25 100644 --- a/doc/pub/week9/ipynb/week9.ipynb +++ b/doc/pub/week9/ipynb/week9.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "4b5596ee", + "id": "7f934c76", "metadata": { "editable": true }, @@ -14,7 +14,7 @@ }, { "cell_type": "markdown", - "id": "d33a95e3", + "id": "5d8c5800", "metadata": { "editable": true }, @@ -27,7 +27,7 @@ }, { "cell_type": "markdown", - "id": "d548cf0f", + "id": "7bd2e26c", "metadata": { "editable": true }, @@ -45,16 +45,12 @@ "\n", "\n", "\n", - "**Teaching Material, videos and written material.**\n", - "\n", - "* Overview video on the [Bootstrap method](https://www.youtube.com/watch?v=O_Fj4q8lgmc&ab_channel=MarinStatsLectures-RProgramming%26Statistics)\n", - "\n", - "* [Marius Johnson's Master thesis on the Blocking Method](https://www.duo.uio.no/bitstream/handle/10852/68360/PhysRevE.98.043304.pdf?sequence=2&isAllowed=y)" + "Note, these notes contain additional material om optimization and parallelization. Parts of this material will be discussed this week." ] }, { "cell_type": "markdown", - "id": "930c8d02", + "id": "181ed6f2", "metadata": { "editable": true }, @@ -71,7 +67,7 @@ }, { "cell_type": "markdown", - "id": "4d06554e", + "id": "e47adac5", "metadata": { "editable": true }, @@ -90,7 +86,7 @@ }, { "cell_type": "markdown", - "id": "0be7d825", + "id": "e143647e", "metadata": { "editable": true }, @@ -108,7 +104,7 @@ }, { "cell_type": "markdown", - "id": "f008a4ba", + "id": "b8c9399e", "metadata": { "editable": true }, @@ -125,7 +121,7 @@ }, { "cell_type": "markdown", - "id": "f32d922f", + "id": "72de4651", "metadata": { "editable": true }, @@ -137,7 +133,7 @@ }, { "cell_type": "markdown", - "id": "2484aca0", + "id": "8e9371fe", "metadata": { "editable": true }, @@ -147,7 +143,7 @@ }, { "cell_type": "markdown", - "id": "7b45ca9a", + "id": "cd3e9e74", "metadata": { "editable": true }, @@ -159,7 +155,7 @@ }, { "cell_type": "markdown", - "id": "acf96a39", + "id": "9cc43d3d", "metadata": { "editable": true }, @@ -174,7 +170,7 @@ }, { "cell_type": "markdown", - "id": "9369eada", + "id": "e4d44112", "metadata": { "editable": true }, @@ -189,7 +185,7 @@ }, { "cell_type": "markdown", - "id": "50693dbd", + "id": "cd1734a3", "metadata": { "editable": true }, @@ -201,7 +197,7 @@ }, { "cell_type": "markdown", - "id": "7a026966", + "id": "0edda6fb", "metadata": { "editable": true }, @@ -211,7 +207,7 @@ }, { "cell_type": "markdown", - "id": "82e82fa9", + "id": "485ab088", "metadata": { "editable": true }, @@ -223,7 +219,7 @@ }, { "cell_type": "markdown", - "id": "5b3f8c63", + "id": "428e6c44", "metadata": { "editable": true }, @@ -233,7 +229,7 @@ }, { "cell_type": "markdown", - "id": "2e651fdb", + "id": "703c3170", "metadata": { "editable": true }, @@ -245,7 +241,7 @@ }, { "cell_type": "markdown", - "id": "bf9b0aa4", + "id": "3404f05c", "metadata": { "editable": true }, @@ -257,7 +253,7 @@ }, { "cell_type": "markdown", - "id": "0f3459da", + "id": "56c64606", "metadata": { "editable": true }, @@ -267,7 +263,7 @@ }, { "cell_type": "markdown", - "id": "3aecc426", + "id": "5c1f6ee5", "metadata": { "editable": true }, @@ -279,7 +275,7 @@ }, { "cell_type": "markdown", - "id": "451588ba", + "id": "a192cbd4", "metadata": { "editable": true }, @@ -289,7 +285,7 @@ }, { "cell_type": "markdown", - "id": "8cbe60fb", + "id": "a4458392", "metadata": { "editable": true }, @@ -301,7 +297,7 @@ }, { "cell_type": "markdown", - "id": "cf819af3", + "id": "085cafc0", "metadata": { "editable": true }, @@ -313,7 +309,7 @@ }, { "cell_type": "markdown", - "id": "94d446e9", + "id": "f8b1bd83", "metadata": { "editable": true }, @@ -328,7 +324,7 @@ }, { "cell_type": "markdown", - "id": "a3bed598", + "id": "085dc147", "metadata": { "editable": true }, @@ -338,7 +334,7 @@ }, { "cell_type": "markdown", - "id": "289ea914", + "id": "b07f69d6", "metadata": { "editable": true }, @@ -350,7 +346,7 @@ }, { "cell_type": "markdown", - "id": "e4f189cc", + "id": "524a3980", "metadata": { "editable": true }, @@ -364,7 +360,7 @@ }, { "cell_type": "markdown", - "id": "e0c75890", + "id": "aa229836", "metadata": { "editable": true }, @@ -383,7 +379,7 @@ }, { "cell_type": "markdown", - "id": "b5fc2142", + "id": "a74e265c", "metadata": { "editable": true }, @@ -395,7 +391,7 @@ }, { "cell_type": "markdown", - "id": "3ba59e20", + "id": "48a0b0b5", "metadata": { "editable": true }, @@ -407,7 +403,7 @@ }, { "cell_type": "markdown", - "id": "85c360b6", + "id": "4fa10f66", "metadata": { "editable": true }, @@ -417,7 +413,7 @@ }, { "cell_type": "markdown", - "id": "88132941", + "id": "10aaa6c6", "metadata": { "editable": true }, @@ -429,7 +425,7 @@ }, { "cell_type": "markdown", - "id": "47ce48bd", + "id": "6dc77832", "metadata": { "editable": true }, @@ -439,7 +435,7 @@ }, { "cell_type": "markdown", - "id": "a17dde39", + "id": "78b3c31c", "metadata": { "editable": true }, @@ -451,7 +447,7 @@ }, { "cell_type": "markdown", - "id": "dbcd8555", + "id": "4c584946", "metadata": { "editable": true }, @@ -463,7 +459,7 @@ }, { "cell_type": "markdown", - "id": "9753ec4d", + "id": "bb141426", "metadata": { "editable": true }, @@ -477,7 +473,7 @@ }, { "cell_type": "markdown", - "id": "aed90511", + "id": "6f36565b", "metadata": { "editable": true }, @@ -487,7 +483,7 @@ }, { "cell_type": "markdown", - "id": "3d10512f", + "id": "250b1b3e", "metadata": { "editable": true }, @@ -495,9 +491,9 @@ "## Resampling methods: Blocking\n", "\n", "The blocking method was made popular by [Flyvbjerg and Pedersen (1989)](https://aip.scitation.org/doi/10.1063/1.457480)\n", - "and has become one of the standard ways to estimate\n", - "$V(\\widehat{\\theta})$ for exactly one $\\widehat{\\theta}$, namely\n", - "$\\widehat{\\theta} = \\overline{X}$. \n", + "and has become one of the standard ways to estimate the variance\n", + "$\\mathrm{var}(\\widehat{\\theta})$ for exactly one estimator $\\widehat{\\theta}$, namely\n", + "$\\widehat{\\theta} = \\overline{X}$, the mean value. \n", "\n", "Assume $n = 2^d$ for some integer $d>1$ and $X_1,X_2,\\cdots, X_n$ is a stationary time series to begin with. \n", "Moreover, assume that the series is asymptotically uncorrelated. We switch to vector notation by arranging $X_1,X_2,\\cdots,X_n$ in an $n$-tuple. Define:" @@ -505,7 +501,7 @@ }, { "cell_type": "markdown", - "id": "297d90f5", + "id": "233f70b8", "metadata": { "editable": true }, @@ -519,7 +515,7 @@ }, { "cell_type": "markdown", - "id": "74859bc8", + "id": "b17d9e13", "metadata": { "editable": true }, @@ -534,7 +530,7 @@ }, { "cell_type": "markdown", - "id": "0bed800d", + "id": "179d551e", "metadata": { "editable": true }, @@ -550,7 +546,7 @@ }, { "cell_type": "markdown", - "id": "a7e0c557", + "id": "f89ed58e", "metadata": { "editable": true }, @@ -562,7 +558,7 @@ }, { "cell_type": "markdown", - "id": "7557cd42", + "id": "44308237", "metadata": { "editable": true }, @@ -581,7 +577,7 @@ }, { "cell_type": "markdown", - "id": "315c77b3", + "id": "1a9ef202", "metadata": { "editable": true }, @@ -605,7 +601,7 @@ }, { "cell_type": "markdown", - "id": "e11a5834", + "id": "0ca89ff0", "metadata": { "editable": true }, @@ -620,7 +616,7 @@ }, { "cell_type": "markdown", - "id": "03174f94", + "id": "6bc37505", "metadata": { "editable": true }, @@ -632,7 +628,7 @@ }, { "cell_type": "markdown", - "id": "741ee4c9", + "id": "87775adf", "metadata": { "editable": true }, @@ -644,7 +640,7 @@ }, { "cell_type": "markdown", - "id": "f14a96cb", + "id": "14149079", "metadata": { "editable": true }, @@ -662,7 +658,7 @@ }, { "cell_type": "markdown", - "id": "323e46df", + "id": "73987934", "metadata": { "editable": true }, @@ -680,7 +676,7 @@ }, { "cell_type": "markdown", - "id": "a71d9b3c", + "id": "69fbf5d1", "metadata": { "editable": true }, @@ -691,7 +687,7 @@ }, { "cell_type": "markdown", - "id": "dd98a112", + "id": "81fe5990", "metadata": { "editable": true }, @@ -702,7 +698,7 @@ }, { "cell_type": "markdown", - "id": "0e0bec22", + "id": "d4706212", "metadata": { "editable": true }, @@ -720,7 +716,7 @@ }, { "cell_type": "markdown", - "id": "7b004b32", + "id": "eb661f47", "metadata": { "editable": true }, @@ -730,7 +726,7 @@ }, { "cell_type": "markdown", - "id": "a70cba82", + "id": "2f1d607f", "metadata": { "editable": true }, @@ -748,7 +744,7 @@ }, { "cell_type": "markdown", - "id": "14a5f387", + "id": "eecababc", "metadata": { "editable": true }, @@ -758,7 +754,7 @@ }, { "cell_type": "markdown", - "id": "68a6d6ac", + "id": "49a6dfb0", "metadata": { "editable": true }, @@ -770,7 +766,7 @@ }, { "cell_type": "markdown", - "id": "86df1a10", + "id": "2c62cc63", "metadata": { "editable": true }, @@ -782,7 +778,7 @@ }, { "cell_type": "markdown", - "id": "0a2ea53b", + "id": "06fde16f", "metadata": { "editable": true }, @@ -800,7 +796,7 @@ }, { "cell_type": "markdown", - "id": "c5852aa3", + "id": "61e106ab", "metadata": { "editable": true }, @@ -810,7 +806,7 @@ }, { "cell_type": "markdown", - "id": "09cbe900", + "id": "a9f5001a", "metadata": { "editable": true }, @@ -827,15 +823,17 @@ }, { "cell_type": "markdown", - "id": "a15058db", + "id": "aed624ba", "metadata": { "editable": true }, "source": [ + "## More on the blocking method\n", + "\n", "Flyvbjerg and Petersen demonstrated that the sequence\n", "$\\{e_k\\}_{k=0}^{d-1}$ is decreasing, and conjecture that the term\n", "$e_k$ can be made as small as we would like by making $k$ (and hence\n", - "$d$) sufficiently large. The sequence is decreasing (Master of Science thesis by Marius Jonsson, UiO 2018).\n", + "$d$) sufficiently large. The sequence is decreasing.\n", "It means we can apply blocking transformations until\n", "$e_k$ is sufficiently small, and then estimate $\\mathrm{var}(\\overline{X})$ by\n", "$\\widehat{\\sigma}^2_k/n_k$. \n", @@ -845,7 +843,7 @@ }, { "cell_type": "markdown", - "id": "4f46a36c", + "id": "421f9f8b", "metadata": { "editable": true }, @@ -856,7 +854,7 @@ { "cell_type": "code", "execution_count": 1, - "id": "9b554ff8", + "id": "8d23bb07", "metadata": { "collapsed": false, "editable": true @@ -1085,7 +1083,7 @@ }, { "cell_type": "markdown", - "id": "7dee6cbf", + "id": "7d5001dc", "metadata": { "editable": true }, @@ -1100,7 +1098,7 @@ { "cell_type": "code", "execution_count": 2, - "id": "989a7557", + "id": "6da7142f", "metadata": { "collapsed": false, "editable": true @@ -1163,6 +1161,4877 @@ "frame = pd.DataFrame(data,index=['Values'])\n", "print(frame)" ] + }, + { + "cell_type": "markdown", + "id": "6d0b7e85", + "metadata": { + "editable": true + }, + "source": [ + "## Content\n", + "* Simple compiler options \n", + "\n", + "* Tools to benchmark your code\n", + "\n", + "* Machine architectures\n", + "\n", + "* What is vectorization?\n", + "\n", + "* How to measure code performance\n", + "\n", + "* Parallelization with OpenMP\n", + "\n", + "* Parallelization with MPI\n", + "\n", + "* Vectorization and parallelization, examples" + ] + }, + { + "cell_type": "markdown", + "id": "17407842", + "metadata": { + "editable": true + }, + "source": [ + "## Optimization and profiling\n", + "\n", + "Till now we have not paid much attention to speed and possible optimization possibilities\n", + "inherent in the various compilers. We have compiled and linked as" + ] + }, + { + "cell_type": "markdown", + "id": "ad210718", + "metadata": { + "editable": true + }, + "source": [ + " c++ -c mycode.cpp\n", + " c++ -o mycode.exe mycode.o\n" + ] + }, + { + "cell_type": "markdown", + "id": "69401651", + "metadata": { + "editable": true + }, + "source": [ + "For Fortran replace with for example **gfortran** or **ifort**.\n", + "This is what we call a flat compiler option and should be used when we develop the code.\n", + "It produces normally a very large and slow code when translated to machine instructions.\n", + "We use this option for debugging and for establishing the correct program output because\n", + "every operation is done precisely as the user specified it.\n", + "\n", + "It is instructive to look up the compiler manual for further instructions by writing" + ] + }, + { + "cell_type": "markdown", + "id": "68b36411", + "metadata": { + "editable": true + }, + "source": [ + " man c++\n" + ] + }, + { + "cell_type": "markdown", + "id": "6c321ece", + "metadata": { + "editable": true + }, + "source": [ + "## More on optimization\n", + "We have additional compiler options for optimization. These may include procedure inlining where \n", + "performance may be improved, moving constants inside loops outside the loop, \n", + "identify potential parallelism, include automatic vectorization or replace a division with a reciprocal\n", + "and a multiplication if this speeds up the code." + ] + }, + { + "cell_type": "markdown", + "id": "e8c5dca5", + "metadata": { + "editable": true + }, + "source": [ + " c++ -O3 -c mycode.cpp\n", + " c++ -O3 -o mycode.exe mycode.o\n" + ] + }, + { + "cell_type": "markdown", + "id": "5dbfe538", + "metadata": { + "editable": true + }, + "source": [ + "This (other options are -O2 or -Ofast) is the recommended option." + ] + }, + { + "cell_type": "markdown", + "id": "f67659a4", + "metadata": { + "editable": true + }, + "source": [ + "## Optimization and profiling\n", + "It is also useful to profile your program under the development stage.\n", + "You would then compile with" + ] + }, + { + "cell_type": "markdown", + "id": "77187b55", + "metadata": { + "editable": true + }, + "source": [ + " c++ -pg -O3 -c mycode.cpp\n", + " c++ -pg -O3 -o mycode.exe mycode.o\n" + ] + }, + { + "cell_type": "markdown", + "id": "92aa4055", + "metadata": { + "editable": true + }, + "source": [ + "After you have run the code you can obtain the profiling information via" + ] + }, + { + "cell_type": "markdown", + "id": "1e09c863", + "metadata": { + "editable": true + }, + "source": [ + " gprof mycode.exe > ProfileOutput\n" + ] + }, + { + "cell_type": "markdown", + "id": "ec018a86", + "metadata": { + "editable": true + }, + "source": [ + "When you have profiled properly your code, you must take out this option as it \n", + "slows down performance.\n", + "For memory tests use [valgrind](http://www.valgrind.org). An excellent environment for all these aspects, and much more, is Qt creator." + ] + }, + { + "cell_type": "markdown", + "id": "0d745d5f", + "metadata": { + "editable": true + }, + "source": [ + "## Optimization and debugging\n", + "Adding debugging options is a very useful alternative under the development stage of a program.\n", + "You would then compile with" + ] + }, + { + "cell_type": "markdown", + "id": "9d0406a1", + "metadata": { + "editable": true + }, + "source": [ + " c++ -g -O0 -c mycode.cpp\n", + " c++ -g -O0 -o mycode.exe mycode.o\n" + ] + }, + { + "cell_type": "markdown", + "id": "7c0f74b9", + "metadata": { + "editable": true + }, + "source": [ + "This option generates debugging information allowing you to trace for example if an array is properly allocated. Some compilers work best with the no optimization option **-O0**.\n", + "\n", + "**Other optimization flags.**\n", + "\n", + "Depending on the compiler, one can add flags which generate code that catches integer overflow errors. \n", + "The flag **-ftrapv** does this for the CLANG compiler on OS X operating systems." + ] + }, + { + "cell_type": "markdown", + "id": "018d7811", + "metadata": { + "editable": true + }, + "source": [ + "## Other hints\n", + "In general, irrespective of compiler options, it is useful to\n", + "* avoid if tests or call to functions inside loops, if possible. \n", + "\n", + "* avoid multiplication with constants inside loops if possible\n", + "\n", + "Here is an example of a part of a program where specific operations lead to a slower code" + ] + }, + { + "cell_type": "markdown", + "id": "897c72c9", + "metadata": { + "editable": true + }, + "source": [ + " k = n-1;\n", + " for (i = 0; i < n; i++){\n", + " a[i] = b[i] +c*d;\n", + " e = g[k];\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "adec4792", + "metadata": { + "editable": true + }, + "source": [ + "A better code is" + ] + }, + { + "cell_type": "markdown", + "id": "cb465899", + "metadata": { + "editable": true + }, + "source": [ + " temp = c*d;\n", + " for (i = 0; i < n; i++){\n", + " a[i] = b[i] + temp;\n", + " }\n", + " e = g[n-1];\n" + ] + }, + { + "cell_type": "markdown", + "id": "3d14ad95", + "metadata": { + "editable": true + }, + "source": [ + "Here we avoid a repeated multiplication inside a loop. \n", + "Most compilers, depending on compiler flags, identify and optimize such bottlenecks on their own, without requiring any particular action by the programmer. However, it is always useful to single out and avoid code examples like the first one discussed here." + ] + }, + { + "cell_type": "markdown", + "id": "002fc562", + "metadata": { + "editable": true + }, + "source": [ + "## Vectorization and the basic idea behind parallel computing\n", + "Present CPUs are highly parallel processors with varying levels of parallelism. The typical situation can be described via the following three statements.\n", + "* Pursuit of shorter computation time and larger simulation size gives rise to parallel computing.\n", + "\n", + "* Multiple processors are involved to solve a global problem.\n", + "\n", + "* The essence is to divide the entire computation evenly among collaborative processors. Divide and conquer.\n", + "\n", + "Before we proceed with a more detailed discussion of topics like vectorization and parallelization, we need to remind ourselves about some basic features of different hardware models." + ] + }, + { + "cell_type": "markdown", + "id": "d03ee544", + "metadata": { + "editable": true + }, + "source": [ + "## A rough classification of hardware models\n", + "\n", + "* Conventional single-processor computers are named SISD (single-instruction-single-data) machines.\n", + "\n", + "* SIMD (single-instruction-multiple-data) machines incorporate the idea of parallel processing, using a large number of processing units to execute the same instruction on different data.\n", + "\n", + "* Modern parallel computers are so-called MIMD (multiple-instruction-multiple-data) machines and can execute different instruction streams in parallel on different data." + ] + }, + { + "cell_type": "markdown", + "id": "9a764260", + "metadata": { + "editable": true + }, + "source": [ + "## Shared memory and distributed memory\n", + "One way of categorizing modern parallel computers is to look at the memory configuration.\n", + "* In shared memory systems the CPUs share the same address space. Any CPU can access any data in the global memory.\n", + "\n", + "* In distributed memory systems each CPU has its own memory.\n", + "\n", + "The CPUs are connected by some network and may exchange messages." + ] + }, + { + "cell_type": "markdown", + "id": "0de76fb9", + "metadata": { + "editable": true + }, + "source": [ + "## Different parallel programming paradigms\n", + "\n", + "* **Task parallelism**: the work of a global problem can be divided into a number of independent tasks, which rarely need to synchronize. Monte Carlo simulations represent a typical situation. Integration is another. However this paradigm is of limited use.\n", + "\n", + "* **Data parallelism**: use of multiple threads (e.g. one or more threads per processor) to dissect loops over arrays etc. Communication and synchronization between processors are often hidden, thus easy to program. However, the user surrenders much control to a specialized compiler. Examples of data parallelism are compiler-based parallelization and OpenMP directives." + ] + }, + { + "cell_type": "markdown", + "id": "7fd90368", + "metadata": { + "editable": true + }, + "source": [ + "## Different parallel programming paradigms\n", + "\n", + "* **Message passing**: all involved processors have an independent memory address space. The user is responsible for partitioning the data/work of a global problem and distributing the subproblems to the processors. Collaboration between processors is achieved by explicit message passing, which is used for data transfer plus synchronization.\n", + "\n", + "* This paradigm is the most general one where the user has full control. Better parallel efficiency is usually achieved by explicit message passing. However, message-passing programming is more difficult." + ] + }, + { + "cell_type": "markdown", + "id": "18ac99c8", + "metadata": { + "editable": true + }, + "source": [ + "## What is vectorization?\n", + "Vectorization is a special\n", + "case of **Single Instructions Multiple Data** (SIMD) to denote a single\n", + "instruction stream capable of operating on multiple data elements in\n", + "parallel. \n", + "We can think of vectorization as the unrolling of loops accompanied with SIMD instructions.\n", + "\n", + "Vectorization is the process of converting an algorithm that performs scalar operations\n", + "(typically one operation at the time) to vector operations where a single operation can refer to many simultaneous operations.\n", + "Consider the following example" + ] + }, + { + "cell_type": "markdown", + "id": "10f0f1c5", + "metadata": { + "editable": true + }, + "source": [ + " for (i = 0; i < n; i++){\n", + " a[i] = b[i] + c[i];\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "76cbe575", + "metadata": { + "editable": true + }, + "source": [ + "If the code is not vectorized, the compiler will simply start with the first element and \n", + "then perform subsequent additions operating on one address in memory at the time." + ] + }, + { + "cell_type": "markdown", + "id": "ac611c90", + "metadata": { + "editable": true + }, + "source": [ + "## Number of elements that can acted upon\n", + "A SIMD instruction can operate on multiple data elements in one single instruction.\n", + "It uses the so-called 128-bit SIMD floating-point register. \n", + "In this sense, vectorization adds some form of parallelism since one instruction is applied \n", + "to many parts of say a vector.\n", + "\n", + "The number of elements which can be operated on in parallel\n", + "range from four single-precision floating point data elements in so-called \n", + "Streaming SIMD Extensions and two double-precision floating-point data\n", + "elements in Streaming SIMD Extensions 2 to sixteen byte operations in\n", + "a 128-bit register in Streaming SIMD Extensions 2. Thus, vector-length\n", + "ranges from 2 to 16, depending on the instruction extensions used and\n", + "on the data type. \n", + "\n", + "IN summary, our instructions operate on 128 bit (16 byte) operands\n", + "* 4 floats or ints\n", + "\n", + "* 2 doubles\n", + "\n", + "* Data paths 128 bits vide for vector unit" + ] + }, + { + "cell_type": "markdown", + "id": "ce267e9b", + "metadata": { + "editable": true + }, + "source": [ + "## Number of elements that can acted upon, examples\n", + "We start with the simple scalar operations given by" + ] + }, + { + "cell_type": "markdown", + "id": "b5e50c74", + "metadata": { + "editable": true + }, + "source": [ + " for (i = 0; i < n; i++){\n", + " a[i] = b[i] + c[i];\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "4834c522", + "metadata": { + "editable": true + }, + "source": [ + "If the code is not vectorized and we have a 128-bit register to store a 32 bits floating point number,\n", + "it means that we have $3\\times 32$ bits that are not used. \n", + "\n", + "We have thus unused space in our SIMD registers. These registers could hold three additional integers." + ] + }, + { + "cell_type": "markdown", + "id": "cd5abf08", + "metadata": { + "editable": true + }, + "source": [ + "## Operation counts for scalar operation\n", + "The code" + ] + }, + { + "cell_type": "markdown", + "id": "c0cfbeb1", + "metadata": { + "editable": true + }, + "source": [ + " for (i = 0; i < n; i++){\n", + " a[i] = b[i] + c[i];\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "1c2b9caf", + "metadata": { + "editable": true + }, + "source": [ + "has for $n$ repeats\n", + "1. one load for $c[i]$ in address 1\n", + "\n", + "2. one load for $b[i]$ in address 2\n", + "\n", + "3. add $c[i]$ and $b[i]$ to give $a[i]$\n", + "\n", + "4. store $a[i]$ in address 2" + ] + }, + { + "cell_type": "markdown", + "id": "b3627ae3", + "metadata": { + "editable": true + }, + "source": [ + "## Number of elements that can acted upon, examples\n", + "If we vectorize the code, we can perform, with a 128-bit register four simultaneous operations, that is\n", + "we have" + ] + }, + { + "cell_type": "markdown", + "id": "c85f3d13", + "metadata": { + "editable": true + }, + "source": [ + " for (i = 0; i < n; i+=4){\n", + " a[i] = b[i] + c[i];\n", + " a[i+1] = b[i+1] + c[i+1];\n", + " a[i+2] = b[i+2] + c[i+2];\n", + " a[i+3] = b[i+3] + c[i+3];\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "b3d4e458", + "metadata": { + "editable": true + }, + "source": [ + "Four additions are now done in a single step." + ] + }, + { + "cell_type": "markdown", + "id": "c45ed72e", + "metadata": { + "editable": true + }, + "source": [ + "## Number of operations when vectorized\n", + "For $n/4$ repeats assuming floats or integers\n", + "1. one vector load for $c[i]$ in address 1\n", + "\n", + "2. one load for $b[i]$ in address 2\n", + "\n", + "3. add $c[i]$ and $b[i]$ to give $a[i]$\n", + "\n", + "4. store $a[i]$ in address 2" + ] + }, + { + "cell_type": "markdown", + "id": "6b9f160e", + "metadata": { + "editable": true + }, + "source": [ + "## [A simple test case with and without vectorization](https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program7.cpp)\n", + "We implement these operations in a simple c++ program that computes at the end the norm of a vector." + ] + }, + { + "cell_type": "markdown", + "id": "9251ed34", + "metadata": { + "editable": true + }, + "source": [ + " #include \n", + " #include \n", + " #include \n", + " #include \n", + " #include \"time.h\"\n", + " \n", + " using namespace std; // note use of namespace\n", + " int main (int argc, char* argv[])\n", + " {\n", + " // read in dimension of square matrix\n", + " int n = atoi(argv[1]);\n", + " double s = 1.0/sqrt( (double) n);\n", + " double *a, *b, *c;\n", + " // Start timing\n", + " clock_t start, finish;\n", + " start = clock();\n", + " // Allocate space for the vectors to be used\n", + " a = new double [n]; b = new double [n]; c = new double [n];\n", + " // Define parallel region\n", + " // Set up values for vectors a and b\n", + " for (int i = 0; i < n; i++){\n", + " double angle = 2.0*M_PI*i/ (( double ) n);\n", + " a[i] = s*(sin(angle) + cos(angle));\n", + " b[i] = s*sin(2.0*angle);\n", + " c[i] = 0.0;\n", + " }\n", + " // Then perform the vector addition\n", + " for (int i = 0; i < n; i++){\n", + " c[i] += a[i]+b[i];\n", + " }\n", + " // Compute now the norm-2\n", + " double Norm2 = 0.0;\n", + " for (int i = 0; i < n; i++){\n", + " Norm2 += c[i]*c[i];\n", + " }\n", + " finish = clock();\n", + " double timeused = (double) (finish - start)/(CLOCKS_PER_SEC );\n", + " cout << setiosflags(ios::showpoint | ios::uppercase);\n", + " cout << setprecision(10) << setw(20) << \"Time used for norm computation=\" << timeused << endl;\n", + " cout << \" Norm-2 = \" << Norm2 << endl;\n", + " // Free up space\n", + " delete[] a;\n", + " delete[] b;\n", + " delete[] c;\n", + " return 0;\n", + " }\n", + " \n", + " \n", + " \n", + " \n" + ] + }, + { + "cell_type": "markdown", + "id": "d4cb34c8", + "metadata": { + "editable": true + }, + "source": [ + "## Compiling with and without vectorization\n", + "We can compile and link without vectorization using the clang c++ compiler" + ] + }, + { + "cell_type": "markdown", + "id": "aba109f4", + "metadata": { + "editable": true + }, + "source": [ + " clang -o novec.x vecexample.cpp\n" + ] + }, + { + "cell_type": "markdown", + "id": "b29b64ce", + "metadata": { + "editable": true + }, + "source": [ + "and with vectorization (and additional optimizations)" + ] + }, + { + "cell_type": "markdown", + "id": "ecb51ee3", + "metadata": { + "editable": true + }, + "source": [ + " clang++ -O3 -Rpass=loop-vectorize -o vec.x vecexample.cpp \n" + ] + }, + { + "cell_type": "markdown", + "id": "ef5054ab", + "metadata": { + "editable": true + }, + "source": [ + "The speedup depends on the size of the vectors. In the example here we have run with $10^7$ elements.\n", + "The example here was run on an IMac17.1 with OSX El Capitan (10.11.4) as operating system and an Intel i5 3.3 GHz CPU." + ] + }, + { + "cell_type": "markdown", + "id": "f09ca19c", + "metadata": { + "editable": true + }, + "source": [ + " Compphys:~ hjensen$ ./vec.x 10000000\n", + " Time used for norm computation=0.04720500000\n", + " Compphys:~ hjensen$ ./novec.x 10000000\n", + " Time used for norm computation=0.03311700000\n" + ] + }, + { + "cell_type": "markdown", + "id": "0efe59e0", + "metadata": { + "editable": true + }, + "source": [ + "This particular C++ compiler speeds up the above loop operations with a factor of 1.5 \n", + "Performing the same operations for $10^9$ elements results in a smaller speedup since reading from main memory is required. The non-vectorized code is seemingly faster." + ] + }, + { + "cell_type": "markdown", + "id": "bc05c8e0", + "metadata": { + "editable": true + }, + "source": [ + " Compphys:~ hjensen$ ./vec.x 1000000000\n", + " Time used for norm computation=58.41391100\n", + " Compphys:~ hjensen$ ./novec.x 1000000000\n", + " Time used for norm computation=46.51295300\n" + ] + }, + { + "cell_type": "markdown", + "id": "57fe77fa", + "metadata": { + "editable": true + }, + "source": [ + "We will discuss these issues further in the next slides." + ] + }, + { + "cell_type": "markdown", + "id": "ee0eb549", + "metadata": { + "editable": true + }, + "source": [ + "## Compiling with and without vectorization using clang\n", + "We can compile and link without vectorization with clang compiler" + ] + }, + { + "cell_type": "markdown", + "id": "a9b8a0cb", + "metadata": { + "editable": true + }, + "source": [ + " clang++ -o -fno-vectorize novec.x vecexample.cpp\n" + ] + }, + { + "cell_type": "markdown", + "id": "05e175db", + "metadata": { + "editable": true + }, + "source": [ + "and with vectorization" + ] + }, + { + "cell_type": "markdown", + "id": "f6ecf4e7", + "metadata": { + "editable": true + }, + "source": [ + " clang++ -O3 -Rpass=loop-vectorize -o vec.x vecexample.cpp \n" + ] + }, + { + "cell_type": "markdown", + "id": "c0ee7b75", + "metadata": { + "editable": true + }, + "source": [ + "We can also add vectorization analysis, see for example" + ] + }, + { + "cell_type": "markdown", + "id": "74dcf2f8", + "metadata": { + "editable": true + }, + "source": [ + " clang++ -O3 -Rpass-analysis=loop-vectorize -o vec.x vecexample.cpp \n" + ] + }, + { + "cell_type": "markdown", + "id": "b72d5a50", + "metadata": { + "editable": true + }, + "source": [ + "or figure out if vectorization was missed" + ] + }, + { + "cell_type": "markdown", + "id": "6c936d22", + "metadata": { + "editable": true + }, + "source": [ + " clang++ -O3 -Rpass-missed=loop-vectorize -o vec.x vecexample.cpp \n" + ] + }, + { + "cell_type": "markdown", + "id": "e57b5c9b", + "metadata": { + "editable": true + }, + "source": [ + "## Automatic vectorization and vectorization inhibitors, criteria\n", + "\n", + "Not all loops can be vectorized, as discussed in [Intel's guide to vectorization](https://software.intel.com/en-us/articles/a-guide-to-auto-vectorization-with-intel-c-compilers)\n", + "\n", + "An important criteria is that the loop counter $n$ is known at the entry of the loop." + ] + }, + { + "cell_type": "markdown", + "id": "591ea5f8", + "metadata": { + "editable": true + }, + "source": [ + " for (int j = 0; j < n; j++) {\n", + " a[j] = cos(j*1.0);\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "86e62af7", + "metadata": { + "editable": true + }, + "source": [ + "The variable $n$ does need to be known at compile time. However, this variable must stay the same for the entire duration of the loop. It implies that an exit statement inside the loop cannot be data dependent." + ] + }, + { + "cell_type": "markdown", + "id": "d4eadc28", + "metadata": { + "editable": true + }, + "source": [ + "## Automatic vectorization and vectorization inhibitors, exit criteria\n", + "\n", + "An exit statement should in general be avoided. \n", + "If the exit statement contains data-dependent conditions, the loop cannot be vectorized. \n", + "The following is an example of a non-vectorizable loop" + ] + }, + { + "cell_type": "markdown", + "id": "9fb55622", + "metadata": { + "editable": true + }, + "source": [ + " for (int j = 0; j < n; j++) {\n", + " a[j] = cos(j*1.0);\n", + " if (a[j] < 0 ) break;\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "b4c7cce6", + "metadata": { + "editable": true + }, + "source": [ + "Avoid loop termination conditions and opt for a single entry loop variable $n$. The lower and upper bounds have to be kept fixed within the loop." + ] + }, + { + "cell_type": "markdown", + "id": "63f1d357", + "metadata": { + "editable": true + }, + "source": [ + "## Automatic vectorization and vectorization inhibitors, straight-line code\n", + "\n", + "SIMD instructions perform the same type of operations multiple times. \n", + "A **switch** statement leads thus to a non-vectorizable loop since different statemens cannot branch.\n", + "The following code can however be vectorized since the **if** statement is implemented as a masked assignment." + ] + }, + { + "cell_type": "markdown", + "id": "a485face", + "metadata": { + "editable": true + }, + "source": [ + " for (int j = 0; j < n; j++) {\n", + " double x = cos(j*1.0);\n", + " if (x > 0 ) {\n", + " a[j] = x*sin(j*2.0); \n", + " }\n", + " else {\n", + " a[j] = 0.0;\n", + " }\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "ad55a449", + "metadata": { + "editable": true + }, + "source": [ + "These operations can be performed for all data elements but only those elements which the mask evaluates as true are stored. In general, one should avoid branches such as **switch**, **go to**, or **return** statements or **if** constructs that cannot be treated as masked assignments." + ] + }, + { + "cell_type": "markdown", + "id": "776ddba9", + "metadata": { + "editable": true + }, + "source": [ + "## Automatic vectorization and vectorization inhibitors, nested loops\n", + "\n", + "Only the innermost loop of the following example is vectorized" + ] + }, + { + "cell_type": "markdown", + "id": "ca642f90", + "metadata": { + "editable": true + }, + "source": [ + " for (int i = 0; i < n; i++) {\n", + " for (int j = 0; j < n; j++) {\n", + " a[i][j] += b[i][j];\n", + " } \n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "14123a0d", + "metadata": { + "editable": true + }, + "source": [ + "The exception is if an original outer loop is transformed into an inner loop as the result of compiler optimizations." + ] + }, + { + "cell_type": "markdown", + "id": "3a9e1c79", + "metadata": { + "editable": true + }, + "source": [ + "## Automatic vectorization and vectorization inhibitors, function calls\n", + "\n", + "Calls to programmer defined functions ruin vectorization. However, calls to intrinsic functions like\n", + "$\\sin{x}$, $\\cos{x}$, $\\exp{x}$ etc are allowed since they are normally efficiently vectorized. \n", + "The following example is fully vectorizable" + ] + }, + { + "cell_type": "markdown", + "id": "0224f0d3", + "metadata": { + "editable": true + }, + "source": [ + " for (int i = 0; i < n; i++) {\n", + " a[i] = log10(i)*cos(i);\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "4643b973", + "metadata": { + "editable": true + }, + "source": [ + "Similarly, **inline** functions defined by the programmer, allow for vectorization since the function statements are glued into the actual place where the function is called." + ] + }, + { + "cell_type": "markdown", + "id": "bd6152a6", + "metadata": { + "editable": true + }, + "source": [ + "## Automatic vectorization and vectorization inhibitors, data dependencies\n", + "\n", + "One has to keep in mind that vectorization changes the order of operations inside a loop. A so-called\n", + "read-after-write statement with an explicit flow dependency cannot be vectorized. The following code" + ] + }, + { + "cell_type": "markdown", + "id": "3d86ddd8", + "metadata": { + "editable": true + }, + "source": [ + " double b = 15.;\n", + " for (int i = 1; i < n; i++) {\n", + " a[i] = a[i-1] + b;\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "adb31489", + "metadata": { + "editable": true + }, + "source": [ + "is an example of flow dependency and results in wrong numerical results if vectorized. For a scalar operation, the value $a[i-1]$ computed during the iteration is loaded into the right-hand side and the results are fine. In vector mode however, with a vector length of four, the values $a[0]$, $a[1]$, $a[2]$ and $a[3]$ from the previous loop will be loaded into the right-hand side and produce wrong results. That is, we have" + ] + }, + { + "cell_type": "markdown", + "id": "77d0b5d3", + "metadata": { + "editable": true + }, + "source": [ + " a[1] = a[0] + b;\n", + " a[2] = a[1] + b;\n", + " a[3] = a[2] + b;\n", + " a[4] = a[3] + b;\n" + ] + }, + { + "cell_type": "markdown", + "id": "c31215ca", + "metadata": { + "editable": true + }, + "source": [ + "and if the two first iterations are executed at the same by the SIMD instruction, the value of say $a[1]$ could be used by the second iteration before it has been calculated by the first iteration, leading thereby to wrong results." + ] + }, + { + "cell_type": "markdown", + "id": "46ed8de3", + "metadata": { + "editable": true + }, + "source": [ + "## Automatic vectorization and vectorization inhibitors, more data dependencies\n", + "\n", + "On the other hand, a so-called \n", + "write-after-read statement can be vectorized. The following code" + ] + }, + { + "cell_type": "markdown", + "id": "85a36227", + "metadata": { + "editable": true + }, + "source": [ + " double b = 15.;\n", + " for (int i = 1; i < n; i++) {\n", + " a[i-1] = a[i] + b;\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "8e5714f0", + "metadata": { + "editable": true + }, + "source": [ + "is an example of flow dependency that can be vectorized since no iteration with a higher value of $i$\n", + "can complete before an iteration with a lower value of $i$. However, such code leads to problems with parallelization." + ] + }, + { + "cell_type": "markdown", + "id": "efcc358e", + "metadata": { + "editable": true + }, + "source": [ + "## Automatic vectorization and vectorization inhibitors, memory stride\n", + "\n", + "For C++ programmers it is also worth keeping in mind that an array notation is preferred to the more compact use of pointers to access array elements. The compiler can often not tell if it is safe to vectorize the code. \n", + "\n", + "When dealing with arrays, you should also avoid memory stride, since this slows down considerably vectorization. When you access array element, write for example the inner loop to vectorize using unit stride, that is, access successively the next array element in memory, as shown here" + ] + }, + { + "cell_type": "markdown", + "id": "925846f6", + "metadata": { + "editable": true + }, + "source": [ + " for (int i = 0; i < n; i++) {\n", + " for (int j = 0; j < n; j++) {\n", + " a[i][j] += b[i][j];\n", + " } \n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "30c20cb6", + "metadata": { + "editable": true + }, + "source": [ + "## Memory management\n", + "The main memory contains the program data\n", + "1. Cache memory contains a copy of the main memory data\n", + "\n", + "2. Cache is faster but consumes more space and power. It is normally assumed to be much faster than main memory\n", + "\n", + "3. Registers contain working data only\n", + "\n", + " * Modern CPUs perform most or all operations only on data in register\n", + "\n", + "4. Multiple Cache memories contain a copy of the main memory data\n", + "\n", + " * Cache items accessed by their address in main memory\n", + "\n", + " * L1 cache is the fastest but has the least capacity\n", + "\n", + " * L2, L3 provide intermediate performance/size tradeoffs\n", + "\n", + "Loads and stores to memory can be as important as floating point operations when we measure performance." + ] + }, + { + "cell_type": "markdown", + "id": "40b4b92c", + "metadata": { + "editable": true + }, + "source": [ + "## Memory and communication\n", + "\n", + "1. Most communication in a computer is carried out in chunks, blocks of bytes of data that move together\n", + "\n", + "2. In the memory hierarchy, data moves between memory and cache, and between different levels of cache, in groups called lines\n", + "\n", + " * Lines are typically 64-128 bytes, or 8-16 double precision words\n", + "\n", + " * Even if you do not use the data, it is moved and occupies space in the cache\n", + "\n", + "Many of these performance features are not captured in most programming languages." + ] + }, + { + "cell_type": "markdown", + "id": "da5377a0", + "metadata": { + "editable": true + }, + "source": [ + "## Measuring performance\n", + "\n", + "How do we measure performance? What is wrong with this code to time a loop?" + ] + }, + { + "cell_type": "markdown", + "id": "36b63048", + "metadata": { + "editable": true + }, + "source": [ + " clock_t start, finish;\n", + " start = clock();\n", + " for (int j = 0; j < i; j++) {\n", + " a[j] = b[j]+b[j]*c[j];\n", + " }\n", + " finish = clock();\n", + " double timeused = (double) (finish - start)/(CLOCKS_PER_SEC );\n" + ] + }, + { + "cell_type": "markdown", + "id": "b73861c2", + "metadata": { + "editable": true + }, + "source": [ + "## Problems with measuring time\n", + "1. Timers are not infinitely accurate\n", + "\n", + "2. All clocks have a granularity, the minimum time that they can measure\n", + "\n", + "3. The error in a time measurement, even if everything is perfect, may be the size of this granularity (sometimes called a clock tick)\n", + "\n", + "4. Always know what your clock granularity is\n", + "\n", + "5. Ensure that your measurement is for a long enough duration (say 100 times the **tick**)" + ] + }, + { + "cell_type": "markdown", + "id": "9aec5620", + "metadata": { + "editable": true + }, + "source": [ + "## Problems with cold start\n", + "\n", + "What happens when the code is executed? The assumption is that the code is ready to\n", + "execute. But\n", + "1. Code may still be on disk, and not even read into memory.\n", + "\n", + "2. Data may be in slow memory rather than fast (which may be wrong or right for what you are measuring)\n", + "\n", + "3. Multiple tests often necessary to ensure that cold start effects are not present\n", + "\n", + "4. Special effort often required to ensure data in the intended part of the memory hierarchy." + ] + }, + { + "cell_type": "markdown", + "id": "f9313bb0", + "metadata": { + "editable": true + }, + "source": [ + "## Problems with smart compilers\n", + "\n", + "1. If the result of the computation is not used, the compiler may eliminate the code\n", + "\n", + "2. Performance will look impossibly fantastic\n", + "\n", + "3. Even worse, eliminate some of the code so the performance looks plausible\n", + "\n", + "4. Ensure that the results are (or may be) used." + ] + }, + { + "cell_type": "markdown", + "id": "7a2037ca", + "metadata": { + "editable": true + }, + "source": [ + "## Problems with interference\n", + "1. Other activities are sharing your processor\n", + "\n", + " * Operating system, system demons, other users\n", + "\n", + " * Some parts of the hardware do not always perform with exactly the same performance\n", + "\n", + "2. Make multiple tests and report\n", + "\n", + "3. Easy choices include\n", + "\n", + " * Average tests represent what users might observe over time" + ] + }, + { + "cell_type": "markdown", + "id": "2e1caede", + "metadata": { + "editable": true + }, + "source": [ + "## Problems with measuring performance\n", + "1. Accurate, reproducible performance measurement is hard\n", + "\n", + "2. Think carefully about your experiment:\n", + "\n", + "3. What is it, precisely, that you want to measure?\n", + "\n", + "4. How representative is your test to the situation that you are trying to measure?" + ] + }, + { + "cell_type": "markdown", + "id": "08ecf53f", + "metadata": { + "editable": true + }, + "source": [ + "## Thomas algorithm for tridiagonal linear algebra equations" + ] + }, + { + "cell_type": "markdown", + "id": "1d4b1934", + "metadata": { + "editable": true + }, + "source": [ + "$$\n", + "\\left( \\begin{array}{ccccc}\n", + " b_0 & c_0 & & & \\\\\n", + "\ta_0 & b_1 & c_1 & & \\\\\n", + "\t & & \\ddots & & \\\\\n", + "\t &\t & a_{m-3} & b_{m-2} & c_{m-2} \\\\\n", + "\t & & & a_{m-2} & b_{m-1}\n", + " \\end{array} \\right)\n", + "\\left( \\begin{array}{c}\n", + " x_0 \\\\\n", + " x_1 \\\\\n", + " \\vdots \\\\\n", + " x_{m-2} \\\\\n", + " x_{m-1}\n", + " \\end{array} \\right)=\\left( \\begin{array}{c}\n", + " f_0 \\\\\n", + " f_1 \\\\\n", + " \\vdots \\\\\n", + " f_{m-2} \\\\\n", + " f_{m-1} \\\\\n", + " \\end{array} \\right)\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "id": "ea777020", + "metadata": { + "editable": true + }, + "source": [ + "## Thomas algorithm, forward substitution\n", + "The first step is to multiply the first row by $a_0/b_0$ and subtract it from the second row. This is known as the forward substitution step. We obtain then" + ] + }, + { + "cell_type": "markdown", + "id": "41c4860a", + "metadata": { + "editable": true + }, + "source": [ + "$$\n", + "a_i = 0,\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "id": "d3d46adc", + "metadata": { + "editable": true + }, + "source": [ + "$$\n", + "b_i = b_i - \\frac{a_{i-1}}{b_{i-1}}c_{i-1},\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "id": "75b8a579", + "metadata": { + "editable": true + }, + "source": [ + "and" + ] + }, + { + "cell_type": "markdown", + "id": "d554b123", + "metadata": { + "editable": true + }, + "source": [ + "$$\n", + "f_i = f_i - \\frac{a_{i-1}}{b_{i-1}}f_{i-1}.\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "id": "936fd39c", + "metadata": { + "editable": true + }, + "source": [ + "At this point the simplified equation, with only an upper triangular matrix takes the form" + ] + }, + { + "cell_type": "markdown", + "id": "a862d627", + "metadata": { + "editable": true + }, + "source": [ + "$$\n", + "\\left( \\begin{array}{ccccc}\n", + " b_0 & c_0 & & & \\\\\n", + " & b_1 & c_1 & & \\\\\n", + " & & \\ddots & & \\\\\n", + "\t & & & b_{m-2} & c_{m-2} \\\\\n", + "\t & & & & b_{m-1}\n", + " \\end{array} \\right)\\left( \\begin{array}{c}\n", + " x_0 \\\\\n", + " x_1 \\\\\n", + " \\vdots \\\\\n", + " x_{m-2} \\\\\n", + " x_{m-1}\n", + " \\end{array} \\right)=\\left( \\begin{array}{c}\n", + " f_0 \\\\\n", + " f_1 \\\\\n", + " \\vdots \\\\\n", + " f_{m-2} \\\\\n", + " f_{m-1} \\\\\n", + " \\end{array} \\right)\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "id": "5684ece5", + "metadata": { + "editable": true + }, + "source": [ + "## Thomas algorithm, backward substitution\n", + "The next step is the backward substitution step. The last row is multiplied by $c_{N-3}/b_{N-2}$ and subtracted from the second to last row, thus eliminating $c_{N-3}$ from the last row. The general backward substitution procedure is" + ] + }, + { + "cell_type": "markdown", + "id": "ef75d9c4", + "metadata": { + "editable": true + }, + "source": [ + "$$\n", + "c_i = 0,\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "id": "9cce31b6", + "metadata": { + "editable": true + }, + "source": [ + "and" + ] + }, + { + "cell_type": "markdown", + "id": "f9769935", + "metadata": { + "editable": true + }, + "source": [ + "$$\n", + "f_{i-1} = f_{i-1} - \\frac{c_{i-1}}{b_i}f_i\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "id": "5f461a3e", + "metadata": { + "editable": true + }, + "source": [ + "All that ramains to be computed is the solution, which is the very straight forward process of" + ] + }, + { + "cell_type": "markdown", + "id": "3cdbaea1", + "metadata": { + "editable": true + }, + "source": [ + "$$\n", + "x_i = \\frac{f_i}{b_i}\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "id": "23e1510f", + "metadata": { + "editable": true + }, + "source": [ + "## Thomas algorithm and counting of operations (floating point and memory)\n", + "\n", + "We have in specific case the following operations with the floating operations\n", + "\n", + "* Memory Reads: $14(N-2)$;\n", + "\n", + "* Memory Writes: $4(N-2)$; \n", + "\n", + "* Subtractions: $3(N-2)$; \n", + "\n", + "* Multiplications: $3(N-2)$;\n", + "\n", + "* Divisions: $4(N-2)$." + ] + }, + { + "cell_type": "markdown", + "id": "0856ac03", + "metadata": { + "editable": true + }, + "source": [ + " // Forward substitution \n", + " // Note that we can simplify by precalculating a[i-1]/b[i-1]\n", + " for (int i=1; i < n; i++) {\n", + " b[i] = b[i] - (a[i-1]*c[i-1])/b[i-1];\n", + " f[i] = g[i] - (a[i-1]*f[i-1])/b[i-1];\n", + " }\n", + " x[n-1] = f[n-1] / b[n-1];\n", + " // Backwards substitution \n", + " for (int i = n-2; i >= 0; i--) {\n", + " f[i] = f[i] - c[i]*f[i+1]/b[i+1];\n", + " x[i] = f[i]/b[i];\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "454ee61c", + "metadata": { + "editable": true + }, + "source": [ + "## [Example: Transpose of a matrix](https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program8.cpp)" + ] + }, + { + "cell_type": "markdown", + "id": "e44cdc6b", + "metadata": { + "editable": true + }, + "source": [ + " #include \n", + " #include \n", + " #include \n", + " #include \n", + " #include \"time.h\"\n", + " \n", + " using namespace std; // note use of namespace\n", + " int main (int argc, char* argv[])\n", + " {\n", + " // read in dimension of square matrix\n", + " int n = atoi(argv[1]);\n", + " double **A, **B;\n", + " // Allocate space for the two matrices\n", + " A = new double*[n]; B = new double*[n];\n", + " for (int i = 0; i < n; i++){\n", + " A[i] = new double[n];\n", + " B[i] = new double[n];\n", + " }\n", + " // Set up values for matrix A\n", + " for (int i = 0; i < n; i++){\n", + " for (int j = 0; j < n; j++) {\n", + " A[i][j] = cos(i*1.0)*sin(j*3.0);\n", + " }\n", + " }\n", + " clock_t start, finish;\n", + " start = clock();\n", + " // Then compute the transpose\n", + " for (int i = 0; i < n; i++){\n", + " for (int j = 0; j < n; j++) {\n", + " B[i][j]= A[j][i];\n", + " }\n", + " }\n", + " \n", + " finish = clock();\n", + " double timeused = (double) (finish - start)/(CLOCKS_PER_SEC );\n", + " cout << setiosflags(ios::showpoint | ios::uppercase);\n", + " cout << setprecision(10) << setw(20) << \"Time used for setting up transpose of matrix=\" << timeused << endl;\n", + " \n", + " // Free up space\n", + " for (int i = 0; i < n; i++){\n", + " delete[] A[i];\n", + " delete[] B[i];\n", + " }\n", + " delete[] A;\n", + " delete[] B;\n", + " return 0;\n", + " }\n", + " \n" + ] + }, + { + "cell_type": "markdown", + "id": "a9959a86", + "metadata": { + "editable": true + }, + "source": [ + "## [Matrix-matrix multiplication](https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/LecturePrograms/programs/Classes/cpp/program9.cpp)\n", + "This the matrix-matrix multiplication code with plain c++ memory allocation. It computes at the end the Frobenius norm." + ] + }, + { + "cell_type": "markdown", + "id": "da017900", + "metadata": { + "editable": true + }, + "source": [ + " #include \n", + " #include \n", + " #include \n", + " #include \n", + " #include \"time.h\"\n", + " \n", + " using namespace std; // note use of namespace\n", + " int main (int argc, char* argv[])\n", + " {\n", + " // read in dimension of square matrix\n", + " int n = atoi(argv[1]);\n", + " double s = 1.0/sqrt( (double) n);\n", + " double **A, **B, **C;\n", + " // Start timing\n", + " clock_t start, finish;\n", + " start = clock();\n", + " // Allocate space for the two matrices\n", + " A = new double*[n]; B = new double*[n]; C = new double*[n];\n", + " for (int i = 0; i < n; i++){\n", + " A[i] = new double[n];\n", + " B[i] = new double[n];\n", + " C[i] = new double[n];\n", + " }\n", + " // Set up values for matrix A and B and zero matrix C\n", + " for (int i = 0; i < n; i++){\n", + " for (int j = 0; j < n; j++) {\n", + " double angle = 2.0*M_PI*i*j/ (( double ) n);\n", + " A[i][j] = s * ( sin ( angle ) + cos ( angle ) );\n", + " B[j][i] = A[i][j];\n", + " }\n", + " }\n", + " // Then perform the matrix-matrix multiplication\n", + " for (int i = 0; i < n; i++){\n", + " for (int j = 0; j < n; j++) {\n", + " double sum = 0.0;\n", + " for (int k = 0; k < n; k++) {\n", + " sum += B[i][k]*A[k][j];\n", + " }\n", + " C[i][j] = sum;\n", + " }\n", + " }\n", + " // Compute now the Frobenius norm\n", + " double Fsum = 0.0;\n", + " for (int i = 0; i < n; i++){\n", + " for (int j = 0; j < n; j++) {\n", + " Fsum += C[i][j]*C[i][j];\n", + " }\n", + " }\n", + " Fsum = sqrt(Fsum);\n", + " finish = clock();\n", + " double timeused = (double) (finish - start)/(CLOCKS_PER_SEC );\n", + " cout << setiosflags(ios::showpoint | ios::uppercase);\n", + " cout << setprecision(10) << setw(20) << \"Time used for matrix-matrix multiplication=\" << timeused << endl;\n", + " cout << \" Frobenius norm = \" << Fsum << endl;\n", + " // Free up space\n", + " for (int i = 0; i < n; i++){\n", + " delete[] A[i];\n", + " delete[] B[i];\n", + " delete[] C[i];\n", + " }\n", + " delete[] A;\n", + " delete[] B;\n", + " delete[] C;\n", + " return 0;\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "8cc611e7", + "metadata": { + "editable": true + }, + "source": [ + "## How do we define speedup? Simplest form\n", + "* Speedup measures the ratio of performance between two objects\n", + "\n", + "* Versions of same code, with different number of processors\n", + "\n", + "* Serial and vector versions\n", + "\n", + "* Try different programing languages, c++ and Fortran\n", + "\n", + "* Two algorithms computing the **same** result" + ] + }, + { + "cell_type": "markdown", + "id": "6f8e7fda", + "metadata": { + "editable": true + }, + "source": [ + "## How do we define speedup? Correct baseline\n", + "The key is choosing the correct baseline for comparison\n", + "* For our serial vs. vectorization examples, using compiler-provided vectorization, the baseline is simple; the same code, with vectorization turned off\n", + "\n", + " * For parallel applications, this is much harder:\n", + "\n", + " * Choice of algorithm, decomposition, performance of baseline case etc." + ] + }, + { + "cell_type": "markdown", + "id": "fc160e54", + "metadata": { + "editable": true + }, + "source": [ + "## Parallel speedup\n", + "For parallel applications, speedup is typically defined as\n", + "* Speedup $=T_1/T_p$\n", + "\n", + "Here $T_1$ is the time on one processor and $T_p$ is the time using $p$ processors.\n", + " * Can the speedup become larger than $p$? That means using $p$ processors is more than $p$ times faster than using one processor." + ] + }, + { + "cell_type": "markdown", + "id": "35d4c6fc", + "metadata": { + "editable": true + }, + "source": [ + "## Speedup and memory\n", + "The speedup on $p$ processors can\n", + "be greater than $p$ if memory usage is optimal!\n", + "Consider the case of a memorybound computation with $M$ words of memory\n", + " * If $M/p$ fits into cache while $M$ does not, the time to access memory will be different in the two cases:\n", + "\n", + " * $T_1$ uses the main memory bandwidth\n", + "\n", + " * $T_p$ uses the appropriate cache bandwidth" + ] + }, + { + "cell_type": "markdown", + "id": "42361424", + "metadata": { + "editable": true + }, + "source": [ + "## Upper bounds on speedup\n", + "Assume that almost all parts of a code are perfectly\n", + "parallelizable (fraction $f$). The remainder,\n", + "fraction $(1-f)$ cannot be parallelized at all.\n", + "\n", + "That is, there is work that takes time $W$ on one process; a fraction $f$ of that work will take\n", + "time $Wf/p$ on $p$ processors. \n", + "* What is the maximum possible speedup as a function of $f$?" + ] + }, + { + "cell_type": "markdown", + "id": "1aaeb684", + "metadata": { + "editable": true + }, + "source": [ + "## Amdahl's law\n", + "On one processor we have" + ] + }, + { + "cell_type": "markdown", + "id": "b81bb408", + "metadata": { + "editable": true + }, + "source": [ + "$$\n", + "T_1 = (1-f)W + fW = W\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "id": "b0311044", + "metadata": { + "editable": true + }, + "source": [ + "On $p$ processors we have" + ] + }, + { + "cell_type": "markdown", + "id": "5804b8d3", + "metadata": { + "editable": true + }, + "source": [ + "$$\n", + "T_p = (1-f)W + \\frac{fW}{p},\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "id": "ee810179", + "metadata": { + "editable": true + }, + "source": [ + "resulting in a speedup of" + ] + }, + { + "cell_type": "markdown", + "id": "ec2840b5", + "metadata": { + "editable": true + }, + "source": [ + "$$\n", + "\\frac{T_1}{T_p} = \\frac{W}{(1-f)W+fW/p}\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "id": "ae2e230a", + "metadata": { + "editable": true + }, + "source": [ + "As $p$ goes to infinity, $fW/p$ goes to zero, and the maximum speedup is" + ] + }, + { + "cell_type": "markdown", + "id": "d67d97e1", + "metadata": { + "editable": true + }, + "source": [ + "$$\n", + "\\frac{1}{1-f},\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "id": "1525f768", + "metadata": { + "editable": true + }, + "source": [ + "meaning that if \n", + "if $f = 0.99$ (all but $1\\%$ parallelizable), the maximum speedup\n", + "is $1/(1-.99)=100$!" + ] + }, + { + "cell_type": "markdown", + "id": "3e5a0366", + "metadata": { + "editable": true + }, + "source": [ + "## How much is parallelizable\n", + "If any non-parallel code slips into the\n", + "application, the parallel\n", + "performance is limited. \n", + "\n", + "In many simulations, however, the fraction of non-parallelizable work\n", + "is $10^{-6}$ or less due to large arrays or objects that are perfectly parallelizable." + ] + }, + { + "cell_type": "markdown", + "id": "0a3d70dd", + "metadata": { + "editable": true + }, + "source": [ + "## Today's situation of parallel computing\n", + "\n", + "* Distributed memory is the dominant hardware configuration. There is a large diversity in these machines, from MPP (massively parallel processing) systems to clusters of off-the-shelf PCs, which are very cost-effective.\n", + "\n", + "* Message-passing is a mature programming paradigm and widely accepted. It often provides an efficient match to the hardware. It is primarily used for the distributed memory systems, but can also be used on shared memory systems.\n", + "\n", + "* Modern nodes have nowadays several cores, which makes it interesting to use both shared memory (the given node) and distributed memory (several nodes with communication). This leads often to codes which use both MPI and OpenMP.\n", + "\n", + "Our lectures will focus on both MPI and OpenMP." + ] + }, + { + "cell_type": "markdown", + "id": "3693a713", + "metadata": { + "editable": true + }, + "source": [ + "## Overhead present in parallel computing\n", + "\n", + "* **Uneven load balance**: not all the processors can perform useful work at all time.\n", + "\n", + "* **Overhead of synchronization**\n", + "\n", + "* **Overhead of communication**\n", + "\n", + "* **Extra computation due to parallelization**\n", + "\n", + "Due to the above overhead and that certain parts of a sequential\n", + "algorithm cannot be parallelized we may not achieve an optimal parallelization." + ] + }, + { + "cell_type": "markdown", + "id": "5a5ccdc1", + "metadata": { + "editable": true + }, + "source": [ + "## Parallelizing a sequential algorithm\n", + "\n", + "* Identify the part(s) of a sequential algorithm that can be executed in parallel. This is the difficult part,\n", + "\n", + "* Distribute the global work and data among $P$ processors." + ] + }, + { + "cell_type": "markdown", + "id": "c59cac43", + "metadata": { + "editable": true + }, + "source": [ + "## Strategies\n", + "* Develop codes locally, run with some few processes and test your codes. Do benchmarking, timing and so forth on local nodes, for example your laptop or PC. \n", + "\n", + "* When you are convinced that your codes run correctly, you can start your production runs on available supercomputers." + ] + }, + { + "cell_type": "markdown", + "id": "4f099832", + "metadata": { + "editable": true + }, + "source": [ + "## How do I run MPI on a PC/Laptop? MPI\n", + "To install MPI is rather easy on hardware running unix/linux as operating systems, follow simply the instructions from the [OpenMPI website](https://www.open-mpi.org/). See also subsequent slides.\n", + "When you have made sure you have installed MPI on your PC/laptop, \n", + "* Compile with mpicxx/mpic++ or mpif90" + ] + }, + { + "cell_type": "markdown", + "id": "f34cc9cb", + "metadata": { + "editable": true + }, + "source": [ + " # Compile and link\n", + " mpic++ -O3 -o nameofprog.x nameofprog.cpp\n", + " # run code with for example 8 processes using mpirun/mpiexec\n", + " mpiexec -n 8 ./nameofprog.x\n" + ] + }, + { + "cell_type": "markdown", + "id": "991094cb", + "metadata": { + "editable": true + }, + "source": [ + "## Can I do it on my own PC/laptop? OpenMP installation\n", + "If you wish to install MPI and OpenMP \n", + "on your laptop/PC, we recommend the following:\n", + "\n", + "* For OpenMP, the compile option **-fopenmp** is included automatically in recent versions of the C++ compiler and Fortran compilers. For users of different Linux distributions, simply use the available C++ or Fortran compilers and add the above compiler instructions, see also code examples below.\n", + "\n", + "* For OS X users however, install **libomp**" + ] + }, + { + "cell_type": "markdown", + "id": "68e14aec", + "metadata": { + "editable": true + }, + "source": [ + " brew install libomp\n" + ] + }, + { + "cell_type": "markdown", + "id": "947df6e7", + "metadata": { + "editable": true + }, + "source": [ + "and compile and link as" + ] + }, + { + "cell_type": "markdown", + "id": "3b580a58", + "metadata": { + "editable": true + }, + "source": [ + " c++ -o -lomp\n" + ] + }, + { + "cell_type": "markdown", + "id": "cf57172d", + "metadata": { + "editable": true + }, + "source": [ + "## Installing MPI\n", + "For linux/ubuntu users, you need to install two packages (alternatively use the synaptic package manager)" + ] + }, + { + "cell_type": "markdown", + "id": "8954eb04", + "metadata": { + "editable": true + }, + "source": [ + " sudo apt-get install libopenmpi-dev\n", + " sudo apt-get install openmpi-bin\n" + ] + }, + { + "cell_type": "markdown", + "id": "4671e326", + "metadata": { + "editable": true + }, + "source": [ + "For OS X users, install brew (after having installed xcode and gcc, needed for the \n", + "gfortran compiler of openmpi) and then install with brew" + ] + }, + { + "cell_type": "markdown", + "id": "b08a52e8", + "metadata": { + "editable": true + }, + "source": [ + " brew install openmpi\n" + ] + }, + { + "cell_type": "markdown", + "id": "a861537e", + "metadata": { + "editable": true + }, + "source": [ + "When running an executable (code.x), run as" + ] + }, + { + "cell_type": "markdown", + "id": "d4185b13", + "metadata": { + "editable": true + }, + "source": [ + " mpirun -n 10 ./code.x\n" + ] + }, + { + "cell_type": "markdown", + "id": "fdbf1c9f", + "metadata": { + "editable": true + }, + "source": [ + "where we indicate that we want the number of processes to be 10." + ] + }, + { + "cell_type": "markdown", + "id": "4fa81e38", + "metadata": { + "editable": true + }, + "source": [ + "## Installing MPI and using Qt\n", + "With openmpi installed, when using Qt, add to your .pro file the instructions [here](http://dragly.org/2012/03/14/developing-mpi-applications-in-qt-creator/)\n", + "\n", + "You may need to tell Qt where openmpi is stored." + ] + }, + { + "cell_type": "markdown", + "id": "5fda1f95", + "metadata": { + "editable": true + }, + "source": [ + "## What is Message Passing Interface (MPI)?\n", + "\n", + "**MPI** is a library, not a language. It specifies the names, calling sequences and results of functions\n", + "or subroutines to be called from C/C++ or Fortran programs, and the classes and methods that make up the MPI C++\n", + "library. The programs that users write in Fortran, C or C++ are compiled with ordinary compilers and linked\n", + "with the MPI library.\n", + "\n", + "MPI programs should be able to run\n", + "on all possible machines and run all MPI implementetations without change.\n", + "\n", + "An MPI computation is a collection of processes communicating with messages." + ] + }, + { + "cell_type": "markdown", + "id": "5a56b5b5", + "metadata": { + "editable": true + }, + "source": [ + "## Going Parallel with MPI\n", + "**Task parallelism**: the work of a global problem can be divided\n", + "into a number of independent tasks, which rarely need to synchronize. \n", + "Monte Carlo simulations or numerical integration are examples of this.\n", + "\n", + "MPI is a message-passing library where all the routines\n", + "have corresponding C/C++-binding" + ] + }, + { + "cell_type": "markdown", + "id": "e9bda343", + "metadata": { + "editable": true + }, + "source": [ + " MPI_Command_name\n" + ] + }, + { + "cell_type": "markdown", + "id": "db0d73df", + "metadata": { + "editable": true + }, + "source": [ + "and Fortran-binding (routine names are in uppercase, but can also be in lower case)" + ] + }, + { + "cell_type": "markdown", + "id": "f3a3c4b5", + "metadata": { + "editable": true + }, + "source": [ + " MPI_COMMAND_NAME\n" + ] + }, + { + "cell_type": "markdown", + "id": "373dc478", + "metadata": { + "editable": true + }, + "source": [ + "## MPI is a library\n", + "MPI is a library specification for the message passing interface,\n", + "proposed as a standard.\n", + "\n", + "* independent of hardware;\n", + "\n", + "* not a language or compiler specification;\n", + "\n", + "* not a specific implementation or product.\n", + "\n", + "A message passing standard for portability and ease-of-use. \n", + "Designed for high performance.\n", + "\n", + "Insert communication and synchronization functions where necessary." + ] + }, + { + "cell_type": "markdown", + "id": "6958b4e5", + "metadata": { + "editable": true + }, + "source": [ + "## Bindings to MPI routines\n", + "\n", + "MPI is a message-passing library where all the routines\n", + "have corresponding C/C++-binding" + ] + }, + { + "cell_type": "markdown", + "id": "b33344da", + "metadata": { + "editable": true + }, + "source": [ + " MPI_Command_name\n" + ] + }, + { + "cell_type": "markdown", + "id": "fdeedc35", + "metadata": { + "editable": true + }, + "source": [ + "and Fortran-binding (routine names are in uppercase, but can also be in lower case)" + ] + }, + { + "cell_type": "markdown", + "id": "eee795e9", + "metadata": { + "editable": true + }, + "source": [ + " MPI_COMMAND_NAME\n" + ] + }, + { + "cell_type": "markdown", + "id": "ad0bbfc5", + "metadata": { + "editable": true + }, + "source": [ + "The discussion in these slides focuses on the C++ binding." + ] + }, + { + "cell_type": "markdown", + "id": "6a84fe02", + "metadata": { + "editable": true + }, + "source": [ + "## Communicator\n", + "* A group of MPI processes with a name (context).\n", + "\n", + "* Any process is identified by its rank. The rank is only meaningful within a particular communicator.\n", + "\n", + "* By default the communicator contains all the MPI processes." + ] + }, + { + "cell_type": "markdown", + "id": "71b44913", + "metadata": { + "editable": true + }, + "source": [ + " MPI_COMM_WORLD \n" + ] + }, + { + "cell_type": "markdown", + "id": "042775ec", + "metadata": { + "editable": true + }, + "source": [ + "* Mechanism to identify subset of processes.\n", + "\n", + "* Promotes modular design of parallel libraries." + ] + }, + { + "cell_type": "markdown", + "id": "03b93bde", + "metadata": { + "editable": true + }, + "source": [ + "## Some of the most important MPI functions\n", + "\n", + "* $MPI\\_Init$ - initiate an MPI computation\n", + "\n", + "* $MPI\\_Finalize$ - terminate the MPI computation and clean up\n", + "\n", + "* $MPI\\_Comm\\_size$ - how many processes participate in a given MPI communicator?\n", + "\n", + "* $MPI\\_Comm\\_rank$ - which one am I? (A number between 0 and size-1.)\n", + "\n", + "* $MPI\\_Send$ - send a message to a particular process within an MPI communicator\n", + "\n", + "* $MPI\\_Recv$ - receive a message from a particular process within an MPI communicator\n", + "\n", + "* $MPI\\_reduce$ or $MPI\\_Allreduce$, send and receive messages" + ] + }, + { + "cell_type": "markdown", + "id": "8f65b101", + "metadata": { + "editable": true + }, + "source": [ + "## [The first MPI C/C++ program](https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program2.cpp)\n", + "\n", + "Let every process write \"Hello world\" (oh not this program again!!) on the standard output." + ] + }, + { + "cell_type": "markdown", + "id": "4d44f32f", + "metadata": { + "editable": true + }, + "source": [ + " using namespace std;\n", + " #include \n", + " #include \n", + " int main (int nargs, char* args[])\n", + " {\n", + " int numprocs, my_rank;\n", + " // MPI initializations\n", + " MPI_Init (&nargs, &args);\n", + " MPI_Comm_size (MPI_COMM_WORLD, &numprocs);\n", + " MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);\n", + " cout << \"Hello world, I have rank \" << my_rank << \" out of \" \n", + " << numprocs << endl;\n", + " // End MPI\n", + " MPI_Finalize ();\n" + ] + }, + { + "cell_type": "markdown", + "id": "f5fca32b", + "metadata": { + "editable": true + }, + "source": [ + "## The Fortran program" + ] + }, + { + "cell_type": "markdown", + "id": "6e7dbd2c", + "metadata": { + "editable": true + }, + "source": [ + " PROGRAM hello\n", + " INCLUDE \"mpif.h\"\n", + " INTEGER:: size, my_rank, ierr\n", + " \n", + " CALL MPI_INIT(ierr)\n", + " CALL MPI_COMM_SIZE(MPI_COMM_WORLD, size, ierr)\n", + " CALL MPI_COMM_RANK(MPI_COMM_WORLD, my_rank, ierr)\n", + " WRITE(*,*)\"Hello world, I've rank \",my_rank,\" out of \",size\n", + " CALL MPI_FINALIZE(ierr)\n", + " \n", + " END PROGRAM hello\n" + ] + }, + { + "cell_type": "markdown", + "id": "5c0f2635", + "metadata": { + "editable": true + }, + "source": [ + "## Note 1\n", + "\n", + "* The output to screen is not ordered since all processes are trying to write to screen simultaneously.\n", + "\n", + "* It is the operating system which opts for an ordering. \n", + "\n", + "* If we wish to have an organized output, starting from the first process, we may rewrite our program as in the next example." + ] + }, + { + "cell_type": "markdown", + "id": "61669cf2", + "metadata": { + "editable": true + }, + "source": [ + "## [Ordered output with MPIBarrier](https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program3.cpp)" + ] + }, + { + "cell_type": "markdown", + "id": "b067e83a", + "metadata": { + "editable": true + }, + "source": [ + " int main (int nargs, char* args[])\n", + " {\n", + " int numprocs, my_rank, i;\n", + " MPI_Init (&nargs, &args);\n", + " MPI_Comm_size (MPI_COMM_WORLD, &numprocs);\n", + " MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);\n", + " for (i = 0; i < numprocs; i++) {}\n", + " MPI_Barrier (MPI_COMM_WORLD);\n", + " if (i == my_rank) {\n", + " cout << \"Hello world, I have rank \" << my_rank << \n", + " \" out of \" << numprocs << endl;}\n", + " MPI_Finalize ();\n" + ] + }, + { + "cell_type": "markdown", + "id": "76158876", + "metadata": { + "editable": true + }, + "source": [ + "## Note 2\n", + "* Here we have used the $MPI\\_Barrier$ function to ensure that that every process has completed its set of instructions in a particular order.\n", + "\n", + "* A barrier is a special collective operation that does not allow the processes to continue until all processes in the communicator (here $MPI\\_COMM\\_WORLD$) have called $MPI\\_Barrier$. \n", + "\n", + "* The barriers make sure that all processes have reached the same point in the code. Many of the collective operations like $MPI\\_ALLREDUCE$ to be discussed later, have the same property; that is, no process can exit the operation until all processes have started. \n", + "\n", + "However, this is slightly more time-consuming since the processes synchronize between themselves as many times as there\n", + "are processes. In the next Hello world example we use the send and receive functions in order to a have a synchronized\n", + "action." + ] + }, + { + "cell_type": "markdown", + "id": "f50ed3d1", + "metadata": { + "editable": true + }, + "source": [ + "## [Ordered output](https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program4.cpp)" + ] + }, + { + "cell_type": "markdown", + "id": "b548728b", + "metadata": { + "editable": true + }, + "source": [ + " .....\n", + " int numprocs, my_rank, flag;\n", + " MPI_Status status;\n", + " MPI_Init (&nargs, &args);\n", + " MPI_Comm_size (MPI_COMM_WORLD, &numprocs);\n", + " MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);\n", + " if (my_rank > 0)\n", + " MPI_Recv (&flag, 1, MPI_INT, my_rank-1, 100, \n", + " MPI_COMM_WORLD, &status);\n", + " cout << \"Hello world, I have rank \" << my_rank << \" out of \" \n", + " << numprocs << endl;\n", + " if (my_rank < numprocs-1)\n", + " MPI_Send (&my_rank, 1, MPI_INT, my_rank+1, \n", + " 100, MPI_COMM_WORLD);\n", + " MPI_Finalize ();\n" + ] + }, + { + "cell_type": "markdown", + "id": "8085bb44", + "metadata": { + "editable": true + }, + "source": [ + "## Note 3\n", + "\n", + "The basic sending of messages is given by the function $MPI\\_SEND$, which in C/C++\n", + "is defined as" + ] + }, + { + "cell_type": "markdown", + "id": "20b2a3b9", + "metadata": { + "editable": true + }, + "source": [ + " int MPI_Send(void *buf, int count, \n", + " MPI_Datatype datatype, \n", + " int dest, int tag, MPI_Comm comm)}\n" + ] + }, + { + "cell_type": "markdown", + "id": "04cce86a", + "metadata": { + "editable": true + }, + "source": [ + "This single command allows the passing of any kind of variable, even a large array, to any group of tasks. \n", + "The variable **buf** is the variable we wish to send while **count**\n", + "is the number of variables we are passing. If we are passing only a single value, this should be 1. \n", + "\n", + "If we transfer an array, it is the overall size of the array. \n", + "For example, if we want to send a 10 by 10 array, count would be $10\\times 10=100$ \n", + "since we are actually passing 100 values." + ] + }, + { + "cell_type": "markdown", + "id": "158cd5e2", + "metadata": { + "editable": true + }, + "source": [ + "## Note 4\n", + "\n", + "Once you have sent a message, you must receive it on another task. The function $MPI\\_RECV$\n", + "is similar to the send call." + ] + }, + { + "cell_type": "markdown", + "id": "10926207", + "metadata": { + "editable": true + }, + "source": [ + " int MPI_Recv( void *buf, int count, MPI_Datatype datatype, \n", + " int source, \n", + " int tag, MPI_Comm comm, MPI_Status *status )\n" + ] + }, + { + "cell_type": "markdown", + "id": "d0dece69", + "metadata": { + "editable": true + }, + "source": [ + "The arguments that are different from those in MPI\\_SEND are\n", + "**buf** which is the name of the variable where you will be storing the received data, \n", + "**source** which replaces the destination in the send command. This is the return ID of the sender.\n", + "\n", + "Finally, we have used $MPI\\_Status\\_status$, \n", + "where one can check if the receive was completed.\n", + "\n", + "The output of this code is the same as the previous example, but now\n", + "process 0 sends a message to process 1, which forwards it further\n", + "to process 2, and so forth." + ] + }, + { + "cell_type": "markdown", + "id": "3d053e2f", + "metadata": { + "editable": true + }, + "source": [ + "## [Numerical integration in parallel](https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp)\n", + "**Integrating $\\pi$.**\n", + "\n", + "* The code example computes $\\pi$ using the trapezoidal rules.\n", + "\n", + "* The trapezoidal rule" + ] + }, + { + "cell_type": "markdown", + "id": "9668b407", + "metadata": { + "editable": true + }, + "source": [ + "$$\n", + "I=\\int_a^bf(x) dx\\approx h\\left(f(a)/2 + f(a+h) +f(a+2h)+\\dots +f(b-h)+ f(b)/2\\right).\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "id": "12eb35b9", + "metadata": { + "editable": true + }, + "source": [ + "Click [on this link](https://github.com/CompPhysics/ComputationalPhysics2/blob/gh-pages/doc/Programs/LecturePrograms/programs/MPI/chapter07/program6.cpp) for the full program." + ] + }, + { + "cell_type": "markdown", + "id": "02f90f49", + "metadata": { + "editable": true + }, + "source": [ + "## Dissection of trapezoidal rule with $MPI\\_reduce$" + ] + }, + { + "cell_type": "markdown", + "id": "24fb6f69", + "metadata": { + "editable": true + }, + "source": [ + " // Trapezoidal rule and numerical integration usign MPI\n", + " using namespace std;\n", + " #include \n", + " #include \n", + " \n", + " // Here we define various functions called by the main program\n", + " \n", + " double int_function(double );\n", + " double trapezoidal_rule(double , double , int , double (*)(double));\n", + " \n", + " // Main function begins here\n", + " int main (int nargs, char* args[])\n", + " {\n", + " int n, local_n, numprocs, my_rank; \n", + " double a, b, h, local_a, local_b, total_sum, local_sum; \n", + " double time_start, time_end, total_time;\n" + ] + }, + { + "cell_type": "markdown", + "id": "1eda1f15", + "metadata": { + "editable": true + }, + "source": [ + "## Dissection of trapezoidal rule" + ] + }, + { + "cell_type": "markdown", + "id": "62c86791", + "metadata": { + "editable": true + }, + "source": [ + " // MPI initializations\n", + " MPI_Init (&nargs, &args);\n", + " MPI_Comm_size (MPI_COMM_WORLD, &numprocs);\n", + " MPI_Comm_rank (MPI_COMM_WORLD, &my_rank);\n", + " time_start = MPI_Wtime();\n", + " // Fixed values for a, b and n \n", + " a = 0.0 ; b = 1.0; n = 1000;\n", + " h = (b-a)/n; // h is the same for all processes \n", + " local_n = n/numprocs; \n", + " // make sure n > numprocs, else integer division gives zero\n", + " // Length of each process' interval of\n", + " // integration = local_n*h. \n", + " local_a = a + my_rank*local_n*h;\n", + " local_b = local_a + local_n*h;\n" + ] + }, + { + "cell_type": "markdown", + "id": "d203a19a", + "metadata": { + "editable": true + }, + "source": [ + "## Integrating with **MPI**" + ] + }, + { + "cell_type": "markdown", + "id": "be8560b4", + "metadata": { + "editable": true + }, + "source": [ + " total_sum = 0.0;\n", + " local_sum = trapezoidal_rule(local_a, local_b, local_n, \n", + " &int_function); \n", + " MPI_Reduce(&local_sum, &total_sum, 1, MPI_DOUBLE, \n", + " MPI_SUM, 0, MPI_COMM_WORLD);\n", + " time_end = MPI_Wtime();\n", + " total_time = time_end-time_start;\n", + " if ( my_rank == 0) {\n", + " cout << \"Trapezoidal rule = \" << total_sum << endl;\n", + " cout << \"Time = \" << total_time \n", + " << \" on number of processors: \" << numprocs << endl;\n", + " }\n", + " // End MPI\n", + " MPI_Finalize (); \n", + " return 0;\n", + " } // end of main program\n" + ] + }, + { + "cell_type": "markdown", + "id": "683a1a3a", + "metadata": { + "editable": true + }, + "source": [ + "## How do I use $MPI\\_reduce$?\n", + "\n", + "Here we have used" + ] + }, + { + "cell_type": "markdown", + "id": "4a3d1ec8", + "metadata": { + "editable": true + }, + "source": [ + " MPI_reduce( void *senddata, void* resultdata, int count, \n", + " MPI_Datatype datatype, MPI_Op, int root, MPI_Comm comm)\n" + ] + }, + { + "cell_type": "markdown", + "id": "b96a8840", + "metadata": { + "editable": true + }, + "source": [ + "The two variables $senddata$ and $resultdata$ are obvious, besides the fact that one sends the address\n", + "of the variable or the first element of an array. If they are arrays they need to have the same size. \n", + "The variable $count$ represents the total dimensionality, 1 in case of just one variable, \n", + "while $MPI\\_Datatype$ \n", + "defines the type of variable which is sent and received. \n", + "\n", + "The new feature is $MPI\\_Op$. It defines the type\n", + "of operation we want to do." + ] + }, + { + "cell_type": "markdown", + "id": "bcc235b8", + "metadata": { + "editable": true + }, + "source": [ + "## More on $MPI\\_Reduce$\n", + "In our case, since we are summing\n", + "the rectangle contributions from every process we define $MPI\\_Op = MPI\\_SUM$.\n", + "If we have an array or matrix we can search for the largest og smallest element by sending either $MPI\\_MAX$ or \n", + "$MPI\\_MIN$. If we want the location as well (which array element) we simply transfer \n", + "$MPI\\_MAXLOC$ or $MPI\\_MINOC$. If we want the product we write $MPI\\_PROD$. \n", + "\n", + "$MPI\\_Allreduce$ is defined as" + ] + }, + { + "cell_type": "markdown", + "id": "6f521ed1", + "metadata": { + "editable": true + }, + "source": [ + " MPI_Allreduce( void *senddata, void* resultdata, int count, \n", + " MPI_Datatype datatype, MPI_Op, MPI_Comm comm) \n" + ] + }, + { + "cell_type": "markdown", + "id": "307ef4f1", + "metadata": { + "editable": true + }, + "source": [ + "## Dissection of trapezoidal rule\n", + "\n", + "We use $MPI\\_reduce$ to collect data from each process. Note also the use of the function \n", + "$MPI\\_Wtime$." + ] + }, + { + "cell_type": "markdown", + "id": "1a2f72b4", + "metadata": { + "editable": true + }, + "source": [ + " // this function defines the function to integrate\n", + " double int_function(double x)\n", + " {\n", + " double value = 4./(1.+x*x);\n", + " return value;\n", + " } // end of function to evaluate\n", + " \n" + ] + }, + { + "cell_type": "markdown", + "id": "214ebedd", + "metadata": { + "editable": true + }, + "source": [ + "## Dissection of trapezoidal rule" + ] + }, + { + "cell_type": "markdown", + "id": "96619240", + "metadata": { + "editable": true + }, + "source": [ + " // this function defines the trapezoidal rule\n", + " double trapezoidal_rule(double a, double b, int n, \n", + " double (*func)(double))\n", + " {\n", + " double trapez_sum;\n", + " double fa, fb, x, step;\n", + " int j;\n", + " step=(b-a)/((double) n);\n", + " fa=(*func)(a)/2. ;\n", + " fb=(*func)(b)/2. ;\n", + " trapez_sum=0.;\n", + " for (j=1; j <= n-1; j++){\n", + " x=j*step+a;\n", + " trapez_sum+=(*func)(x);\n", + " }\n", + " trapez_sum=(trapez_sum+fb+fa)*step;\n", + " return trapez_sum;\n", + " } // end trapezoidal_rule \n" + ] + }, + { + "cell_type": "markdown", + "id": "740865e4", + "metadata": { + "editable": true + }, + "source": [ + "## [The quantum dot program for two electrons](https://github.com/CompPhysics/ComputationalPhysics2/blob/master/doc/Programs/ParallelizationMPI/MPIvmcqdot.cpp)" + ] + }, + { + "cell_type": "markdown", + "id": "484916ee", + "metadata": { + "editable": true + }, + "source": [ + " // Variational Monte Carlo for atoms with importance sampling, slater det\n", + " // Test case for 2-electron quantum dot, no classes using Mersenne-Twister RNG\n", + " #include \"mpi.h\"\n", + " #include \n", + " #include \n", + " #include \n", + " #include \n", + " #include \n", + " #include \n", + " #include \"vectormatrixclass.h\"\n", + " \n", + " using namespace std;\n", + " // output file as global variable\n", + " ofstream ofile; \n", + " // the step length and its squared inverse for the second derivative \n", + " // Here we define global variables used in various functions\n", + " // These can be changed by using classes\n", + " int Dimension = 2; \n", + " int NumberParticles = 2; // we fix also the number of electrons to be 2\n", + " \n", + " // declaration of functions \n", + " \n", + " // The Mc sampling for the variational Monte Carlo \n", + " void MonteCarloSampling(int, double &, double &, Vector &);\n", + " \n", + " // The variational wave function\n", + " double WaveFunction(Matrix &, Vector &);\n", + " \n", + " // The local energy \n", + " double LocalEnergy(Matrix &, Vector &);\n", + " \n", + " // The quantum force\n", + " void QuantumForce(Matrix &, Matrix &, Vector &);\n", + " \n", + " \n", + " // inline function for single-particle wave function\n", + " inline double SPwavefunction(double r, double alpha) { \n", + " return exp(-alpha*r*0.5);\n", + " }\n", + " \n", + " // inline function for derivative of single-particle wave function\n", + " inline double DerivativeSPwavefunction(double r, double alpha) { \n", + " return -r*alpha;\n", + " }\n", + " \n", + " // function for absolute value of relative distance\n", + " double RelativeDistance(Matrix &r, int i, int j) { \n", + " double r_ij = 0; \n", + " for (int k = 0; k < Dimension; k++) { \n", + " \tr_ij += (r(i,k)-r(j,k))*(r(i,k)-r(j,k));\n", + " }\n", + " return sqrt(r_ij); \n", + " }\n", + " \n", + " // inline function for derivative of Jastrow factor\n", + " inline double JastrowDerivative(Matrix &r, double beta, int i, int j, int k){\n", + " return (r(i,k)-r(j,k))/(RelativeDistance(r, i, j)*pow(1.0+beta*RelativeDistance(r, i, j),2));\n", + " }\n", + " \n", + " // function for square of position of single particle\n", + " double singleparticle_pos2(Matrix &r, int i) { \n", + " double r_single_particle = 0;\n", + " for (int j = 0; j < Dimension; j++) { \n", + " r_single_particle += r(i,j)*r(i,j);\n", + " }\n", + " return r_single_particle;\n", + " }\n", + " \n", + " void lnsrch(int n, Vector &xold, double fold, Vector &g, Vector &p, Vector &x,\n", + " \t\t double *f, double stpmax, int *check, double (*func)(Vector &p));\n", + " \n", + " void dfpmin(Vector &p, int n, double gtol, int *iter, double *fret,\n", + " \t double(*func)(Vector &p), void (*dfunc)(Vector &p, Vector &g));\n", + " \n", + " static double sqrarg;\n", + " #define SQR(a) ((sqrarg=(a)) == 0.0 ? 0.0 : sqrarg*sqrarg)\n", + " \n", + " \n", + " static double maxarg1,maxarg2;\n", + " #define FMAX(a,b) (maxarg1=(a),maxarg2=(b),(maxarg1) > (maxarg2) ?\\\n", + " (maxarg1) : (maxarg2))\n", + " \n", + " \n", + " // Begin of main program \n", + " \n", + " int main(int argc, char* argv[])\n", + " {\n", + " \n", + " // MPI initializations\n", + " int NumberProcesses, MyRank, NumberMCsamples;\n", + " MPI_Init (&argc, &argv);\n", + " MPI_Comm_size (MPI_COMM_WORLD, &NumberProcesses);\n", + " MPI_Comm_rank (MPI_COMM_WORLD, &MyRank);\n", + " double StartTime = MPI_Wtime();\n", + " if (MyRank == 0 && argc <= 1) {\n", + " cout << \"Bad Usage: \" << argv[0] << \n", + " \" Read also output file on same line and number of Monte Carlo cycles\" << endl;\n", + " }\n", + " // Read filename and number of Monte Carlo cycles from the command line\n", + " if (MyRank == 0 && argc > 2) {\n", + " string filename = argv[1]; // first command line argument after name of program\n", + " NumberMCsamples = atoi(argv[2]);\n", + " string fileout = filename;\n", + " string argument = to_string(NumberMCsamples);\n", + " // Final filename as filename+NumberMCsamples\n", + " fileout.append(argument);\n", + " ofile.open(fileout);\n", + " }\n", + " // broadcast the number of Monte Carlo samples\n", + " MPI_Bcast (&NumberMCsamples, 1, MPI_INT, 0, MPI_COMM_WORLD);\n", + " // Two variational parameters only\n", + " Vector VariationalParameters(2);\n", + " int TotalNumberMCsamples = NumberMCsamples*NumberProcesses; \n", + " // Loop over variational parameters\n", + " for (double alpha = 0.5; alpha <= 1.5; alpha +=0.1){\n", + " for (double beta = 0.1; beta <= 0.5; beta +=0.05){\n", + " VariationalParameters(0) = alpha; // value of alpha\n", + " VariationalParameters(1) = beta; // value of beta\n", + " // Do the mc sampling and accumulate data with MPI_Reduce\n", + " double TotalEnergy, TotalEnergySquared, LocalProcessEnergy, LocalProcessEnergy2;\n", + " LocalProcessEnergy = LocalProcessEnergy2 = 0.0;\n", + " MonteCarloSampling(NumberMCsamples, LocalProcessEnergy, LocalProcessEnergy2, VariationalParameters);\n", + " // Collect data in total averages\n", + " MPI_Reduce(&LocalProcessEnergy, &TotalEnergy, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);\n", + " MPI_Reduce(&LocalProcessEnergy2, &TotalEnergySquared, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);\n", + " // Print out results in case of Master node, set to MyRank = 0\n", + " if ( MyRank == 0) {\n", + " \tdouble Energy = TotalEnergy/( (double)NumberProcesses);\n", + " \tdouble Variance = TotalEnergySquared/( (double)NumberProcesses)-Energy*Energy;\n", + " \tdouble StandardDeviation = sqrt(Variance/((double)TotalNumberMCsamples)); // over optimistic error\n", + " \tofile << setiosflags(ios::showpoint | ios::uppercase);\n", + " \tofile << setw(15) << setprecision(8) << VariationalParameters(0);\n", + " \tofile << setw(15) << setprecision(8) << VariationalParameters(1);\n", + " \tofile << setw(15) << setprecision(8) << Energy;\n", + " \tofile << setw(15) << setprecision(8) << Variance;\n", + " \tofile << setw(15) << setprecision(8) << StandardDeviation << endl;\n", + " }\n", + " }\n", + " }\n", + " double EndTime = MPI_Wtime();\n", + " double TotalTime = EndTime-StartTime;\n", + " if ( MyRank == 0 ) cout << \"Time = \" << TotalTime << \" on number of processors: \" << NumberProcesses << endl;\n", + " if (MyRank == 0) ofile.close(); // close output file\n", + " // End MPI\n", + " MPI_Finalize (); \n", + " return 0;\n", + " } // end of main function\n", + " \n", + " \n", + " // Monte Carlo sampling with the Metropolis algorithm \n", + " \n", + " void MonteCarloSampling(int NumberMCsamples, double &cumulative_e, double &cumulative_e2, Vector &VariationalParameters)\n", + " {\n", + " \n", + " // Initialize the seed and call the Mersienne algo\n", + " std::random_device rd;\n", + " std::mt19937_64 gen(rd());\n", + " // Set up the uniform distribution for x \\in [[0, 1]\n", + " std::uniform_real_distribution UniformNumberGenerator(0.0,1.0);\n", + " std::normal_distribution Normaldistribution(0.0,1.0);\n", + " // diffusion constant from Schroedinger equation\n", + " double D = 0.5; \n", + " double timestep = 0.05; // we fix the time step for the gaussian deviate\n", + " // allocate matrices which contain the position of the particles \n", + " Matrix OldPosition( NumberParticles, Dimension), NewPosition( NumberParticles, Dimension);\n", + " Matrix OldQuantumForce(NumberParticles, Dimension), NewQuantumForce(NumberParticles, Dimension);\n", + " double Energy = 0.0; double EnergySquared = 0.0; double DeltaE = 0.0;\n", + " // initial trial positions\n", + " for (int i = 0; i < NumberParticles; i++) { \n", + " for (int j = 0; j < Dimension; j++) {\n", + " OldPosition(i,j) = Normaldistribution(gen)*sqrt(timestep);\n", + " }\n", + " }\n", + " double OldWaveFunction = WaveFunction(OldPosition, VariationalParameters);\n", + " QuantumForce(OldPosition, OldQuantumForce, VariationalParameters);\n", + " // loop over monte carlo cycles \n", + " for (int cycles = 1; cycles <= NumberMCsamples; cycles++){ \n", + " // new position \n", + " for (int i = 0; i < NumberParticles; i++) { \n", + " for (int j = 0; j < Dimension; j++) {\n", + " \t// gaussian deviate to compute new positions using a given timestep\n", + " \tNewPosition(i,j) = OldPosition(i,j) + Normaldistribution(gen)*sqrt(timestep)+OldQuantumForce(i,j)*timestep*D;\n", + " \t//\tNewPosition(i,j) = OldPosition(i,j) + gaussian_deviate(&idum)*sqrt(timestep)+OldQuantumForce(i,j)*timestep*D;\n", + " } \n", + " // for the other particles we need to set the position to the old position since\n", + " // we move only one particle at the time\n", + " for (int k = 0; k < NumberParticles; k++) {\n", + " \tif ( k != i) {\n", + " \t for (int j = 0; j < Dimension; j++) {\n", + " \t NewPosition(k,j) = OldPosition(k,j);\n", + " \t }\n", + " \t} \n", + " }\n", + " double NewWaveFunction = WaveFunction(NewPosition, VariationalParameters); \n", + " QuantumForce(NewPosition, NewQuantumForce, VariationalParameters);\n", + " // we compute the log of the ratio of the greens functions to be used in the \n", + " // Metropolis-Hastings algorithm\n", + " double GreensFunction = 0.0; \n", + " for (int j = 0; j < Dimension; j++) {\n", + " \tGreensFunction += 0.5*(OldQuantumForce(i,j)+NewQuantumForce(i,j))*\n", + " \t (D*timestep*0.5*(OldQuantumForce(i,j)-NewQuantumForce(i,j))-NewPosition(i,j)+OldPosition(i,j));\n", + " }\n", + " GreensFunction = exp(GreensFunction);\n", + " // The Metropolis test is performed by moving one particle at the time\n", + " if(UniformNumberGenerator(gen) <= GreensFunction*NewWaveFunction*NewWaveFunction/OldWaveFunction/OldWaveFunction ) { \n", + " \tfor (int j = 0; j < Dimension; j++) {\n", + " \t OldPosition(i,j) = NewPosition(i,j);\n", + " \t OldQuantumForce(i,j) = NewQuantumForce(i,j);\n", + " \t}\n", + " \tOldWaveFunction = NewWaveFunction;\n", + " }\n", + " } // end of loop over particles\n", + " // compute local energy \n", + " double DeltaE = LocalEnergy(OldPosition, VariationalParameters);\n", + " // update energies\n", + " Energy += DeltaE;\n", + " EnergySquared += DeltaE*DeltaE;\n", + " } // end of loop over MC trials \n", + " // update the energy average and its squared \n", + " cumulative_e = Energy/NumberMCsamples;\n", + " cumulative_e2 = EnergySquared/NumberMCsamples;\n", + " } // end MonteCarloSampling function \n", + " \n", + " \n", + " // Function to compute the squared wave function and the quantum force\n", + " \n", + " double WaveFunction(Matrix &r, Vector &VariationalParameters)\n", + " {\n", + " double wf = 0.0;\n", + " // full Slater determinant for two particles, replace with Slater det for more particles \n", + " wf = SPwavefunction(singleparticle_pos2(r, 0), VariationalParameters(0))*SPwavefunction(singleparticle_pos2(r, 1),VariationalParameters(0));\n", + " // contribution from Jastrow factor\n", + " for (int i = 0; i < NumberParticles-1; i++) { \n", + " for (int j = i+1; j < NumberParticles; j++) {\n", + " wf *= exp(RelativeDistance(r, i, j)/((1.0+VariationalParameters(1)*RelativeDistance(r, i, j))));\n", + " }\n", + " }\n", + " return wf;\n", + " }\n", + " \n", + " // Function to calculate the local energy without numerical derivation of kinetic energy\n", + " \n", + " double LocalEnergy(Matrix &r, Vector &VariationalParameters)\n", + " {\n", + " \n", + " // compute the kinetic and potential energy from the single-particle part\n", + " // for a many-electron system this has to be replaced by a Slater determinant\n", + " // The absolute value of the interparticle length\n", + " Matrix length( NumberParticles, NumberParticles);\n", + " // Set up interparticle distance\n", + " for (int i = 0; i < NumberParticles-1; i++) { \n", + " for(int j = i+1; j < NumberParticles; j++){\n", + " length(i,j) = RelativeDistance(r, i, j);\n", + " length(j,i) = length(i,j);\n", + " }\n", + " }\n", + " double KineticEnergy = 0.0;\n", + " // Set up kinetic energy from Slater and Jastrow terms\n", + " for (int i = 0; i < NumberParticles; i++) { \n", + " for (int k = 0; k < Dimension; k++) {\n", + " double sum1 = 0.0; \n", + " for(int j = 0; j < NumberParticles; j++){\n", + " \tif ( j != i) {\n", + " \t sum1 += JastrowDerivative(r, VariationalParameters(1), i, j, k);\n", + " \t}\n", + " }\n", + " KineticEnergy += (sum1+DerivativeSPwavefunction(r(i,k),VariationalParameters(0)))*(sum1+DerivativeSPwavefunction(r(i,k),VariationalParameters(0)));\n", + " }\n", + " }\n", + " KineticEnergy += -2*VariationalParameters(0)*NumberParticles;\n", + " for (int i = 0; i < NumberParticles-1; i++) {\n", + " for (int j = i+1; j < NumberParticles; j++) {\n", + " KineticEnergy += 2.0/(pow(1.0 + VariationalParameters(1)*length(i,j),2))*(1.0/length(i,j)-2*VariationalParameters(1)/(1+VariationalParameters(1)*length(i,j)) );\n", + " }\n", + " }\n", + " KineticEnergy *= -0.5;\n", + " // Set up potential energy, external potential + eventual electron-electron repulsion\n", + " double PotentialEnergy = 0;\n", + " for (int i = 0; i < NumberParticles; i++) { \n", + " double DistanceSquared = singleparticle_pos2(r, i);\n", + " PotentialEnergy += 0.5*DistanceSquared; // sp energy HO part, note it has the oscillator frequency set to 1!\n", + " }\n", + " // Add the electron-electron repulsion\n", + " for (int i = 0; i < NumberParticles-1; i++) { \n", + " for (int j = i+1; j < NumberParticles; j++) {\n", + " PotentialEnergy += 1.0/length(i,j); \n", + " }\n", + " }\n", + " double LocalE = KineticEnergy+PotentialEnergy;\n", + " return LocalE;\n", + " }\n", + " \n", + " // Compute the analytical expression for the quantum force\n", + " void QuantumForce(Matrix &r, Matrix &qforce, Vector &VariationalParameters)\n", + " {\n", + " // compute the first derivative \n", + " for (int i = 0; i < NumberParticles; i++) {\n", + " for (int k = 0; k < Dimension; k++) {\n", + " // single-particle part, replace with Slater det for larger systems\n", + " double sppart = DerivativeSPwavefunction(r(i,k),VariationalParameters(0));\n", + " // Jastrow factor contribution\n", + " double Jsum = 0.0;\n", + " for (int j = 0; j < NumberParticles; j++) {\n", + " \tif ( j != i) {\n", + " \t Jsum += JastrowDerivative(r, VariationalParameters(1), i, j, k);\n", + " \t}\n", + " }\n", + " qforce(i,k) = 2.0*(Jsum+sppart);\n", + " }\n", + " }\n", + " } // end of QuantumForce function\n", + " \n", + " \n", + " #define ITMAX 200\n", + " #define EPS 3.0e-8\n", + " #define TOLX (4*EPS)\n", + " #define STPMX 100.0\n", + " \n", + " void dfpmin(Vector &p, int n, double gtol, int *iter, double *fret,\n", + " \t double(*func)(Vector &p), void (*dfunc)(Vector &p, Vector &g))\n", + " {\n", + " \n", + " int check,i,its,j;\n", + " double den,fac,fad,fae,fp,stpmax,sum=0.0,sumdg,sumxi,temp,test;\n", + " Vector dg(n), g(n), hdg(n), pnew(n), xi(n);\n", + " Matrix hessian(n,n);\n", + " \n", + " fp=(*func)(p);\n", + " (*dfunc)(p,g);\n", + " for (i = 0;i < n;i++) {\n", + " for (j = 0; j< n;j++) hessian(i,j)=0.0;\n", + " hessian(i,i)=1.0;\n", + " xi(i) = -g(i);\n", + " sum += p(i)*p(i);\n", + " }\n", + " stpmax=STPMX*FMAX(sqrt(sum),(double)n);\n", + " for (its=1;its<=ITMAX;its++) {\n", + " *iter=its;\n", + " lnsrch(n,p,fp,g,xi,pnew,fret,stpmax,&check,func);\n", + " fp = *fret;\n", + " for (i = 0; i< n;i++) {\n", + " xi(i)=pnew(i)-p(i);\n", + " p(i)=pnew(i);\n", + " }\n", + " test=0.0;\n", + " for (i = 0;i< n;i++) {\n", + " temp=fabs(xi(i))/FMAX(fabs(p(i)),1.0);\n", + " if (temp > test) test=temp;\n", + " }\n", + " if (test < TOLX) {\n", + " return;\n", + " }\n", + " for (i=0;i test) test=temp;\n", + " }\n", + " if (test < gtol) {\n", + " return;\n", + " }\n", + " for (i=0;i EPS*sumdg*sumxi) {\n", + " fac=1.0/fac;\n", + " fad=1.0/fae;\n", + " for (i=0;i stpmax)\n", + " for (i=0;i test) test=temp;\n", + " }\n", + " alamin=TOLX/test;\n", + " alam=1.0;\n", + " for (;;) {\n", + " for (i=0;i0.5*alam)\n", + " \t tmplam=0.5*alam;\n", + " }\n", + " }\n", + " alam2=alam;\n", + " f2 = *f;\n", + " fold2=fold;\n", + " alam=FMAX(tmplam,0.1*alam);\n", + " }\n", + " }\n", + " #undef ALF\n", + " #undef TOLX\n", + " \n" + ] + }, + { + "cell_type": "markdown", + "id": "a2672ee2", + "metadata": { + "editable": true + }, + "source": [ + "## What is OpenMP\n", + "* OpenMP provides high-level thread programming\n", + "\n", + "* Multiple cooperating threads are allowed to run simultaneously\n", + "\n", + "* Threads are created and destroyed dynamically in a fork-join pattern\n", + "\n", + " * An OpenMP program consists of a number of parallel regions\n", + "\n", + " * Between two parallel regions there is only one master thread\n", + "\n", + " * In the beginning of a parallel region, a team of new threads is spawned\n", + "\n", + " * The newly spawned threads work simultaneously with the master thread\n", + "\n", + " * At the end of a parallel region, the new threads are destroyed\n", + "\n", + "Many good tutorials online and excellent textbook\n", + "1. [Using OpenMP, by B. Chapman, G. Jost, and A. van der Pas](http://mitpress.mit.edu/books/using-openmp)\n", + "\n", + "2. Many tutorials online like [OpenMP official site](http://www.openmp.org)" + ] + }, + { + "cell_type": "markdown", + "id": "ab7b0b53", + "metadata": { + "editable": true + }, + "source": [ + "## Getting started, things to remember\n", + " * Remember the header file" + ] + }, + { + "cell_type": "markdown", + "id": "50cd6880", + "metadata": { + "editable": true + }, + "source": [ + " #include \n" + ] + }, + { + "cell_type": "markdown", + "id": "4b877c85", + "metadata": { + "editable": true + }, + "source": [ + "* Insert compiler directives in C++ syntax as" + ] + }, + { + "cell_type": "markdown", + "id": "edc15526", + "metadata": { + "editable": true + }, + "source": [ + " #pragma omp...\n" + ] + }, + { + "cell_type": "markdown", + "id": "4f80c44e", + "metadata": { + "editable": true + }, + "source": [ + "* Compile with for example *c++ -fopenmp code.cpp*\n", + "\n", + "* Execute\n", + "\n", + " * Remember to assign the environment variable **OMP NUM THREADS**\n", + "\n", + " * It specifies the total number of threads inside a parallel region, if not otherwise overwritten" + ] + }, + { + "cell_type": "markdown", + "id": "ae290278", + "metadata": { + "editable": true + }, + "source": [ + "## OpenMP syntax\n", + "* Mostly directives" + ] + }, + { + "cell_type": "markdown", + "id": "0b47582b", + "metadata": { + "editable": true + }, + "source": [ + " #pragma omp construct [ clause ...]\n" + ] + }, + { + "cell_type": "markdown", + "id": "29680064", + "metadata": { + "editable": true + }, + "source": [ + "* Some functions and types" + ] + }, + { + "cell_type": "markdown", + "id": "add80e8f", + "metadata": { + "editable": true + }, + "source": [ + " #include \n" + ] + }, + { + "cell_type": "markdown", + "id": "badeef32", + "metadata": { + "editable": true + }, + "source": [ + "* Most apply to a block of code\n", + "\n", + " * Specifically, a **structured block**\n", + "\n", + " * Enter at top, exit at bottom only, exit(), abort() permitted" + ] + }, + { + "cell_type": "markdown", + "id": "1655cea6", + "metadata": { + "editable": true + }, + "source": [ + "## Different OpenMP styles of parallelism\n", + "OpenMP supports several different ways to specify thread parallelism\n", + "\n", + "* General parallel regions: All threads execute the code, roughly as if you made a routine of that region and created a thread to run that code\n", + "\n", + "* Parallel loops: Special case for loops, simplifies data parallel code\n", + "\n", + "* Task parallelism, new in OpenMP 3\n", + "\n", + "* Several ways to manage thread coordination, including Master regions and Locks\n", + "\n", + "* Memory model for shared data" + ] + }, + { + "cell_type": "markdown", + "id": "542d46d6", + "metadata": { + "editable": true + }, + "source": [ + "## General code structure" + ] + }, + { + "cell_type": "markdown", + "id": "5f3a057b", + "metadata": { + "editable": true + }, + "source": [ + " #include \n", + " main ()\n", + " {\n", + " int var1, var2, var3;\n", + " /* serial code */\n", + " /* ... */\n", + " /* start of a parallel region */\n", + " #pragma omp parallel private(var1, var2) shared(var3)\n", + " {\n", + " /* ... */\n", + " }\n", + " /* more serial code */\n", + " /* ... */\n", + " /* another parallel region */\n", + " #pragma omp parallel\n", + " {\n", + " /* ... */\n", + " }\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "e600a25b", + "metadata": { + "editable": true + }, + "source": [ + "## Parallel region\n", + "* A parallel region is a block of code that is executed by a team of threads\n", + "\n", + "* The following compiler directive creates a parallel region" + ] + }, + { + "cell_type": "markdown", + "id": "7cd27256", + "metadata": { + "editable": true + }, + "source": [ + " #pragma omp parallel { ... }\n" + ] + }, + { + "cell_type": "markdown", + "id": "4af8979b", + "metadata": { + "editable": true + }, + "source": [ + "* Clauses can be added at the end of the directive\n", + "\n", + "* Most often used clauses:\n", + "\n", + " * **default(shared)** or **default(none)**\n", + "\n", + " * **public(list of variables)**\n", + "\n", + " * **private(list of variables)**" + ] + }, + { + "cell_type": "markdown", + "id": "d314f63a", + "metadata": { + "editable": true + }, + "source": [ + "## Hello world, not again, please!" + ] + }, + { + "cell_type": "markdown", + "id": "476b27d6", + "metadata": { + "editable": true + }, + "source": [ + " #include \n", + " #include \n", + " int main (int argc, char *argv[])\n", + " {\n", + " int th_id, nthreads;\n", + " #pragma omp parallel private(th_id) shared(nthreads)\n", + " {\n", + " th_id = omp_get_thread_num();\n", + " printf(\"Hello World from thread %d\\n\", th_id);\n", + " #pragma omp barrier\n", + " if ( th_id == 0 ) {\n", + " nthreads = omp_get_num_threads();\n", + " printf(\"There are %d threads\\n\",nthreads);\n", + " }\n", + " }\n", + " return 0;\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "b3150e25", + "metadata": { + "editable": true + }, + "source": [ + "## Hello world, yet another variant" + ] + }, + { + "cell_type": "markdown", + "id": "d7994f3d", + "metadata": { + "editable": true + }, + "source": [ + " #include \n", + " #include \n", + " int main(int argc, char *argv[]) \n", + " {\n", + " omp_set_num_threads(4); \n", + " #pragma omp parallel\n", + " {\n", + " int id = omp_get_thread_num();\n", + " int nproc = omp_get_num_threads(); \n", + " cout << \"Hello world with id number and processes \" << id << nproc << endl;\n", + " } \n", + " return 0;\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "a1b0ca46", + "metadata": { + "editable": true + }, + "source": [ + "Variables declared outside of the parallel region are shared by all threads\n", + "If a variable like **id** is declared outside of the" + ] + }, + { + "cell_type": "markdown", + "id": "af8f316a", + "metadata": { + "editable": true + }, + "source": [ + " #pragma omp parallel, \n" + ] + }, + { + "cell_type": "markdown", + "id": "f2def2ed", + "metadata": { + "editable": true + }, + "source": [ + "it would have been shared by various the threads, possibly causing erroneous output\n", + " * Why? What would go wrong? Why do we add possibly?" + ] + }, + { + "cell_type": "markdown", + "id": "5a2e2450", + "metadata": { + "editable": true + }, + "source": [ + "## Important OpenMP library routines\n", + "\n", + "* **int omp get num threads ()**, returns the number of threads inside a parallel region\n", + "\n", + "* **int omp get thread num ()**, returns the a thread for each thread inside a parallel region\n", + "\n", + "* **void omp set num threads (int)**, sets the number of threads to be used\n", + "\n", + "* **void omp set nested (int)**, turns nested parallelism on/off" + ] + }, + { + "cell_type": "markdown", + "id": "66bbd83e", + "metadata": { + "editable": true + }, + "source": [ + "## Private variables\n", + "Private clause can be used to make thread- private versions of such variables:" + ] + }, + { + "cell_type": "markdown", + "id": "6d307151", + "metadata": { + "editable": true + }, + "source": [ + " #pragma omp parallel private(id)\n", + " {\n", + " int id = omp_get_thread_num();\n", + " cout << \"My thread num\" << id << endl; \n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "2a35060f", + "metadata": { + "editable": true + }, + "source": [ + "* What is their value on entry? Exit?\n", + "\n", + "* OpenMP provides ways to control that\n", + "\n", + "* Can use default(none) to require the sharing of each variable to be described" + ] + }, + { + "cell_type": "markdown", + "id": "9721e8f2", + "metadata": { + "editable": true + }, + "source": [ + "## Master region\n", + "It is often useful to have only one thread execute some of the code in a parallel region. I/O statements are a common example" + ] + }, + { + "cell_type": "markdown", + "id": "a99bffb1", + "metadata": { + "editable": true + }, + "source": [ + " #pragma omp parallel \n", + " {\n", + " #pragma omp master\n", + " {\n", + " int id = omp_get_thread_num();\n", + " cout << \"My thread num\" << id << endl; \n", + " } \n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "bf58be92", + "metadata": { + "editable": true + }, + "source": [ + "## Parallel for loop\n", + " * Inside a parallel region, the following compiler directive can be used to parallelize a for-loop:" + ] + }, + { + "cell_type": "markdown", + "id": "b9ad7fc0", + "metadata": { + "editable": true + }, + "source": [ + " #pragma omp for\n" + ] + }, + { + "cell_type": "markdown", + "id": "dfd937c8", + "metadata": { + "editable": true + }, + "source": [ + "* Clauses can be added, such as\n", + "\n", + " * **schedule(static, chunk size)**\n", + "\n", + " * **schedule(dynamic, chunk size)** \n", + "\n", + " * **schedule(guided, chunk size)** (non-deterministic allocation)\n", + "\n", + " * **schedule(runtime)**\n", + "\n", + " * **private(list of variables)**\n", + "\n", + " * **reduction(operator:variable)**\n", + "\n", + " * **nowait**" + ] + }, + { + "cell_type": "markdown", + "id": "39645b2c", + "metadata": { + "editable": true + }, + "source": [ + "## Parallel computations and loops\n", + "\n", + "OpenMP provides an easy way to parallelize a loop" + ] + }, + { + "cell_type": "markdown", + "id": "31bf0645", + "metadata": { + "editable": true + }, + "source": [ + " #pragma omp parallel for\n", + " for (i=0; i\n", + " #define CHUNKSIZE 100\n", + " #define N 1000\n", + " int main (int argc, char *argv[])\n", + " {\n", + " int i, chunk;\n", + " float a[N], b[N], c[N];\n", + " for (i=0; i < N; i++) a[i] = b[i] = i * 1.0;\n", + " chunk = CHUNKSIZE;\n", + " #pragma omp parallel shared(a,b,c,chunk) private(i)\n", + " {\n", + " #pragma omp for schedule(dynamic,chunk)\n", + " for (i=0; i < N; i++) c[i] = a[i] + b[i];\n", + " } /* end of parallel region */\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "bcbc9208", + "metadata": { + "editable": true + }, + "source": [ + "## Example code for loop scheduling, guided instead of dynamic" + ] + }, + { + "cell_type": "markdown", + "id": "a9bcffe4", + "metadata": { + "editable": true + }, + "source": [ + " #include \n", + " #define CHUNKSIZE 100\n", + " #define N 1000\n", + " int main (int argc, char *argv[])\n", + " {\n", + " int i, chunk;\n", + " float a[N], b[N], c[N];\n", + " for (i=0; i < N; i++) a[i] = b[i] = i * 1.0;\n", + " chunk = CHUNKSIZE;\n", + " #pragma omp parallel shared(a,b,c,chunk) private(i)\n", + " {\n", + " #pragma omp for schedule(guided,chunk)\n", + " for (i=0; i < N; i++) c[i] = a[i] + b[i];\n", + " } /* end of parallel region */\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "2e3fc84f", + "metadata": { + "editable": true + }, + "source": [ + "## More on Parallel for loop\n", + "* The number of loop iterations cannot be non-deterministic; break, return, exit, goto not allowed inside the for-loop\n", + "\n", + "* The loop index is private to each thread\n", + "\n", + "* A reduction variable is special\n", + "\n", + " * During the for-loop there is a local private copy in each thread\n", + "\n", + " * At the end of the for-loop, all the local copies are combined together by the reduction operation\n", + "\n", + "* Unless the nowait clause is used, an implicit barrier synchronization will be added at the end by the compiler" + ] + }, + { + "cell_type": "markdown", + "id": "f8bc1f7a", + "metadata": { + "editable": true + }, + "source": [ + " // #pragma omp parallel and #pragma omp for\n" + ] + }, + { + "cell_type": "markdown", + "id": "b573c665", + "metadata": { + "editable": true + }, + "source": [ + "can be combined into" + ] + }, + { + "cell_type": "markdown", + "id": "1549ba33", + "metadata": { + "editable": true + }, + "source": [ + " #pragma omp parallel for\n" + ] + }, + { + "cell_type": "markdown", + "id": "2c132037", + "metadata": { + "editable": true + }, + "source": [ + "## What can happen with this loop?\n", + "\n", + "What happens with code like this" + ] + }, + { + "cell_type": "markdown", + "id": "823a30b6", + "metadata": { + "editable": true + }, + "source": [ + " #pragma omp parallel for\n", + " for (i=0; i r) {\n", + " #pragma omp task\n", + " do_work (p_vec[i]);\n" + ] + }, + { + "cell_type": "markdown", + "id": "790aa2d3", + "metadata": { + "editable": true + }, + "source": [ + "## Common mistakes\n", + "Race condition" + ] + }, + { + "cell_type": "markdown", + "id": "30b561e2", + "metadata": { + "editable": true + }, + "source": [ + " int nthreads;\n", + " #pragma omp parallel shared(nthreads)\n", + " {\n", + " nthreads = omp_get_num_threads();\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "4d2a0797", + "metadata": { + "editable": true + }, + "source": [ + "Deadlock" + ] + }, + { + "cell_type": "markdown", + "id": "3cd95899", + "metadata": { + "editable": true + }, + "source": [ + " #pragma omp parallel\n", + " {\n", + " ...\n", + " #pragma omp critical\n", + " {\n", + " ...\n", + " #pragma omp barrier\n", + " }\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "68c2a4fe", + "metadata": { + "editable": true + }, + "source": [ + "## Not all computations are simple\n", + "Not all computations are simple loops where the data can be evenly \n", + "divided among threads without any dependencies between threads\n", + "\n", + "An example is finding the location and value of the largest element in an array" + ] + }, + { + "cell_type": "markdown", + "id": "047f7c41", + "metadata": { + "editable": true + }, + "source": [ + " for (i=0; i maxval) {\n", + " maxval = x[i];\n", + " maxloc = i; \n", + " }\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "e60ece38", + "metadata": { + "editable": true + }, + "source": [ + "## Not all computations are simple, competing threads\n", + "All threads are potentially accessing and changing the same values, **maxloc** and **maxval**.\n", + "1. OpenMP provides several ways to coordinate access to shared values" + ] + }, + { + "cell_type": "markdown", + "id": "f56129e7", + "metadata": { + "editable": true + }, + "source": [ + " #pragma omp atomic\n" + ] + }, + { + "cell_type": "markdown", + "id": "7b02497f", + "metadata": { + "editable": true + }, + "source": [ + "1. Only one thread at a time can execute the following statement (not block). We can use the critical option" + ] + }, + { + "cell_type": "markdown", + "id": "466f17e6", + "metadata": { + "editable": true + }, + "source": [ + " #pragma omp critical\n" + ] + }, + { + "cell_type": "markdown", + "id": "35fb343a", + "metadata": { + "editable": true + }, + "source": [ + "1. Only one thread at a time can execute the following block\n", + "\n", + "Atomic may be faster than critical but depends on hardware" + ] + }, + { + "cell_type": "markdown", + "id": "50838011", + "metadata": { + "editable": true + }, + "source": [ + "## How to find the max value using OpenMP\n", + "Write down the simplest algorithm and look carefully for race conditions. How would you handle them? \n", + "The first step would be to parallelize as" + ] + }, + { + "cell_type": "markdown", + "id": "1ac70c07", + "metadata": { + "editable": true + }, + "source": [ + " #pragma omp parallel for\n", + " for (i=0; i maxval) {\n", + " maxval = x[i];\n", + " maxloc = i; \n", + " }\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "06de5d43", + "metadata": { + "editable": true + }, + "source": [ + "## Then deal with the race conditions\n", + "Write down the simplest algorithm and look carefully for race conditions. How would you handle them? \n", + "The first step would be to parallelize as" + ] + }, + { + "cell_type": "markdown", + "id": "4674fff0", + "metadata": { + "editable": true + }, + "source": [ + " #pragma omp parallel for\n", + " for (i=0; i maxval) {\n", + " maxval = x[i];\n", + " maxloc = i; \n", + " }\n", + " }\n", + " } \n" + ] + }, + { + "cell_type": "markdown", + "id": "1a21898f", + "metadata": { + "editable": true + }, + "source": [ + "Exercise: write a code which implements this and give an estimate on performance. Perform several runs,\n", + "with a serial code only with and without vectorization and compare the serial code with the one that uses OpenMP. Run on different archictectures if you can." + ] + }, + { + "cell_type": "markdown", + "id": "361afe33", + "metadata": { + "editable": true + }, + "source": [ + "## What can slow down OpenMP performance?\n", + "Give it a thought!" + ] + }, + { + "cell_type": "markdown", + "id": "0872a8b1", + "metadata": { + "editable": true + }, + "source": [ + "## What can slow down OpenMP performance?\n", + "Performance poor because we insisted on keeping track of the maxval and location during the execution of the loop.\n", + " * We do not care about the value during the execution of the loop, just the value at the end.\n", + "\n", + "This is a common source of performance issues, namely the description of the method used to compute a value imposes additional, unnecessary requirements or properties\n", + "\n", + "**Idea: Have each thread find the maxloc in its own data, then combine and use temporary arrays indexed by thread number to hold the values found by each thread**" + ] + }, + { + "cell_type": "markdown", + "id": "573d56fe", + "metadata": { + "editable": true + }, + "source": [ + "## Find the max location for each thread" + ] + }, + { + "cell_type": "markdown", + "id": "371fbbfd", + "metadata": { + "editable": true + }, + "source": [ + " int maxloc[MAX_THREADS], mloc;\n", + " double maxval[MAX_THREADS], mval; \n", + " #pragma omp parallel shared(maxval,maxloc)\n", + " {\n", + " int id = omp_get_thread_num(); \n", + " maxval[id] = -1.0e30;\n", + " #pragma omp for\n", + " for (int i=0; i maxval[id]) { \n", + " maxloc[id] = i;\n", + " maxval[id] = x[i]; \n", + " }\n", + " }\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "7d7972d7", + "metadata": { + "editable": true + }, + "source": [ + "## Combine the values from each thread" + ] + }, + { + "cell_type": "markdown", + "id": "7119f580", + "metadata": { + "editable": true + }, + "source": [ + " #pragma omp flush (maxloc,maxval)\n", + " #pragma omp master\n", + " {\n", + " int nt = omp_get_num_threads(); \n", + " mloc = maxloc[0]; \n", + " mval = maxval[0]; \n", + " for (int i=1; i mval) { \n", + " mval = maxval[i]; \n", + " mloc = maxloc[i];\n", + " } \n", + " }\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a3de8bd", + "metadata": { + "editable": true + }, + "source": [ + "Note that we let the master process perform the last operation." + ] + }, + { + "cell_type": "markdown", + "id": "dace2927", + "metadata": { + "editable": true + }, + "source": [ + "## [Matrix-matrix multiplication](https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPvectornorm.cpp)\n", + "This code computes the norm of a vector using OpenMp" + ] + }, + { + "cell_type": "markdown", + "id": "fb2465d2", + "metadata": { + "editable": true + }, + "source": [ + " // OpenMP program to compute vector norm by adding two other vectors\n", + " #include \n", + " #include \n", + " #include \n", + " #include \n", + " #include \n", + " # include \n", + " \n", + " using namespace std; // note use of namespace\n", + " int main (int argc, char* argv[])\n", + " {\n", + " // read in dimension of vector\n", + " int n = atoi(argv[1]);\n", + " double *a, *b, *c;\n", + " int i;\n", + " int thread_num;\n", + " double wtime, Norm2, s, angle;\n", + " cout << \" Perform addition of two vectors and compute the norm-2.\" << endl;\n", + " omp_set_num_threads(4);\n", + " thread_num = omp_get_max_threads ();\n", + " cout << \" The number of processors available = \" << omp_get_num_procs () << endl ;\n", + " cout << \" The number of threads available = \" << thread_num << endl;\n", + " cout << \" The matrix order n = \" << n << endl;\n", + " \n", + " s = 1.0/sqrt( (double) n);\n", + " wtime = omp_get_wtime ( );\n", + " // Allocate space for the vectors to be used\n", + " a = new double [n]; b = new double [n]; c = new double [n];\n", + " // Define parallel region\n", + " # pragma omp parallel for default(shared) private (angle, i) reduction(+:Norm2)\n", + " // Set up values for vectors a and b\n", + " for (i = 0; i < n; i++){\n", + " angle = 2.0*M_PI*i/ (( double ) n);\n", + " a[i] = s*(sin(angle) + cos(angle));\n", + " b[i] = s*sin(2.0*angle);\n", + " c[i] = 0.0;\n", + " }\n", + " // Then perform the vector addition\n", + " for (i = 0; i < n; i++){\n", + " c[i] += a[i]+b[i];\n", + " }\n", + " // Compute now the norm-2\n", + " Norm2 = 0.0;\n", + " for (i = 0; i < n; i++){\n", + " Norm2 += c[i]*c[i];\n", + " }\n", + " // end parallel region\n", + " wtime = omp_get_wtime ( ) - wtime;\n", + " cout << setiosflags(ios::showpoint | ios::uppercase);\n", + " cout << setprecision(10) << setw(20) << \"Time used for norm-2 computation=\" << wtime << endl;\n", + " cout << \" Norm-2 = \" << Norm2 << endl;\n", + " // Free up space\n", + " delete[] a;\n", + " delete[] b;\n", + " delete[] c;\n", + " return 0;\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "id": "7aca0759", + "metadata": { + "editable": true + }, + "source": [ + "## [Matrix-matrix multiplication](https://github.com/CompPhysics/ComputationalPhysicsMSU/blob/master/doc/Programs/ParallelizationOpenMP/OpenMPmatrixmatrixmult.cpp)\n", + "This the matrix-matrix multiplication code with plain c++ memory allocation using OpenMP" + ] + }, + { + "cell_type": "markdown", + "id": "e4f14771", + "metadata": { + "editable": true + }, + "source": [ + " // Matrix-matrix multiplication and Frobenius norm of a matrix with OpenMP\n", + " #include \n", + " #include \n", + " #include \n", + " #include \n", + " #include \n", + " # include \n", + " \n", + " using namespace std; // note use of namespace\n", + " int main (int argc, char* argv[])\n", + " {\n", + " // read in dimension of square matrix\n", + " int n = atoi(argv[1]);\n", + " double **A, **B, **C;\n", + " int i, j, k;\n", + " int thread_num;\n", + " double wtime, Fsum, s, angle;\n", + " cout << \" Compute matrix product C = A * B and Frobenius norm.\" << endl;\n", + " omp_set_num_threads(4);\n", + " thread_num = omp_get_max_threads ();\n", + " cout << \" The number of processors available = \" << omp_get_num_procs () << endl ;\n", + " cout << \" The number of threads available = \" << thread_num << endl;\n", + " cout << \" The matrix order n = \" << n << endl;\n", + " \n", + " s = 1.0/sqrt( (double) n);\n", + " wtime = omp_get_wtime ( );\n", + " // Allocate space for the two matrices\n", + " A = new double*[n]; B = new double*[n]; C = new double*[n];\n", + " for (i = 0; i < n; i++){\n", + " A[i] = new double[n];\n", + " B[i] = new double[n];\n", + " C[i] = new double[n];\n", + " }\n", + " // Define parallel region\n", + " # pragma omp parallel for default(shared) private (angle, i, j, k) reduction(+:Fsum)\n", + " // Set up values for matrix A and B and zero matrix C\n", + " for (i = 0; i < n; i++){\n", + " for (j = 0; j < n; j++) {\n", + " angle = 2.0*M_PI*i*j/ (( double ) n);\n", + " A[i][j] = s * ( sin ( angle ) + cos ( angle ) );\n", + " B[j][i] = A[i][j];\n", + " }\n", + " }\n", + " // Then perform the matrix-matrix multiplication\n", + " for (i = 0; i < n; i++){\n", + " for (j = 0; j < n; j++) {\n", + " C[i][j] = 0.0; \n", + " for (k = 0; k < n; k++) {\n", + " C[i][j] += A[i][k]*B[k][j];\n", + " }\n", + " }\n", + " }\n", + " // Compute now the Frobenius norm\n", + " Fsum = 0.0;\n", + " for (i = 0; i < n; i++){\n", + " for (j = 0; j < n; j++) {\n", + " Fsum += C[i][j]*C[i][j];\n", + " }\n", + " }\n", + " Fsum = sqrt(Fsum);\n", + " // end parallel region and letting only one thread perform I/O\n", + " wtime = omp_get_wtime ( ) - wtime;\n", + " cout << setiosflags(ios::showpoint | ios::uppercase);\n", + " cout << setprecision(10) << setw(20) << \"Time used for matrix-matrix multiplication=\" << wtime << endl;\n", + " cout << \" Frobenius norm = \" << Fsum << endl;\n", + " // Free up space\n", + " for (int i = 0; i < n; i++){\n", + " delete[] A[i];\n", + " delete[] B[i];\n", + " delete[] C[i];\n", + " }\n", + " delete[] A;\n", + " delete[] B;\n", + " delete[] C;\n", + " return 0;\n", + " }\n", + " \n", + " \n" + ] } ], "metadata": {}, diff --git a/doc/pub/week9/pdf/week9-beamer.pdf b/doc/pub/week9/pdf/week9-beamer.pdf index ffae9f5527f32c99fd2cb8652d8ce941da792ee2..1cc1de250e941f1cf55f8bc1a06019a155c06fef 100644 GIT binary patch delta 409581 zcmZsDQ*(;DUEv(IntyEtcy%*`i!%bkCc}@ z>CY2kEn)KXr%TufP3uvIpXvCXOjL-3_%ykIUnk2!h1uNJwP3)hpr&X`*4HNyc!1YDwqt z-kr)X-O5lxROtiP>lIXfFrI$N?N^48nLqcZJbP3(fpPnWEZM=d@V?c2c!6ygQy*xg zvt-dk^myz;>4egG1-kH*#X%38(=Dwc(WcS%QusILJh3MAzjFqMpW_&)5RFJ94~g^& zkta#xWe=EJU`R)8N?{KNeO-^`cbc!a;)M5+Hn#dQrGf{`7-sir)E_ z+et7NL|}uLJ=LEz6VWc*Hy4znKLdDrVF<0ab~L_itZkd?JH}`j%xmH5hqQ77d`Uq=b9jSfe}Z{3yKbm|UE~9Sf~gLd%(uEZl_+o9 zI2K077Ta_^bv?|ZbIiOm5H0A~A5gZrZJ@f1oQ)sO4cOW&CD?{fiZRfu7&R>``2rTw z_^g}oobxGr`X{S@`ayUB$nMT4?+Do4iZ8_~Qa&7{t<3y2d;QB3jZ2hH=TB6MEAall z^wt%B5AVhgT%OT`x0cq@QpkP2Y*l6=*72|Hrgs9n^wUG+DuA4;=t^0VvHa7$z#B-B zMo8$ookdJ!OMQEerAErevJEzgi>*rSsfRMb2@pYrt3IZ4j^d_h6rlj72rj@0GKXR1 z$~9u!{BemJPEJQj42~Y}|v#1OsMnd_zV6 z0b%D%tQSUU#6z1017`Vm34%=x4$Sdi+%E!AFkrTSZ)cFqg8hxPq@)A`W>1|1fT1>) z(vE=s7X-@4#+n$PN(q=&(}}%gMfBO#+aFaX?)hqIgd(lckFN;=lOl_AWl(H!+5E*P znc!;o&6~I;k;rQ8#sGDnw1hXy#lpvOh}a|o<^vESmxGamndLVKCal3^*CsB(Dv{1# zD43p>!^q_O2>(Hx*6#%k`x*O@`sLBv<>5B^qX5JyA5+sdt{;#9HALEvBr?h733X$G zX7<54-WnOFglF8cgBBTguCKFAFWB#X$L9iD$&XzbJBh$hpMD1cf~??2@?#HT9tx*l zo)RlXnGDw`qjo}Yoz@e;q}~QkTH4G zO{0JZRcCWFWZHc?+%;b0krRC~bn7t5?s;*2Y@Br*n+SHpRGJSe-Q{zOq~Cl_JND#o zl4vCl1S3SH4q-6|7%`&wX6(GvCH3W~k9H^X`VO-5YSp&AT7o!*HYV~zaHAFLhIx`u zB9&Js`y>DXtJg;%B~I=9(-l@wwG56;#U~cl&X#>~wxJ6jiwF&4+>=@-jdSfEjSJ9R z#*+GFP4zh}IHxl=n5B@Br>x&HDtOGbznNHU8U3X_`n>rRMCTO>=r^!}2hW|(8|m1} z7#E88t%n>)eXcIKIFL*cy*L?hs9G;NMG{tBttJ8O?o9*O&Vr(hM$y7;M8|YJJ@^PZ zPde5Raag#|?qA|;ctJHxi?}hMV0ql3ELFl@cc7IFr*nCkVP`hxS9Ior((_a8%ZOuO zDjfi&8=DbcR@j>NgRf2T5)T}S6Xpc(9(~m1L2+b$G$TD0Y;u;x8vG&_|8zwPksFBq z@mWAzk&>Fp{5h2RVNeEQz;f>eJCp#xFeagl#4ut6^ zcd!VFm*t3TNcf~WEbZD6+43RmgcrSvJIimAS|uW{WSDP+Pql7)?zl8WzNjN_vTV|j zDPe}%&3U(hV>4Z_(^!>9k>ahq7)&31F%3Z_$%P^TRIMFAzk+%D_G5_#oh-qKwC$s|kqZg6RQBf3 zdfq2$4p5HX`=KmOen{!<)Tq_&LQDNpBx0kuq^nnEl4vtr0@kn_3q825VW6pr?EGl? zP*w%8uBVTt3F+WYlDD3OiRd{h!TkMPf9>#a_&zITj}GWV@+bDYPm66M+bc`XQeGgG zA=iH`J!&y$Y}*rA{YXs&_!};6F%or>cj57iWo{xhrZaxn;oq_YOHN8kQm&k;T$hvw zI(UUezQ0!8eh!S-{SZ(iEK8u*j}*vX<8Z+c{bc9l;n~8^U_g*@gM0j5zGiIoD_Cdq z`hz=Y6AJLX73%VZggeQy2ePD1z!dz#i9+v!o>*EhxM80+*>Z4?+`PNM~5l# zc;=7IcKyck;qoIxVAXV7Er`w~^W~F1%PZZOsj85Z8w1WJy-b}1O1lF?EzkAOeAKwAom<#l;Ac5RYm%TU>fGe4g*|Ahx$g! zwD*x=8~S7kJT7XapI%t6!64ZCR;1B~ zv6GlT(XEO0LgL&C_Qw8_REZ(}KIG*B(H_C2?T+37xiUVyruxC1KqtOAYZB4|;7T6l z$n!!wv&9D<0>jF9W^^is`dd|;m|hCL`_bZL(DxNjI=?@x{Rhxy7m8~OrSv2)09XJW zi3iGw>?bvBtLNjbQPUsh8OF2hx_TpzWjoh*5f@clP8sOw3#d`VRIU7A~>0 z+nGOUxiT}cg;V9Fu?DPc(noPgWZ&{bI@V`8!{K>v1)7oH$J(X^VGXzs~MV7J4g2)0cU@TDEM zo;Z*=@CfY+br4}NNM@K}Fo9|**`(pk>2=xg3k-2rl@tkm8q*21Uetexku43~t zX;;}>Tg)tlT_7b)gnnLNgE(D>1XrwRkVAz5c$ODbmQ5@bvfZq>F8I1S=<=0=e%@6@ zHPiGv{OX>a5w$Eb$b3Stn_!isTG8aL6f;9BG0-mt#G9#H>%e>rZUao_I|yueh2_6G zkrAiX(uNMxC?9R|B;73-+b{3H-AH;QnT-GxlD= zkumEAoGT7TAe5n)k%5STSi$2^RC3I_gFMXYqQ~@*W+7q}`j7<;DE}fDO?Ipd^MVmp zL!s@q1Jep3L4@UXzEZ0aiffMp@$_zpWXZ9>XY@5MiOWd_6M0ymK;i=*$l)0U_PQ^Y#hIxGNUB2=g6d;ML$eWC2D-k;qqhGD-+Kp`Ph!PB3TeIY&|ee|c23wR^kl@J zUh$g?^?dZWh=D_+tS7hQMLBl5!g{R%wx1m)P9DTN@^s}a6izZ$czl4n8>Cj+FSs3HliK!kvymth?=;i1(?xBWsF2(3(XF&yhaf`MVdUU} za|pEunltnM+5~eJ9)2dUXqOM*xb~Wq2|d)xnigL8dg zX8dv=pz<6ipnzEM4IEV#a`vw__zOJ$01z|lU-vQrM%6gTF9QmwUnZJG ztd{FAWQw=6Pl9m?5-X=9Pw~(G!q2Yj7R&F3KCEE#7;k9x3-0WS#jn$wd)! zy5+m?>AS(3_%kVFu*=sMid&=`^J{j~tj}!85d8GmEO3|Vy$9wDC!XEjp;vv`Fk^P^c{i^MVpQ`EjYEbvZj%EwCWPYgElu3;x zsjw9@s=yb$s$Bkb?siw_^WBB=$*5k9BqQArx_q-{!|}=L!f|w=pma9_@5fN_a-yM} z&{vOQQ#{xF_Qi52Gp);S&yedSN2%Qr-Q_P`K&qB;I=UR&u85-^^4!)!jtyX zn5a0ug+f<|7u1kI9~QT8qD8}@4n2PkhsH{7?RC0A4VzQ+kd1p8FJ8VcWxrw-%bvH? z*wrYZRXrKw6!xAN{yKVuAR< zz0!H(w-Q}cn&U%(C$E)n(o`RR7?5a0#`YA`ee{c$;qeMQVZ$N_w@82vvO$cLE;Rp1 zAQjYh9CCb2j=7!I-LyDxC#i=qu7@(+`LlI!#jQGN+h5#XzD9@v%_Ff>ln_wEn@8C! zhsZKv%EjDbHYkp^zpZ*ENu>f7^&^wLKviKS(P*JhLfA~Q_Q2rGh!fEg@`bmVFuTIHWg4MPeuyX0ux=rHgv;+VJoCKzj!(y@6K}qg5t@K(lo=%H>-{Y|+&4^^(UM1*b`~xhnT<&5xLH4<=4QPXSE>^$ldjq-1B&K+~3sX z&5HW-Ce|MTiWuK&rqN%PIRCaau^XI1Fo>sOw@qo&DxhFMokVw8uK_R^%L~u82#La} zu!`pCSLlGJIDQ^ZgMQkrVORnB`Sy5KtEn-h{LzZBb|YBMf0J6yFFol0dy*qRv8eG& zYYYAINWRp@uDj{brv1=qw;@K&L`BQrhzEKbe?Zk8$e0}I%plGIw86V*;Y%O1i(9{! zewcHlMpA=?bUnjHP#VC|M~IBo!AUo_w(rR-7nEM{Z0(|#l`2d9VCE;@R~kojRA2AS z+ufPD-K0CE1zW^986cNc^laGu?&Fc|Yq*#6xNP0*iD{uyE$KQh?>oq^n<|LA%Cfyk;T3nKe!XSv7YY5R4E@&pZ-!Ry+mbwQ=GhF8l6)V`{Gv; z^GW)No<@SFEjDa$gVk#y^L^5fUeY!)(d_$ua_HG%ZF+wAC*Sw8Ia@spf2U~_aWH||bM6Up>-WLu{iY@B1b?~}rs}^u#`14%4V0CG^Iv>y z*4VWDi;q4pYIhFcZdi`)3~{MIGX7m`XmcPAFi^iI{#J@&j##_DybxYWw%Ods)*m8M zQ%pk#2U*NQlJrxh>j>nA_NeZY+iSQjOO;^d}Q@bGn|cC|;!P2i&6( zHUNCGK99D(4=3FZ(v~$%WbAs(v(bRLsHi=GhBxq!r0&k{++*X$$IQ#+V_LNZv&VGU zb_{kNw$kEG><&(~dGil0h&8-oDw(6fZ#=uyY`gQHu0$yEBgSvE| z96(xxPn~3tRhqCpaY(B%vwdy&aZy{nOJDEQm>KwqN>Vn1ssmx;Xk`Juj~0LdzhZth zl=CC=WyDnnMjuJNhE}bJCbn{8hG{eqr8s?GRU<{}CaAp1x3)_8OdSbfhv;EpuZh{d z!AQS-9eWHpn&3n~$~c}S!9s6FNYIZpCwn*B`<-?TyywGDoUP|wRddJSG%LbxyCGMg z($VSzwo+%Dus!eRi5?0~WDMYpO%(#611Ihf-fJcO)P@+Fsy*3#+WtJW(?I{&x}Cj7 z8>^Ao5%X~+g42R43@gle8;lo9qtkZG18!8Ge7L#!5EI1EN>Wp^n!53lG)(u^bf9$> zWZoWTOnBnKGf^8d#n$M$F#0}RkXlg#q&ZPicic)IDGc^ENFulcAI^<=1 zxF75927)=43A_eQM_((ILY_FJD!!2NKjy@efxOS|G?X`ee&N!r+DiOLkd16LJjzn5 z-o?h`*oskMT9u{kwTfF}sN2;J3g@k&R2{&|%j7`OWPT%#j?>-Fi_rQnQa~>m(E73I zKo%-jBA_~VEtM8p@9=>56(6@G@^;1P3#&g(SSWEUL_<(PuAzuhi?HvgXu0N*Mb6q* zpYNK?AjLZvl0KM8pKp%mdEdH1TV$mIGLmP*@cnmvwCMDaArNcbzZkIP%qOZ8QO3{r zRfluNf;O(GvzeGv7-zRw#)!YKI~pMPUrNlOpC@bH=2M-()8F4aZhmO*8N50 z5m$7L`+PH%AKVXw^KM$JG1hF7ud&xD=S`Be(R-8T3AS*W*6@~?A6UT^1=_+wVBAD_ z_|q2I3VofclfMz;@vGh7R#;7q#9&`g<9fQ{!m|^$#q>a8;1fZFN~jw<(a0h15?1jG>rRygNHD5sL!b)BV}qFes;056U;7dti7b?(+hb&nA7a z7Nv5UjvSBv@zT$Aptcfe>80{FN6Sv0?%cp+@ZVb`x3Rz`mzV4E0q9E1e|##sPBAYj z5nzfvw1{j9IpdlTo2xtAp?cb>-YPrP)G-hcrE}3DM-Ko6J(g>z%X_}-TVmRF6*LhC zldBIqMLpT(3=GDu8a%4=!MgA;Ll_9~%>jRySje@i5~<s%|plf4n>G3SObr$LxE zdaY|M<@j=zu60*ol^{8WlijIbxVU!bdShY24!k#EZb62n1Ca@x!~;nD6ul(<7ADJ6 zdYFWyXqW)5oa8I@8<42243yky#LWzoUr6#6+GP4z=qqjdOti0`&MsS-u0p9VOo1yZ zwsf|3mCDHFzr_WVT1sbG1U!}1uI~`p5zTR~bbU7%85Dg%HsBtg{(_s=Q^wD6v-Xvw zX)E6N^<~3FU5^z*kqn##pLNb&JsU#a;fpTwDGY!_`#iFlH109lJ6wa91Ve1h5Hp^8 z+`9-*1s+ku2`S42@w$-$hsiq{NNMSwiuHV!jjlYk1_T|}m@=d9pQUuznmr5~rA6Rf zQe@cACbtX=>)4F0!)RP${&Wp-kMIx)0siq@C!gyM{~z(NWqZ_LHuS42j5_su-X~aL|uT@4Abe zqSa;p=m&j)fBODu9#-Y=a^`<+`=FqRdj+&<-6H&jcvN&YLPkP6LrWMQ9vB8`6I(N9 zb3!gI=Kl^9vsK4qHaQWy|KdF*DAS;bwsV?Pz7yr(HRR zc-r-h8$t?27ema#&n7j8e6lD z%Tn$@e~&f0R!PBCd0FgN8T02j!AWLqCjx^&rz6N5lyEL|1uByPAbuwr7sSXCa&i&qT|$ zv=&V%qGcE>Q2I|vybYfa@KKe@^Bpx`SN=B&8##?jcLIa#HN(A_y-aD_PkMjuc zjmt8u2}HjJ_Y9qz>*GcQk>sDYH6AmhD~8VA%Vhd_h^daq#u2aQf|)du_ESWq#tH=h zscK{?5i?B-sKL8n`*gIDo5|BXRm*P6SO~io2kFA?@mEfTG6eA8-mt|X(-*t>=xF^a z?9w0+Kx8B!Uo5A0?qdF|2AHu0_Pg{p9yZQPi{wYaeTD|>l8-{oFqH!5 z$sdeK*>A=5^KhYW3^_FmJDvurxbUW z3XZ7jPGU)-_m7^go)E*wcQA&c2gB)8rd2*l5x61u2E7cvJX$is4$xm!jyxI!Tx z+TT|~&msAYlWRHMsqXXMRX)vkt0t!#BsqK9LetRyuD1z!RNr01v6`BLWf%vmK$H5A zP5c^X=YM^D*FweoB2W{`=lWOZu`xBCD1sxVZj6KdXnZvshk|GRKXAar&c^(283UlX zYxlQ~>bt7v5W5*UbZCbz;QuAbp^p<$1lj`bVXu?_jK1by4Sx;jy+!4YtA940hQi2~4t6dwVsAaBNv8OoJrV_Q8;2CAs4llK(PB`DIpY(5YUF%2gH-L1C7UK_fK-OE{6A z;76$2sjo8fYlN(9c*+lCf|>GnRWWy;0YhzH4tjwAr*u3!FdF5Lkuh#@lj<=@#7XRJ z1GAC6zELpMye2up#NWSY#K8e|OCW6aY>W_5g&>qepaEuNS)f0WpfI2XPy-8t%D9Z& zg_QUF=UJG63^-+$4wyiiLOAN~f1w{>`J&+U8NW)x6+rM1lXscbWvWZP_(}R1cgc4A zw&g5#X>ol#GIkDC)7AGc?5{0~jspT(%lC3bqzo&|0A+s3&k zB$Ee(BHM(Pf(guHoaV?t4q^3ID8W90-m`jQ$Q6c+F527ic(DWUpyep6#z4Plt-|+NdPUErD5tB6%EQ7bM>#Z*n6xvq!`cUn$Nx*OC)jF7Z`}e{} zrYEBtGs@ml_Y5G>$9Xrxk!>eu|Llc*r2n=X88fd(oTAtB4?U%Mn(g*t_lZ(J>+2d{ zMHl;V6W0L7k11CDhaw1x^CI^<$Xzm~!~I-SsfF|U0BZB9@S$vNqObrfr*MrwHsUuR z#+6gDnul)WpNC6_pOjBJs@cwbdoE6HK5%V5fLgG|0l33DmsiAi0N3~hc(?NJbrDB7 zvYz*$CDoa^srp+FEca={Wl7qjS69N32#m)!kV5U{(~*J_TpX$PLX~ZL8eRGnf=LtJ ztPXl#YF`@NeMl6&$N(kvlIB^Ps#*xgXz*3c(A_yne=W25n}^9z80JVl(?>uAhMCzj z)NrH)fK6*Ea`NCMGWYm4bXLWs(^C~SaowgTW2TR0=QQ%T^x)7?wQeH6oQkuReyz+m zs0EUm*0Y}rGY?t=k%dFLJ~0EqKsMfONO?m+_GdtiF|sxq3(3*^Co*D+{m{ z_L7$lFh#l0W^$$Jc8L>}6@;3H>8r~A)0GIo>|1|VFuL7FbbsbA$nFn!J1)lHClY$Xl*A5xT{8sWUhUo`^DF9<;`ps)ITMwxQ_Fu4W@ z06qkpOy`@+S*eGyKWj*v6xf+J^If^Sns}KXdnHoNQy`$iE+6EvkZlv2?gB=m%{}%< zol-S{ZrbX54}Ln%((UXoA9O=PH3>Ta$VZN-964LnwN54aH1$xM!@Pk0e8z2i&=pr`!A*r1axKXPF z9++Vjp)SMf&yYVTfk$EG2aB*qO5R6uBDS^kBrvf%_7H`2W~(}79+^>E1~Kc2(^DEf)RB zFqRcH9yb8e&cCLE#3VGE=uWX46~i@!p9_$6%wK#4tb|fqrhS#{8;E3j+gyhoAQ>5o z_-@@~#MNBKz;i3vZF4+X=$5VZ!>QR79d(6u?go`78Ts}!$QxRQL*96+QCK>twMykm zz9BuC@!WuZ%MRLVwtap@Xjs8Jc~wPnF|QyKXzSs|Kk7x==^Y4>LA`0F+X>fD{C@qB z72Ivd7MObTkIum0VEX^q4c7nDzbN(3Ic?8v?Vf@tp=gmXZw+vVh$j^`=2-rXz*J&qz50d9I#wq21a;=w48V5lRw})Gomlwfl7%8W$VnmEtk`Vx%EUW-)5r5k$_vC#$07@0Vh`wX&C}(-1YI@%YEzVR6En3)Ix{} zAYsJmXuPlyIHi=l1`XK)$^j%3je6ab9~oQ&XuMtfZ@xz8Hu{on4d8QzVdmDvfioWL zX^h&WOnWX#F4gOJ?XLLZDSc^C!#y8^@WBSo=N}5`pN6tT(iLfWRBy{C*lOjb1 z*cSp!)?W0CRmeo`w^~wUj z-#MzMTf}d;-l+m4Pv6v^);xI5FLyZ)dnFrzKf6Nk5k@j}o#$Oi*DO2_lP)_HmK?81 zF=)=2H)C9_r4Mn^hn>2nUR@NYndHcMR(nxf1Znj1R8lPjL5xbm>vuJ=mE=oc$~Jgw zm99p_Iw4GYBha+lIpOko!Z6OBZ36EZZ^YOcI;A5dXIKJSed~0_sB$3m-c}`>G9%qG z#w2(mG+kI zCUos?bTTJ+qIPglUh+>0UluujhomN3XTvt&jrdakaBL|jH@2woOL$p_bny3(nARD> zFPR*kI=2D*nz>{~92_ICq$8dKFQ-m+@&7c@s5%y8*%}y?6jYUOh!@bX;#8KWZY4Iw zg1~J(^~d10BUfUww|5eD`_;h`KbvZ=mE7C?&Ey-6{^p$UUkAa#@oy_K*MC5Kttw-e z#faE_s`d&cX>a}LQ@x~Ip)3*|=V|VlH)2c&T(Y;#`uU7!G}zE-vB1vtG&g9-!;<6i zadqmS$1Ob9$>PYWh)ET-+b1ZW#+Vh%WJoobL}rdy#`odga8@gpMHGP5g`lra&LQWq zgKCzlW)NZ?G)QSsR;CbQgiHiLN6AuBRz7Qd5qP7$huDWClHpxWXL?kwHK73{X$Bli z-G7z8i;3@|_eB{55vQaNgq?87GKaW#z90%R8aQOs>Ul(_gz1|g#2ES#Oz~v~BUja_ zdm0M+!x^5HVf1138(cuWA2m*~JOxc{CUu@idzz?72+6LmajDrb;4lH!((~n93bU8` z>@TW$SnAgh3c@^s<+`IWb;4U%pYh?&{dn?-VhXp2B$5gG`b3)XvUFUd zuZ-Qy8n^9+>S!7B4ejkW8V+YzQnOUOn+@nSE+?*pUG6c9RIjz^gakM)8Ws=XjFXZ- zl%j{rJx`Uiy4^0KD_a1$cf7 ze@7p|A`{lP6z+`8*I;k+MNBxfU&opswaAjy^}Bh9NI-SWCXxb}#dt;}V8Wf(g)fSdI*4bvrs~adx-PJ8W0(lwayGS4{GNKvy!RHG zgF$*a37QgxAql?c7Kd{54iOp^qSa4hCSb!gp-_Vijlp*agED2b`^89KL-77!>EYRwqWSDeAXlHTy^Nj4L2&6D1cnoRCI*@ajSG46r6r?}UMVKL*h#bv*6~2{o z?a{%)ZL@*qbr2~!DTG=_1e0y;j9JALYSFbL2{Z~!s>wSdLIYTe($JY~teI1!dV)d> zffxw5v}u(j?J(WSW7K7HCOoDH-_?x<#1yvQrSm)6_{D z*z7MuszZNj$i=fZ>0H2Gp4HVmwKJ0^hKwrO^;3B55VY9Z&T$CNuC#wjY2Ms+k(`m& z*KSDa_0E*d6-c6P}SOG*UxL;T+{$&;kjezbt|eS)Qww4 zp}0d9+@JV{z=KI|Whdso5BvIyg<8180a#B~X=jjXFV$UH*;zWALv2D{LT=oBCPWptbdGR09WD}2cnVy-Q9gKe6DmzoZ=%FBD zbjj5)CXhT~N-9^HAVldj4BGWmwMw8{Mck@!@WXdYpubvZ2LzR*) z)os^)vMt0$8N93Z_z0q7s~%L9{z=t!Hk+*TZNnOE^W0>(R&)(K40pp$`bIt(!BJ$I z=C1TQAc)DP7qtpq;s|QJ3l%u*^lHNR5kce2r@u%fu$U1oR0A_%W zq^poPOj>phb!SfwDCG5*iTv#AQ`T4XrUap1Ihr zBKJOF)nPKTP%9%DEFj*Xz(0D%GvLHH+)To*7QWld~I8hp_yz+;gp1M%s zP1}nxrzo={rufs9c2uR6v?BZmU2UpnQs>%{GRSU{WpfugI02R6He7OJt+??)Wr!19g96Y^Fm~dbfTI!EPp-(8UIQ)XHwF>PQC2=9#ju@r^K;Z&+R~!{( z+w4|pXI$hNP03J}Sm9Wl&% zp&~C9lh~p+oQBcYY}h*iG%L689A<2S;7cv2jMhan@40M;i)}j}6%k5#V=gHSFoI~` zX~?B2Wgwgu1 zfaCgic;@dM%zr&=0QLWC#%zh{CA#O2=0LVfzyk{W?Pl=XkZn*ZLrE4ox4}Md@gkDw zCSz9DWq>f&0%a^-)9mJp z;RM(`k!ema5i{=VGA%Yys`RaJt!}v141eorn(e4J&h6SvRYStUj@w(h6Cs&u=)6e`}^I2Xa8M)p0 zM=EA1Hik3B^eJ)E-U?f(8vDE8pnG<;+#V3X2%P*zni=T`og>s^?&Z;|hwE|`%WRDVysaL!+JcDXl1UId0cue(rTm=Bo z+KWrhrh2;yNd4@Sh8@gZNd_E1q?EFrt#&&YlfcYBR@(<-j4?s&!`}5c>_1=%=cZ!S zkGk==HqIwI!7W2C+vsOYJ3DzEeO*BUNKV&o=B0sjxtXi3A4`1br(#KoAGRoY4BqBQ z^oVXYxn2c+_jOSasNL^uv(jR)$wsqY`1W0$?vH&()yS%|ZIIkLD7)ltgfO?AY=x%EZ zx_2$HMTz%B)r!R#B)W>_^oM6-1P~4YmyE7#;`gIPLRS{~tf1Mz21&guDga{UWy$>^ z`Mm=nhys^}zK}UQOCn+Gk?{1e#Ufbhu^nBkq2FNPG$BXkCX~V#llj)C@QKj^h9$Cy z0sZN3nyjj0H0ZVs2zKkK=iP0CBk5Tqp9?N&&Jf^+ZRE2phf4aot}Xr4!v`^lT@}4l zTq5pO>9l-S6fbRLvff^;1++@oH?Huiz_0SE|lgo>!o_jbL@G{5`caZ0yu-< zoL{&aHHxLwY35yv6mPSK7s*oG3V79x4DcB`k+L5kDR7o`Xt{xUuB^DaHA2Qg+aPL~ zJ0jYWwj9(aHvI!_s+huEDxGH=6VVlqt%|EknP!cTI*a2zFER*%h&hX%K2%m`ERt(i z`B@Z`2G0O^FqV4k&o%q7AB`^ zsAjmTRT-|(P{b*xD9jQ8ty7Rvcs-mdma5xzfIFO~+1?!C@c3NLpmrHKc}Xf}?4LNC zQ1Fa?%o&XV2rFUIMQWm-k3IW{GDTvD%9uABRZ>?cl4keWlS*l`nO?Od;YE-mHxdH?yj;dsqS9(FQ=CM*$x zBB`K2&@@c1mHEgK(n%BX5cVpze2viX>%s1+@dwXPkVP=00Jk zS>s_uO9dUiv*W!;$b=D|GFXD#DY+Ba70Tc*QJOx_W0_pc6AM$*jQx9OCYv!i(R``# z@7XUU0iVQ<(|K%pHEiIsi7awf36Ko}M@VT<&AoMx`kc9rhw=z;`)*`_2cc#fy>Zkw z`V?#?063#p#Zjkepi*+~Ilv>a|F^0{dDbWQ{(~)tNUJ(9k5cm%!DLOaO8($8a2ewwEb#omCQ!`S?m}~V-5Ee8<(ej?EmHVXz_JzJ+ zCQ|cD#E)eoAqpbIPJ6M)j>JS7O`ba^xNG5AfRCS6zR(&fX11ss%<#!5boeZ}9lp5z z6&Zu^EXD4rEOvdW_tZU{BM&TBPBa&M%xhx7dGb}e8GKeV9G4C!FD-XDyEMMWuDEV5 z#Of$aW6VPdRLuF|YjQ#BkC!4_Yv{1Pp~9(#=bc|T`AC(^Zxz(m$4r!R=A$pB3M?J zl*>B~2Emof!{|6`###qOuuilv&Q_!LhC;i?q~V_!@?}Rt%EjU|^52gPW^pb=?Fm`7 zbfNc$G|&Z3)|+EZDJ-Y6Y-?1`QLy`hNp|*(r=M%K$%$b|-LuFd@5DHj1SC-1;vN+~ zlpDk(xiaI9UfpjsfB!gVeghc2uxY$~AB+muFUQP%LTfUIWI7y%zSBiV zt1Od)@;cm;BGt;(mkt}z-$C)Qu&xe{oLpJE0)t^u;4&t{!o-vqOcreGbbR~{@0W=s zK?=Z*uo34E$L*=Do5Pm~%w#-8%u(R$H{y~c=*UP+9Y@BQfJCp`XaeyZPaEDV1Xby- zDrFrt9!4zF3I@x-4bRXJ$m7U0tH<0Pah@bLP32|+Kl)XZQ^2;bXU$RM$&MYcG%PU% zadR_rKQcAhc*P;8Q4}*0*`yLv<(M&2)>$BI=B8CU-m8&r6&EKuFEK$s$=($00(Tk$ zwXm=|j2SWs;5W_CIWdVndbX#dL@E(9Cz?T!@2%yd5YA*YHL%xscMao*B4jjKu?Rl% zw#&1-SYlO8M*gZzs9GMq$VJgzRE2ZQ`?RH#*<^qs$QW+0)YMxa@ks+}$>k&b0WHCRMl4h{$&6eX$03qi&R2bXoy5oF$B;6;xnk{ zYB`Ovb#90UMeb0T={jw+g}24mKk@oY;aNz{<_2O+x+I;y0YQ#P`{{wbaqEAP^^WbC ztxMZ>*kQ*`I<{@wwr$&2Y}@JBwr$(C)k!*jv(}vVdOtkd_zR=ztU9WWy*Q>Z_U8qJ zsG}j}jFQ;k5hW?|xFzS$80}^$h|>NBicBBrDIb!P|{7Yyp*m_JPY~ll!aV4HS8~ z4*fqr9LjeTfFOb5v$OoyQ~M3Im~hObN7*`|Y8+$|Km@VgG#F1nMlzKaO zS%LWKU@bhWEx4tDfGBh}!~>rh@?r#*DDqJ1X$RJfdwe!{KOe@r)e%HRVi}u2kU0y! zL1Rhe-#H%7ZUyw=9<$X!By?~&qTDb^9|d@wA~cuK9PcwGMaLdTGU8vINKI@g9tU<) zVgxt_lZw3Kib>5FNQSQnoDzv;bb_o^0TuEd*QusT8;~0KfJU}fLj7bJiser*SJ^au zn>>Ki^|Kt6M&)G+C~P&nf(=8tA7N*p<4~;urs)O>RTb6?HF0j2QYj-hfXCH@i_j|k08LUCrJ|lNiPf6xsWt2$zPN*mem-T_RVd~n@9{6ibu|ww z0vs=mhK2g+-__>xsVshA*|3kGg-wn+c}+SN@u^{}JW}bur6R~#vP%XKyONPRZzYGL zKp7zrS3se>Jjl(cf*5$|jickZlvOFB5y2%(@1K{j07B&>v{@%=9WAbrCHXX`4BwiRjx%;GbzY&qZB zE?17^{~(jS>cG|^SMzcjVpm;x<;j$Qd4q4*StrHVW42k@D-Xc2bR#cL&!XEtXTn7S zO7{vFp3Yc$zHZ2}B?#BKKCWLS&tFf)St9S;VmEFic^dDluKZUAMVn+SFRd5!cKU?sWm&PS%5gnG zLelr{j(^fCB8zTs)iyBrG5Fn)sbfGP%Qn!Havw48LSYiGZF>8n{8=ITPn;ctV#Gw! zt+-xCWNx$=hz5j(DD+Fz5nx7Z2)j?3;{xLH3H7>G&aq*HI7WT7Uku_rwCeWD*ut8V zs~RSM=)mV+DC_06tCU|)o_pQhVGTA+bW-?nOKI}Eo7N%X(r45MC2bABeTNxt$%~_= z>*47h-Q{Qc%tIe{?pdf4Vq*_KkJGOKTKWm<7HoO2BW_ZGlm+7B(I*Jw^UKkV769GM>n0pr)WchI{$W znJXEUwtx;(=>_!lL2{hljFk~v+e%orFx%WlVAze~8xXB)sj}YK6%L(22=sWDtZSMj zywx$(S7jX3E=$f&f<)B8%dP-%9iI8(XADSa`;yh%{$?GhncH+pjW(^xus~JG+|n)% zG%oaHy2X{}h$(|PP7^k~?lib+$7 z*?VQTl1W^uEeodo%9JjgAi#y>mK1EO-R>{mNQWxm2GLgUOsH26fhr-Tb6*^1sk)P! zLv~z=!EJW#GyVA6Q?jB2pEkZB&UC2FCpmG#$IY1J!OBn=)b2t!i1RMrxN})JStijvUg zHmL!o?r}caRN9XJ7D1_}aG=PEh14|P&ykef;(tZ!ziBEO$Xu~g&kHL#rXO!$^UQ1VwSAeP(vKlwVaJ20}lEy#fQb>Emx>}$!X@<*l%=8%sf`&K8 zLTN#Ea=Lt=64cyb)1v_^(Ts6t2+1RbpuGBybSfhnM}anLZO-GYx}$|I+`wzC+%9qB zul+|On3dj9Xfbdj+zw+@x)s}+=+~|j&JLWUB&qIoQASvuY)AKDmH;n38<7tSZOG9e z|0H$c>iBvI9|F+oZp#bU>+|aPd!Y74MOM<^#g89xCo)U*nz#M9+R#l+q%P|tK;E*V z=~s7~!CH-bKn*Nz%%v)E58Zf@h7I$JdhTxGJWi%*o%X{^&G%?~iJ8C$FzV}tDscR{ zOk+_{skzJi^uf(KumF1J2a2j@JMhg^uN@%o!Xyt82pJi|pn!-7xpuc7hk2#6pBLu8 z8MU@Q+ILQB4|(-|(Rr|acvGt0n5|c!w^wOQW$N&mwX$_d1(e*9LvOznyO2wsav+Z$ z$uoT>tHeMtmmCbnXM1rkEsFL-syk;F*O~f(l~4hR+^!c;W&)RCs>r(PR~f~;z5 zS->RC52*g=$R#o-!S13Z5B|z^e}| z1BkZ!)XUaJRnyWBA&eoN8qx}XJkz1x9A63hM!og3V^OUhuA7&WErB}nL37%7L$UNe zMp)X~*){{I%@D!B2sKugH*8YO9JJ4M!76m&taBdLCLefuUiOa<$xm;mKgbCt#|{6q zDBtVJMPMi(%q;(>LG{`G!$EU)_FHQ*;c|$$<5>+U{rZ^7H%9?gVCB9JA`o>`-gR%p-MuET?M3 z7sFViotLDgA#P*&#_~8Rim^x+ESjv2m0nQ2#(`dh`;`XpfVse@^h>RW>CnG>JN%jRBf6zQ0r6?oPr0kh(Wea_g3)VXxb;K`jQMPgTatd){&Z z%BPWt%y+e1CyEgSweRClc1rbSFN)t+Dr6TwSyb$<;5FA{wQLgrWrTKij)^hQ!0B?_ zRG;S^!TC%2hgPGsEJzoxjzYw_qMUlYkqs664D!cP3-6)gQmCWL=5J~ju(q(dU!XI@ zfb%(JMU+!)1Y{zQ^R0KrUSc4H0+CakpTmUSb!BvO38_;c@n$}UwU36XJ?MgXM+!SQ zc|msDC1^p_y|fguFfjIZ)6H%*o!Lx9vE*)x?KXHwJr*ZN?0N<^O1X{IJ;|Ontj<*? zC@-h#%we1vaf#aV@{1e>=f(NIho91BU*`6w(9c0T(IYzau?! zw69nuMV`ZS-huSCiVeOHGk>f(5BFw!P0;r@(8}rdu&0s4W%!XPM;w;EZN#6FE6mL= z!0zh8F4)ybf$~Ov{UnQrFX{aPI(_Bu$)?YIo$VCY!Ur$QQQ7u-XN>i1^U?^r(@cM; zWH%Ut&4(dnorDtIRO9aaDsW@M`v6Yb%+W{%b^s#-WuyN;5lVIJJHEboD&L&J-7pdg z1(xGVj7Ud$;jsZ5iLNSF#K@=aX zxq|q$__e#xM|l*IEaP~7?xUbPFcxSUu^n4*9uH#7{2;B4x|qzd!|r6ag}I7Yg%EY= zeD*!_c415aBD1Krd;$}*&Tq`mb(>>>QRK=D{3!L13z8_yD%Ntws?4I*q26G5sMHQ5u=0j(q0UYz{^MzHNIV(vEN=g1eoUSg5)X*G?m=# z^(76O|9!-ETNsl$Z;=~*^83S33~@$-U4y3+0`V&N*yCYqS8k@}%qPi>i|1sH2jTkh zS4l$@z(^~9io1-GBg3(P{yRAOj2#v>9g`|F5|b%XM3bRA{h6B z*7Nb|Ihz)~Fk&zN`pA5*WF1CP1v{=@N+qp5mF}Y>Ri}fT%1)c3>OQ`x^G`~U{~dLf zRc^5Y)fUzf%d9Dw&7U$AJ@lqz5n}{~T#|iV0GbPcWL5cu8L0_%#uA)LR!&L#VjNGU zZ_3c0g{+&rr(VGkK^suR|C4RJyMh3O_rN_@j@EIa^$G!>yCi8xgez^JIuPXb>27gb zlp@sf0H^s1NJ7lA0PeNdex)3It;7P7y92q0f=kartxbOBSrtvFfC{pVYbWuhL~lD# zoaIkBi2%k3j-1p|c|lP}9EwZJ6GZZ(etNwEZnidcW5>gxr=R{}Ip6W_QtKml!iH5Q z@%5+duFRU7^ykOV2LsMm7@P`J)KrEPFqG87Krm!bRwnv?-ww5JZx}y9*8|nDc@X#3 z&13mhk)l>vYWXPJcpq>gzKE-(Ii1Rvm)QsKDuW(yK2AcDo0;j37uTCH#wKWB4d^l^ zM|9S(9`gV{mVf*rekDIGED8nn5563dF84u53}{bUbHfKl-o|?h+|D;bqFv}BMv#4Q z;nH-FJWNhFz#R^0-*brpxpW-bm}k1#fc!mq+EGu#<2c`jemuNITPd1q!yyAE^hW2& z)uF}v6~ZhWNq@0FGSYD!wI5ehlApX)QZWcRUtweXVNe)rM1Y6pm4Jv#m(U0M(<6K0 z;4Ef=H&67i;3x-YRfS_@XXyjBLRHXJ50br=DFll&K#<1yHqoNFGCU_|QeNOe?T4-2 zNaj4AUes04J*9rtec|w1gT*4h-Qd?iky)b7<<`qbRQR*>Z>iRy>*+v`-oJkJl}p%e{1^Rmq)h1^;38SWIR&`oFB z0?t)+DkQ)6`cpEwqpVht7quR5FTOLLU(C9~N@s0c47{HOyH4DH;ls{bkp9J3rLw1h z;e)a={yR-OQk#fb{}#U5tn9-qUPC;3{F* z+Bqzp2z6n>SfUkp$e%wqXIMDteyS{(_TLdh7z4{H1D*=ackau$f~z>7qUQjr(-r9!E_~kUDD9Xj6z2`{zR{ zF;9jbm*e(R7jkQ=?Ua;%6N)o0MuIus~lJst?Cz` z-tcIJR@L{)@Tf+ta({h@vRbEd|HcqJwD#_1{ImP3+O3^;k|iWyVwi{Z#Ma8qPzUaB z0r6aPCW9-9t##5Pt%j~vb+QJrhv@*P)eI}9WH!&_C0gruj-_=N`6;GP5U%GXv4hfu ziNSqS!UyLC0P^&a_52@FEwz0F7&>*J6buEFh4J65t*#UqyG(wBkeg3buOQIG?2jNJ zy7+TR9%akJ%*;UKWjIApnmFb2nwwp4q)nDOti=WD0s6OTrUok~SN$v>%{$~sOt zdwbGoKV?hON+6VwrI;%-m(}H}8+T44598PNxYvXETRZ`D45YCf&=!xU2jG_K;fzNq0?1n2E7Eu6yH$n4lKVFb{}Ou z9pKRb$%vh|6`a=vd#GXhmz3#E)^y*{6thUhb6k3wu^>7iSc22i&_BE%oKzAXv4fF3 zg*H=q?b*$7?yN+~+j=|C-L>!M85}xYaj>>+kN-#`*c}wq}4qZ}vLj zXJoQN&d+OfK5v5voecbj-uyU-K5137Y%ZpqreS*b9NDWDf39zkX!WlWk$xpWj)6OF z)5OnIZMg?SdQqOd{X|U+Gf!jBxN+^7fjMy@G)TJpCVh*aUAg|hu~pUEjp4#{}|P>FHUfZh~H=@#UY)@buUwH#-J&10|j)*PmAWBu*D zKI?gmFhi_1#;#>G6;-5-My(vu{nH}nB6w_&{ICePs!%~tx1;&(Zcv@BG>NeiGV4PY z#Bi{CYLL^r;Ak%-JTE8U&)GFDiUs`PjwGv__qofVOJP8R_)MeNPK;kLK-%zz+~HS? zcV!xV8_)~k=R*-#)Ln3R$E1<8u!y8e=?TxWf-Vj6K;|Woe5mwc0Xbsfk;p~GVAH}1 z5d0c;G>0}8WvD@}jLfXfSdc_{(0rIuWq{8k{n!8>ZdAT7_JJv?S>5Izk2sq9;I&0C zZXh_)m19QUPOlpz`-$-o08^=xQnG*7crcWg5pj8H@S=l&8r5Ds&7yc)0h8JBk6&(; z$R@)r#fq-xT@%+b@YH-_9NxvSnAVTpay@zdS4I0=aMGEt)M@$>{fX^^E61eljYFH( zLMrBOWGTwpyajazu}A=2rT+TTX-~W!hAj)jQ>LqdLg{I@LVJBoz&DYlNsNd(xH)z! zuxP5@rJw7xS}25%&->jMLvCGsozybg=MB|n@OuAk+INh=u&jey=_4Ark)Nnxr0tR# zH$d;YpDN)9VOqO!h#TvTcjMN>e2@S_>UaUBsdm-g$rV*S3gn6Y%7#eD17*lQmI(Mu z3Aiym7B+vKlwODz&=VFY^${>@VEXc;@rfzV=o*XDfX6>}MXGr-3Ydw93JHK1NT_kg z4#|~@=bh>ZW#!d>hPxV=+@%8513xF|2wZS0$g6ZY+c?O618*WNwSQTlOXb_<_3mu< zXzgmZhJAYz?n*-q4-r9yN~+Z+v97{GJ+|8!m+7XtH|6^TSdZa8MMDuMZJd(u3QZ3#W9=Ax0yuHJj@@8i%Od9!l4GR(r< z8Iibh=VCcs4BrmJJSoype37`!vfe0Qzo@e1vl8%d?c#jehH)2^`aa#r$HiA(&s{zL zhKAHxDZKoLc;8=ajO?Sq<(AJ0u19qm<*Tmv)y+!H-sxTL(bD0b?tW~@_rF;Sg!NxU zC=cVEVTbKGM!g1rp2n6NX>pgU{otN9r~#QROFXP384;2{{l?z`NU^ zTcC~7)}nEE`!EfpzZiQRhPWRtCAT~n-x}Y(10lu!MJx%&L&9|$cNfNzP*uBmLeqIK zgt{XSbp;KmW8!1I|JXl&Ey_heljjQquxnI`3kuQUbN?|PA0;HZ$v#n0sL)PkV_Gq* zbE;TxvLJ~eZ-_2E*QEL&Pqkq=s9PwN%U4n1cr&^?U~uO01o|CE&%VtBq#9Jj48`uS zxA%rNSHqOUjudl{(|}UM!RC^}Bb`LpxCp!n(3-2tU@+^2@)=CGwtw1}Ehwe{NaIeA z?jam+>ffv}6(IS!q{|J=8ulMxGdX`Gc<`z-M(6jIBFvMsLW31=$c6m0o29LjW5w%o zXeL&Ine&7F`WTc!H;sR`9TggB+So^8ui$*c*&Hj?_D??D`Zl^7dXPnMraEh^EHiFQ zyHr`U%JGCW*Hu5P+0XrM0u*@y?rwBXqZ>q0D42*HH)_sRQQ36{q+OehVz4*rTJ-m` zX0ArrK2KU1`aT67G|hc75W6>YGSyUk*5|v7yyyZ$kBs18kHO0xz95G3+0T}P z$eLI7iJCL2H=|9CHIj`)4Ks#wrtakZ^yPAk39fPQ7^Oi}D2cC)GbT+4CR}^FSBI?c ztP4;)A+l%`eq*+PdrFD;C+dC?r%)GLYvEmYzPzwedHRQ+w3Vt*ky1dT=tuqVfvYc* z+$c(Rz)`RACtg~?IVkr6Y>n^p`>YYS1Iw?CZ`u|LQr(x?Vb}Qn4btEK6b!YG3j63q z!0d2Ra>5&p3;|ud@an=A+Q~t?_|ndzWH4U_2rwiRr5v%P4*%l$dH(3%=qym1o0O1d z_SGB<)`KlKi1AV$ms-AQk)9okLrk!ZWIfGRW~ajPwUlbIgpz6rFjiMxk+nF|06*_L z!wvR224@Iu-Fle3!@zn+^La#6Qq`k0%`ZuNQDgvJ3K@FK;RxR#uN-YUmGG@d(V_KJ z3{Zh>b=|?xtADL8sq-E8|En&^Y{0fZ<11?N0tw-2`Sy4lqo1y1w1Wi4koD66@u?1J zpkp*i0k|elJ!t0%AYH1Vp{f#<{7V4XT48kTySyEN1M3apE(rbTsQ008R8&MwMrBo%K-t4@9HqXNbDPKq zv(EcenW!OWvd(|fq4m9p%SS79%^urR95Vo;c}@|l;^a9HFyN=aB1e5X*YLX}>(kxQ zGp$#q0Qf;^nAT9>4jHCA$MCo!A|0QR0huu;&uhEEwnfYI%k=j2-qi40rxo@LUt_M!5pE@yZSUD2JF2a!l;XLsm zO~>s;`rvl4jc`kcGnK!mId_hyY4_da$sjCR@9tI5RWy!R%z@kDH!ksa-XcDTK-+EJ z6n?HZ1}2Y3Apt$5ysbiA`Xt=PgC%cUcRYc;NYOp`q!VPBDRhu2fO(%@ZV%Z*hb}Xp zx_oFcksH5d=-i4dn7DdgK&3`a^^HhM^-&qDkj8hYBEP!wTO-9yU=+QZc;6zJ^jag3 z{)r<{2d4_hou7xaYat7Ys_`4Igpe3>I4*$L0bR{8;m;`(S6!GwuXDGCMQ@BMoL6h~ zHz91>^OQBfWz#hrpuJ;H>Tp50qxOqa<^za(+M=*&s5!eK;7u?0k$$Mt;q2S|-BMp43z_B5OcHNMB@Ud*|b*Zz38s4P3F7+~q;hCgy7r<8{7YRTv(f8tt|&901|&kdm< z+WbuNGUHw>JIpjCJHq5$f%Cywhew)XnVYU0>2k5U->t_^aUW>iam772ESnO{py5fd zq#@2?{S>T7I3rzFyk^ro$3oFh_2aRlBg8J-j>;5XuW@^ltOvO14|?)H^*HS)*w1(z zNDz*HHH0SB|Ltpq2wJHm&;Vfn$eWTUfif98!tk)y&6g9q)+GxX{Jp6pSu4wyUMF4< z+F!C%dvk4?M;2iV%a;RPe`Ea1`1cd+r-<*U#3jrikt_zh0>zy7ty4tK$K8d)Kp(Fz`Wm!I)tu6l4KVz#upYl+XdJ%xz{K`2-iBQPH?$*4Z2+v-v}F3avM7Wn0Mc%5ig>Ep+d*$UHc%b z-&BX4&mNNegjZ(9-J->{qTPg5099&Gw~PzXn65>ZULDAy$*;Fd#UMGUDGR{_b`atp z0=72o8}V`Yx7d-S=dqj0kEwz002DzTh}Z$r05vkc^o#qsb<_tqV_O0-p!C+`aD3x8 zGtSb2vqnUc{-$(fF&8C<^8o_!etLQt63FPhG7KW9#a^HcbQ3e7r*P;G{MO!`vfpqH zX=#78)khv@o#CBRd0AJtKAZfHZG4CAd2rfHK)rqGtI?_H=&x8mzmuMxnzW9X&)@W=f}1lFD^%4>bMxG%3qDUYdh_{ z_FOZWCLy<)F(I7&TQrsc8`t_ZZQZRvn&``po14$@WhSl8?Qt8n9`OipExS&s-d6D% zpK;gKGbRIZ{HB>GR;Bo^0OejS$~4pouO{uy=V?f#1{}(q>PV3*3{s$^&6qzc?`lAtI zjz%V%lCI>vltcVloFqT#Xo-2(*PV(~28<9=ZaDla%c?CqwRzufAsDv%$=;0Eli2gU zj0GczLBwJdNQRt{`A0I4$j!*I7(^^NIEDgGO`{{FFD))TKcloCH7iYNp407zlj z9;AE-KtZ&Z9)j)mpq|#OGv&Y+Apwjx+(GBb0+#AU%Sy^*k>bz?MCJMzn^bd0Q-UR) z$N~*noo1V>$UWZW%l5C%2ud8734u$@NOGkJNt?z7L`@=ObQo~{!N?B*VDXUoumFA` zNsGX-L1d(p^=UMgnjvX&D#g~cqzQ}bltQ*{0J@~I_j!;4-)~N5vfC2HV?wbH{0Q=Q zRdyR5L@Qk^KLSIA5V!mJhU(V@*3g_m{>S{X^3Pfb1)St*;WUdQX7 zZHh%BO!ZWTJkZAh-)TUmP8A_U=2V0ud)<;p%H?KZ5A?7C=YGxm{A!~A=s2zvP! z;8wxN+(<^IvbtRYa-C0W|L2rwHiO|O&fl|MR@A}yB?`|=7|$Z@Pv5e*Kz z3aSUsnq5-;q&}_5+F17KO2GJLXcasC^6TQ_2&b33m#sWlMlOr>kpB*AzzW33W5gBa zAoj+Ew01Y6 zXwC#*Y2sIoOlAJLzQ!~UPr0UIu~WVBOTURhf9^YLMu7E0vsgouIg(@Kz^TX~nh`>9 zdqHTX_m#$SVhHSVB5k}tCWKogMvmEHEtrtDk(=x?YTy_0`^>;LpUYWa`9IOZo>(i4 z0!q(L|1UUuCNY!s+x4NBM`Vj8f|TGVv61e)?wpX0yZ_oe1{`QD&QOT&%69b!5F12{ z-8p#C)Rm6w+80~$?fj5B1@&j)-+iWPbQ?O4SbDT05y0OC69on(CPqMp!vg+po$#nR zU!aNn@D-k;k*ooe@H{Uj4v+UT2*AX;v!>inj*>Q6M}rOUBz8FKH8m1;@&v@?D)ev zRGEe9C-ZcXk{GELJP%6&(&isR{?|pqL&HF-@%X|db?u8o^?TKpE%qi}SX(ta@*H*d8ashScqRKUwOPCTrLfCXDB4>(L zL-6=TjX=fGu;=>ItB_kmMMR(fpkdt(Pi?G5@(Ex#pf_b508FN> z6JP0JJ=jWURZ`i2tusJ@*1oOGjEgg3VSuIoBKP82IS5qv%^H;Ev>uv|Mag)8%!N|X z&amCwF?*-m7d->{z>6P3ZHW8)3B?5C#&@Er6{(R9q_u^-D_~Q`8Fn%ETgEj*uo9>Db-QhK|c(`qD+kH=WquEsZ5F>`09T2eMoH9ntV>zNn^AlY|uBrCfHG#KEq=->TcSBpn)r))%4}34c?)YIWotMp%l%; z?s%b<(R#$OI2){tvdMgR?N7UfNwk>f42Ycq!~B`n*Ed+UL~fKxys!Mnjs0_~JW*ww ze>2LzJ_DwI2MtH6eKG&>89Y;YJF6I4lgZ-r;qrrgH$0Rr^Fqxot~w+R5{l~l?_7zi z7MZk^TPdg@a8*}#?xx#OBOWMQ_z~Mtr+uUP#`L~mP~UZH_#g0nM6#&RQbls?t{%73 z%Xo2Ns8A2^fGa~!>k|`3&)4&)=)Vijiu&+>8_gmG;g|XInkKT`JP0x-!4IYW7H|`K6X0Sn{x7OE1(6AJZ>i zn0?lk(}sZYT&QiI9+opKKpqMKlc){>&Crzc!p2KX_Bp~dXBPzUJD@#mp!H!Un5R`s zR?9|Q*Qt!@<90I+=&?uEp(Ts60l1r!3s8+b2_p@0On^Lt3wywD2lM_u-=2g{#EDCi z(@Vv`Ax)&s<66CA*HboukSN@b`rI!1#`SGr%ynBj&%G#LlF$LCc?|V}cF2XyUAqpq zelBF<$*nq$RZ1LNG7*8gZTfi=HShkWzom5d0n5ba;lRlhnl*gU^H&?l%t`OP5O6|z zZkx9p7VPBtz?GgN3pQ8^pVrm{|rT&BbI6^WXMA>c*8iW3$wL3n z(ZDTD8K?hT7`jl~UBwj!imVYCPE7Y+v@uJN9hNm+_w69F;Y&!@m-iYLeZ$OW*ql;AvrRzNL1>h&oM&<$-`Bd zEG1Kwb~RQ~#Q{ARFiA`{S;YX>cQ(X?1UPbpR%4jb=Ftrs$r%PpLf(@{qT5qopRGrI zPsL$mn8ru|orDMe@u1N2gj#>8M>!}61*SFP*?!D8c+gD|K~J^{MdTkL(xZ^Li~nE` z2Zu#1rY?#0r&2yC9wEtR?tBlHxZ`jsz51;93>FyXq4c6e+u+Alnr7!oMDzcf+Y_#LNEh- z7MO@U5FAWXobafwCNLS%Qc!Jy3t$%$AwJF$T4Ko0mz*BD-}07H)PMyG71>sa7*!)` z&t&pQcs+o9L zJM?g=l7+Qx6$j^E%YT&#M3-5uJ5Wc5GCpPHC86%2pt%Q;}wVV3w%Z~_uCcqlosw7j7`oov*)ui^F7{8}Nm_e(m(xmifg#Si8R%xZIfQl*&73pLc~8x%ta}4Nk!QI`80J z#>Lx>WjnaoNo~OcLFNQEW7>j`+!w#hn6o{ZF5O0g1o3zvMg)U$*OPtwKar?=wh@ri3OI=1qpJWk-(}JW4y|Q()=pvwYoGF=cm! z76K+oOV62L|Vk_64htVsHdv?HGCPp;rQdV zGEFu0_rBxH@yCcKYt_a*R+&J-*!%JE#ev(awQly=ghVv+`GX~;uwEr;YLhSc&T_?l zbN9QpJ45cGi^j#7)=fngr8~I_H z@8hwtI(B;ID_g=mSeWGLElajNGLsOhIJp&TRZKvBjElGOo^1o;pfq3K6Xpb2KsXHj?7@^^0mV%An4KHU zOO#f|yl@mTfn4!yOKHLaG=Pe&^2H5_xM&CY=}R{;;VZi$+S_i*mQ1K3W2=-toB79>g>BPZ&Z+cH5$z+#g zpB=1%a*^yR)%<2JCWl~h0M*bWZI{ipaaa7hv}EUq1U3pg941j8dc%tp-(YrW3!KuZ zhNaBdvrN2+(uAXpV0GiOKFrGi`Qms|P2~Yqv;h(oWPxYZC6gfcIV6KP#+~4^tQ?ss zqA-eA_ya*9q-c;wc&8>jh)5jaEBWk#!{EmizAP+-350_pMje_sfVu6biK!#)W_6a31SRaN#CgXMpix^nkaAJy;-ftTH5@I{(y$o_y0du_KL{6@3SHZXrSEpR$Ep2rYX%|Hl z24qh-h5QT{qqNt*fXRyW{3f-91%*D7Q7HIo1hMITa+beA8-W`~!crHy9H`Q~e5~>+ znQxeVSC0FO^}GA#9XY>qAW92#MfRr}w%jGYJZ_&BJCn$~oL9$prQ;Vg_@c`H+}5=M zasUWq^a?{b87cK2i8|vKsS;yDWfTgZ@k&~Oz=grb#WVlhI*BZn4wjA;)!PG+tUDz(SQO_2YU!xeQU65K0O5V+T`i-Cs4QEzg zuUS@@`^D9FGOig1JC%)cU)B`Wu8)3E-yRly)Axe1k+w1&mGUL{V*&9r30u))2Tu1R zXQz0h1a+AIx%do1Pyc-~?R$s+U(~pUl+z(I!gmn;JuMsTPaxrgW~T<1qESH+8KoH$ zsAIuI#r3)+TI0XlvAEc)B6&Z`_x*M#B|H6d{dJj%YXX9G{U8?qxaFw;#OG*5G6p zU?7QsNcFQsY)?6ikJzAB?os`7u_Q+XQJ9MPq|~mgMu77 z4@GJzXxSEsF`4Ew@}+hi02?yTsU~Od{O(fRx3m(ZVIm5?kkX~>b__(rEn4*{NnR>S zz2`p; zl_R5O3iDumc_<{23bT27u;~&i6(c``fG{J6o1~v;CQ{or-8VXMf@BwC<@c_ADv>bc z!(M;{Lv8YR(NDF0cx0j zC^Py(Ie0qppnJLq1YE5Pak~)U5P>7a(Bg!~;h>t?tv zOA2HxW9wxYKeReb?6&1ikBpr;wtAh}tcjVDa%dEj(>lu$wBpJRL6n5YD}RnX;R`cD z5n`zLKtBH%pgVfxyZXj?^)_}1M(Ws=@am0~EEL2(fR0aQ0wgwav(qSy@2t@Yf$!@q zgz9rx&APV_1zW`UQ6gH^Vvj{)5zkreVAbpNPr4~x7Ql^HszpeERVG=^`UZfMQVMAC zeztSF?zn+%y+QE*K!6cH2EE4s%EmM{W%xc8u9a6X)NjxTSrkUP1y#cq5KjK>`S*l{`}E4d8RcHd?J(Am2om$qNx(lf|tvwxM!R^_nm5?u7p9>B2`)bgAHh{FSmWm>%Os zhymR1HQXiklo>_M#Hr@?IlEN)iH4K}?H#Uga?zMJ0-yzBc$K1_Qk5b~+N3;_3>=~m zr2kZAXqc#pWma23V$Ja$7@<$RZ#_Ma5FFI~JNvIb*Vvuwwe>U^EC~cARzCS=F=~Vd z?F*Eo`5%zxr(!UOjrCA#jpU25>5Wok)y=Vb6%kmm9|q)SniJ-1s{(V^MclcVlR4bD zw}AnCth@X58$|_T%k`hhpbAfdmUqR9UDf1p#cL$}UE$nSGHC?$%uPz4?F16`64Q z=)lQC60Eve-Pp4KLKMug{5dnyq80zVd&2^2&* z_7UNmSztYx>P>Jrgu+A#OJMhPh=a))^8kYep{z?5*N*grxZ(9o;#|k#(%V6r@6x^I z6$|z(frUEB{q%j_8XD6RMNn4#C$X}AgIQA@T*0Wm&5QptZY0Hrq5j9Xfvfo&#*@PG z@9|-;xUw{VinfGp`|q!9g3$Yn_VAyeJjBFe1?e7k=2I}EJW!*d3biea>X?;!eMoWi z$IL&$_n=CmMhaC5aeO%3&kmD|iGSlf(O@ixj@6Obl@y@g3?$WlyL>0VY5f$a0s!Gw zNb}**Mtr85sd78C`jwZ}|BtPAimq(y)`nx-wr#s&+o@PpvAts3wpFohTNT^3^Jnk# zzTbawPMhm$UCq{JA7k|K^aqGqyWT49TaoVPjjxoN8LO!(3ejmAm@QlOz;8n`%Z}b9 zll=p~+kdk$%xFZ}mgM9zD`fn&c5I+w^>nj@D+jK-(YVrj4pNT+`~tYfI2rLjN35lm4lg5eWrXw>^IUwb zil172sMCN|d%_}5VHm{s*D^*Z3uUS7n@z!aRTUogL9@5zNmS)fGkx)#dEigK9fF`u zEFgm(JB*`YARRZI z&z8B7BLdM_ug(6!0-y3QVe*Rd5y`Nouv2^2?k@G-=jm*te_D@>|7Wh`69aXLHtw|G=yIata`kY_hLf#|H9`is%crNhL0UZP z&zL~V%@t^h;Pf;8b>|YkpM97$c`aW3jy9>fed(gAL!?nOxeOM%J8x?!#f@c*K**xQ z6UAl15o69rPj#cdfEB+bRFi+Zocj79m2J+{`j@FXU(!i6?5EHbxQXa=TTK5+#?K zD8!o81R2{<>C$bhKZMW%jPShT_^3{&LAQLRybHuhMCIfyGz4ad0wgTF`u2W2H%bfR z7ZAD*OKpLWP~zjxdC}SC^uw2b-vbzU9xZSL;L0cD1u2Fldt?ByRb4?6Lz{dr!9dWj zlddY31EyF*sH|;Aj$pVx96LSPRX}pUOFx8po?b>cteDvUxp*<>)7|%s z^TYfdK%$=9bO^&(&a+ET^sPWFLuzQ!TM#I9{_VD^qP$l!Q^{SAQCVMmSUv z+}_{{Wf(tG7aiQh$Yv3dY{pj7L8Al!?7M@>W71#I6>uC&P5gdBKY;MtxBQr+t2ded zp+!^^sj@nBh-={tWJMmWMn2b>N~SE4pQYjyx}E~w*mKA>mCk3AD#dJhAi}YM~qRM-1?GZyOmvNpK6>pFvmeIr;xjFbkv!(#Hx2_86r)BtUIaO^u zCoF$fr^d|9^4jhH6MdyQAYAx?x~030PbsYfQNpu~?df9`3wau&{{7EVB_!j&;}k+3 z{5zI!K9SrW`ODk&WUKp*IjaW_Io^mMa=-`E*j#_f>&Ht*8{D?yr+(gbuO#Q{)?c;p zua1i3FI^4#m{Xd+%7B&}_cc3-RN+Z`4az8Al@inhTJRO?v0Q4%zIX6621-rm&}2V_Bn=q&dcg%)Gfr?T!j(0u@^Il z1qQi`N5w>%l~`&71?Pu0k7#7}df5MWmR~vWT+WJ?ApcApzsmseRs4X*fwc)Y@hQ>0v z3tCj1M)8?IIeWlv6nQPOX`#%$`U>C!>>xVWFezhYWnN7vBLxn3wMnt1Rr z0L;1pLN1oHIxqRT^)Y%Z(fNSmjH?|z{lzO3XwEFiNDVM)fzd8C+XAMaS++-v7M?24 zI|qILiT0FiF5}s*lkpvR-p0!gEbU!>o)YDoC;t@X5F5xj(c-77oX+$I{YLzo4B$Wh7u_RK-d=!s_jU;14&HMHIcc;zE{k~} z(Q|GtN&&DVLv4rjm+#rfu3r_5_CWHn>33C!TD3u@cJrKO(^nAPTQf7$-WAfCEYYR4 zM#!s%)^oVVOaxP&jA;WWblwR5ml?1~&ysIwT6Jb5_YU<*L*?N?5w#@FD@S0#5+|QPRLfZ zVPE1KBmTydQilWE0rdt&PbK~lwmSNa?c!Gob0_PA&i%w zpPVm-0XFNAh@IW$OyOiy0BT}QN_EeS4bQ}@pcJEm`HvI8)G9OLyw6`^2oVV5Pspk zIu$Y-s`j`-hRw(1G^=-6skR(Yn+X_ET9^N-j8KIaQ)%z!+_JHMX7eP%LPBs#C2jzHu|NwNfHk5a2}9!^Jj2Z0 z{|k%dn4RteG!)N>rBG%R-ltT8f5CM;#qh{cp^MC|r*RC2I?1I=5Q6_!>vOUk46zeG zWG}=Q>X$>*Z~eC=8vr!>_K?Bml}9sQW4CGLd+z=q&&j*1Xfi=J|1$M()Og>1_)x zeCdJQ%FCbqGCgd>5(R98OYP+6pXs+x&@A-s)Wo*=M)aD6KY+H(6GpSt;o*sPGb;rJ z{bU^KTJDUx(!7q}D4Q&fJ(3~`NUwljO|cm`;18g`4~8p3T@iB!U+_XnQv(AQ6zDRp zP;nE@=2VQm6Bt68n4C4keh}J3Vm|8Ve94*H(?7adBv}nvTYArscxc}To%uzp*K;Ne z&1-!L>oY7|%L7cAPea}lJTzUmy7S=+Rb&}2ctgT=LPslmH90}6qdSKp**hA<508KQ zVq2XvwdBAvM-hc^YAVX8W?m#uhZ zBBb|S*THNMg5xPzGlbPBm%nhlY`_`1EO)NtD2CLor{u_Rl`4Y?{{O|d}@_(EkZ2u*7WvS@= zV^8Yy?bcuLBkVVCQ-VdVlq!CxAA}wm!H{ZEYvtcn53;gX-{&jdDXHcrp*T0mXz$w% zc{)c22`+cRZ3#6YbVYzrNJ5V!26+02A`3Bm8LTow*QfaF)fh=%TA(w0)Y0VqLW=9C zffKT*C=~`3sg!bjsAYBNLgpaApNzXTFDIhm@518i)Ey}DoWyhrG5zVB$J~rZQZ=i> zOkca4K=znjB8C+^vO!;#oRsJU33-^U5k~svGbop+6KoT3N$X&nb0^4h1%p<%eQUme zMqq2WvE12XqYkaM`dPVF z=7m+3bZ8<%P++sB5EufH=kpYMhy-2@T%EKI>55;uGoxLMSto+v+S#vH2${w+gkF>5 zd;70?$FMY%+aBu9)^2{wP|lDcI|P%`KP+yB0)Uv+n8!1P-gZKJHpbAu-4kmv2QIAo z7@@954VK~W2AUG$GvkugPrD_h-L4D>><+#+B^5UZ59i}1Jnh+$Ibo^00|${iBuslY zfxY9kTdALK9t!cjs6KR%ZU2@6>;KWqvvd6ir!PtzkN!563x0BkVHBnz&~M=yS5Gfn zP}8Z+IPoW@&;*|6p+NS2U0p$nS7Iq}3YhE_`22MAiR{53x^@m?5VsPy8WN=N2aVEHdIFRY`aLb3=#+u_IC;9^v038l2g`{g7 z3$X2Ryw-F618@6eD9Q{QEwb#A3{KDZ*ooqBCSWmpd#&+#fibdy0%rZB9FO6jk9a6T~g!9qUdMuxL z=etsqyoe2c*=~%lAaGW|@=vyCs0YW5GSCzDps^%lIl&~jUCij&3exeVJJRjXuYIJJ zwkmIwTAlW#-6z1sEI5>P+WBnz)RuZxgK7j+XHin#F2Ha}Y zlfxdOc+{T{>Jy%ppUgXrtdk$MC4zUYFj5J>RUfEF=<8s}^>e}&4XhEda$o_^a%W&1 zOzro6Eyg|L{~(}$v#9TNyfiU4mI!gn%wk)ySgw$P)PFi#h7KoPjrBx@`NKQW{{)2s*3x^?OL1Q7#af=dT;!HeTxbIuKdwrzKqg zS-<$wx^46M`|h`7G+n-8lV|sIvW+7tsEpqJw7e6}Yum;GIT*;L5B!-m}Q`w>?j~SuS4|dIpT6DYCYW)sSfTcsVYP-JffP~ zdqT5zpL)sVh5ySbY6RLe1fTRU_SjdAxQRq-KnaO5(9G9I1NKZ&B=~pj8a-&X;a?oW zzsq$GygD-7)NRZ~ffC?pw<&ZUwBwxPMB!XhB~FI2-5Xkqjdxy`Q>ezQ(Plj&C=vkL z=|gTjda)O>T|Py%q;P6mOf$IZQ>w7TKG1sR!30gfe;!y}Rt;XvYU^YD6;4Hq_q z`cQfC&A<`^?O^Oa_&2NcaF}+cnEs>q!T{v|H#1vx}H8^Fh zV7Th0qN)>h>Q_khTv>${Ehwo)BR1gV{OEknFY8LEMeB5wALJK}VWr@K-;k4jMZN&( z?&_KyTd1Q%^58JT-?Qos-)d3{p>jERY4xAfh>e1p@0Ou1{KtaH@;?&$?zJ0nha);w zSGR?1DD2$Swx_*~7ps9)X`{f+U6Ycw5c)3V?->X#i{LbmJJ4om=o`Vb`J zkT}70c33He_a}C{y`j8>>~)P_MPg2B5w_Q@)QJ|koqo1YB>|*b4PNI-$p_?OSPY1l zY8QpG_rR7L+(onk=p>J_t`cp|i=*bYE>A;&+EN#^;jin?9nXkb+?|pvP$_QkM}BKZ zUm{m`gSLBORS^FbZYQKcz+$qo{O=(p+jo*{Zx$FepeUi&;oIsj_{t}oKR+!T1O=tb zjN+o9q@h4(HNdJsR7I+VXmPKZ(Jc&jJd7eC8v{eL>r3C@8VF9{&kCIbVQWmccd)QJ zR5=TDK3ZY~NM*3u!^P=LJE>Yv>9z40>e1*^QBuaQhZ~h*-y^nhSYS2KaOvcL*vM*b zRo)g#fa{VK&qh1hWD7}%ix*r$d`p+SsPK8{eAUU5bxDn2FBfDBT+h>q{PzIo_t+DUvzJOA{kA zavAbT)Qeo~o(W26$S9LYx6GR#aba&wiET1sHb3Qy(vgykb89!1lEs^a%1F}$Cj;O; z2K3E7*h*)~$_&UipPKHp{oY2)Th;2*)oEU8-<=S87gzB4!cswh@MkW?NraO~SoT8E zN&}5R@qOh}dB~C%J@>l&atQ_Re;9jg-+UYs<=p|#M#n04Gz&ZO(GJ+#F7Uv0MY8p_P)Dx#qFzj+l^`^ zZ;LlrZ_L)tl~9}kd4uf{IldBGKR~M>2#t`de+2H8fXi(`mW5?IhmfA zDPmXK`(RBU&*HF={pcoS!m!o4{}3p7Q{NdI$lT!Qyeo~R!r1-G_SPn^9@d{C{$OJx??t$mF#F; zCa#y1sYR={euUW}QX$olh*daAR(RjvA2K8nA zB9LswG!u|;95gvC5E4_T$-EUnU-0_srD4gFH=0cK$ZX3{j6>bBsi5Y(KyV*%xR`iD zRXbF{Mv8S&LC)Y4c})a4u`>)Fc3Mg0tV;S3_CWY7pORn3{LJUM;>rNE@xBTfT3LB- zvb=sC2zBTpWKIJ=a{` zH1)pZsORXIbvZ4mSJ2NV+-VPbd9~WA`>D;asb_I`<117CZ3h-#JjxTx>k0GcQjc}h zOXD|NklAJy9o7TS4I$&5Osxlm4<~ubKOYzP-|W8s3Jn*(Es*}P&wJ9iPQ{y$&KE!g zZNfP7-B90edf0A~!XdJpX#jn^lK_y#S^tF&uN;xDJ24OUBFmu%c0{_q5NS{!t=NAE zq6ZnFxi=Bw4pP=yku`L=DY%N zgMdzu06cT2^HpiQoNo9ZwW?0Pm0YOr$Z%hLg7}fB&1e?Y0Gkd@7XBE?w+J4PhsV z45(yO%5fMvMWXyQ^J1jpM|KKgq$Lz3ZEqGHf#q>7Q0I!ZpV(M;(DpkeU3gC$u3S8~ z0%q`|K|bDFA z|9eXW;b8e@o}hxVu(1BCWm(kvE`RZVAm;NpkiJo$t2#eF3SpJpW(p7wNrzmMQDPEl z4zb>;?kl>KLP-x(rE04dOciYe^R2ElPS$+tR*J-TcVS44V2$8q9o;-p3j}qlK6uC> zc4`b0_nW7HkZdn-87jMX-EEP#gF9na0a?aPACzP!Xpvv?#WF?!VI`@S{8%QIP7?is zlNh;d8_HS(4rxj{Z9Q8GT?sMu&ylU?1M=$B(iX=v%ZHn>t(8l&H5OV|(_lm!3g(EV zgsb^(0#IJAB|??L+1X*JW(4vbmTy<_qnA&FA~Y%uiW8sAqr%2e-m{w?GrrVLPDJuc zgBx9%zF6+l7M&)5FF0sFE(_*Jq`@pFjY%#2M~Zc8i+zq;qV&>!PGfjQQ;vWz8 zYql5|lPz)A&oukpC|$Ame4Ha81$ID%Y}cn{qEP``nhF;U=@%mFyIRWyAXGop0SV-Qs&XP&cK?}K+v=x;US}vWINLUfk zi>6d)k$y%1YuSKSKc%Cn-gcx*O>DcBYrEXY^P*uO_bdX19|8syA@7A3h37fbp_evW zHOXL{`$SX{Uz+&xhzJz#n=oq-jqvv$KK%5Sit3ykmF2aX+_r1sgOxy|NO?oMLrJun z`rRrHL7dFg!n?CqH1nA?V397&x^avvIpu8}TsnIIxOOnpQ|ipmr#pT+PH0XtV6!t8 zW<;QP10_0RpfqIP2_VH5=_Z{{SScE_Fp$&x9=%?ZHx$eXBZ!IXuG17hM;Npqyb^qN zd0zULkS0{~B?R0&{2SRkyk8-}I80Mh#Q0aR-2CNucUEB@qTD4x&)|u7YrqFgR`=dJ z9j zj#HPBDbUj;gGH&RbVBIURC#Z-S*Zt#P%3e~lW?CD94}Ed0Y)l}Ii4nzz-_zcTTdI-e7)e?snvo6|$(%Ub z3bS0o&QGe5B`9t*&L$UzeY@LPsXmUah!j?!SAR_}M9HGgBaRQ4@E2m;#Bgw?wpNCd zl&US~4#8(lYp&j)zXRGQg@1P5C;NrUH~!d1zF>F3zln0hR41$|p1Uh!EJNV^lvs&q zV1gf7>3aQ%wD6T5#DA;&_on0-Tc4(zf-&UF)Ym?b>$OGj^T5@0=k5l;^A2l5AJy~k z9f5=Ef8Px)xK&tlYzW%`?m;!P)Z73mmTa9DpCs)t|M`X> zHkOPwXz$NZgQ-Fy(Lc<|YHK0g6d3=c;l#p%haD|w7mSj}t(gbKiHEgZI5l#3Iv)`8 zlh2bF@P zDMy#8DOv8QmFW2Wb_klxk-Mm39+xZTAHxIScFYW`{B@<9jYP1av&8r7%WNz zHy^O!$rDI69^NtWB$aX1?3g)N3OvV$ezcNcHl0AhNHi(C`yR_C1)=k>4i{de;I>KV z4$wwkv&vbNLT=eb+0CX!SKjM$Xg^Di^TC_5@-FR%=xQpKWib>Qf>;C@^k0>MfgEhE z9|Og7Q1v5X(t`YcVJu^~1!|oW-5Ud~>&@*PZbIaFO>EhiymqfFUJCYHctXl8v%^%7 zn}}B9=v#<2GWO|FT#n%+;DWz!8mPylfNFw%x-ZrV2mdr`_5rV3HoqthsMAMGMyYbh zkqgJdsvnnacQJ;T5YombsA{ceVVql3T6;K6xUs4APg$l?sbVGaXfU_njDmV+c!W_Z z2Rv-@tq`7Jb_(*G$gGindiBjFVU76c!@mLDYAUXuG5-C4i2Rg-Lfdw!wEZ^}E}DqQ5!?f)>(7hY9P)dG7VcUh+bM>jeZ7DA zv5d4osDZQuyQ%HmN26P?a$@mbBdUPPfv7X)8 zt6It+l-jhDQa97!{4E3>w|5Xwwv_o}C5033W!)^%dzPfrDyJ5CI~5xce1Ck~0duMZ zdiHTWALNd5I3UGS3dh-%MQ(=JW&Jv?^Q^BMpXna!&eBw7nJDCd;V{_o-fTJF6#a*0=$*W1Pe9kUBTQVY1<}RR&bdHt zSJICGVTcB0(AQ{Xjp->LDtnwP54F{>-~JAfltqQW#6VB!pC5h%mA zmdsNof-+{cubgkSR_MZ$r5R4^6=&w^<5FZxHpMyK5gHM7Nm}LY!bdSV-XK$QvTdv# zXa?31ZP?Ak>6*lJjmkQ*$9`XPytz(q_=~+`vad%UJR`}_w`d|~7(+)k#m+CxY8Gb6 z?`g1K1KaAy91uQJS-dhlVEnMRv~&luGu^(v4W^4~(&J6^&H;Wc7^tmD!{ca->e7b2 z(y?-y3N&jlsf5M+pk!fn14rRX+b264VTAMWYSr-zC?~FsbT-MXn-E5q#Z63tNKnw$ zSd-U5VC`gUS|f0tyC!aEGswHqEzPR~H~rhe)a4yk%si^LB#+Y>Fy7JF?1`~~)@^wP z4ywCUei3^6jX2-#?~nP%n&ym?w@?FO9a68C1QY$S%1G$Xg-uWQ-xerSF4RE zWOaSb5QfV?o=5_zjZ|B0)ycD6T7?ab|Cyd{{Rth4$e3M_pKI8_Qs%Xm^aZo>rd>2aag~!wptDye+wOM6<87wz zWFm`otMk0ga_n-f*Q?yH)bw-=B&-5c2RY-HtTE#*W^Q;*=VpR8;?eGF7@RGkl%D<> zl@|<;`Unj*M!3w|-Qy3>n^Hk9NjYDznZyoQ2({L1GX6Cm#KHEuvNR#UAugKXu?nZ> zQ*g#*P?A+`t!=T29jPeN&vxk~2PsB<=0wjTIxAAGN(b6d%y-l6d^q1cUAP9<>MB+P zzAheZ44Z6X0%xsqhG2j#ozD0^1J8nZd_O7RaZtPt`;H*2{}UF&pa?cE&ZUO1h|drJ zRD1?H!vNOIH*%UD>@U(m=r!@d&YBD)z))jiU@#uHP502x8wPihUc~I|D26MXYJU6U zPYDpRhIqZIpQwF$o5IU8aY`=x!;jXt$T!U}YtaPWr=rAmne<}^*tw9uc)o>$`%k(Y zELKMB`{46|N(z^S61eWMK}-*%V!7LZtW9$^rCQajMtYzkuWGlu5lp{*St-jOrMDX;ZzN2EPcPAz`G}M9d%`ynrT`R=L0LGbe{8a7}m5& zCD=Xs;fWM$NT01Uj`D$vWdOxfpqwC8&Y9>X#_Z`Y&J;FvU!Aw8TJENfbm_iCu^+!DkYL++b5dh=l7BG-Zv=w`{C40S1q8jPeezA0z!Wn=eu3{(K12F&R^8 zmVC|zXhs+wy;?e)XsB*UOiO$L&HTcjLpAPn2APwGgHX>^%Sx#1meC`hDwlBypnMI& zC`0vRbp4y``MjUBkyzVO)?B;R7bE5_dUl;(TSEu;$C%pS48#An1zhakFa|m>$G;%P zfu@$@t~ip<(;r1{Mv%yvW1!lfC1Z*djZ#f@weF-0DRl5=kqDK>e|5k3`BQ9|HX*zB z!M{DfFaNX)J~R~4Duh?RI~8+QLw!GtM6>9QRu<8gv_k_kvCQfym6g*zw$`tg6^B%9 zW4W>b7=`r*e=PupJTP@^^EkL;4pm&~RXX8@XRBz`GWuF&IU97u zTfxNb_rVkn3rvVCb+D3%W!V{VT&Ek1fKPedhZUgcDk8gTm8aj}a_qw_iNw0H342;L zk}yoNg=V-DwtWzy^HfAIUb2%Boi+O`&2k{^juf;$Y;c4HeYTA=jN@4+U$c!M!;1ksWVP2#>iN(qVu zNaC=C8Hzb4ZdS7kE30lIsK&G1XbXsBxBi*p=D1C-vofruF@&+hgr;ClcZDix1JqRJ z&@TPyk1cCX$I2Iz(B)(Z!i#{YkxOT^Bka5}&9zKS?i>r1`Ry@x>3qs+>{eoVSF}LN z$Ko`==#@BW9;&$!y~5w9qM@@@mzA|((fml-ph(pFOT>+(UbWg?3WZJZ?gtK=ImSYL zji=lQp1NDk1mW6Pr8|mVkzH?};VkWP1rfZW$aYn_yb{LNjL;TMGi<=Y8Uk#M(HDC2 zsTp(L4pU8NUg!DB#qf(U5vvZXFPnE_7a8`rR2+Www9%2gPB5i)|HtP8IOposG((}) zW{^bj34!C+u%`68W&L14eVoIK=8rGT=` zeh+E^?r)ZhWh5^pUt4My()BdiC1pvU2VeA^AME#?t@rl+0!F`78?Q2UzqZ{4wx6>TPA92 zah(!_ZC;wEj`VqR!;T?{Gx%_0(e|an>bSwU{NODZfq5q%(}?;458ui=)jsMBRI3mn z7}#MR)u6D7BOYeu*P|szd}xlT>MfWig%g7)imaDHge>fFmh1{>0NQ% ziN1A;NwI)Wr7C=1+2stnyY44-oGdmCk#i%0GU-AH6zESEmf&j<0RJ>~K6DbD} z3ejuPy@KB%9`uC6I4YR<5(u(-_|8EZ5O8;$`m~D6lG3JXfNy2gY-`M~sM5ao<*mc+ zb$+kbR_~VfG{WN{hmlCf-sqTo9_nJ@{)z=2yBuZpb*_(V;*EhfJzcvKLSa-My}n;v z0T^ek)INZU96f)25q+dm(!!KD^@T^LNvfqvqYE!+ciPQpFOTGvHnQTyzw2jN_+=VL z&aS59sq_p@$7?n-Y6C8sN4uhD9xbhMHwbffx^)WDr3ylBRz~_{Sj!R(KNthA?2TTy zda!?n4S7tqI~_%6$=NUEah)h zR>vsjGHn_PfeJRI|2}TK z@}Ln3S^+kI(p{JPQmAPbX4${}rn{conei@uSZG>4T=gaXZx z?P41jojnh@mB<$>RG)FduE5vAu4eA*6Zd`5LIQ@Jv_b;sS-woBAu7~gX_J%r#37*; zhcWU-Q;?1VFNjwp#}>gJh5EhJv6FloxJc0GBFG2;oG`6$_-9|)EI#NcmAeexIlAC` z)d85Rjc57773kcbx4F!#@Jn3l53UgK6>n`n>erstz*JDHY?n~di`I-_e(p(KedL1V zbZ6b->t;#7QIP@_?ZWLiom~|lZ`d}DbF^iI2;|{}lMOLk6V&S2gU+N-EeCMOLsMQUJd41@g6t#C*yW>)MuvyUlB2Q^XOU(kOas6>HCcXq+jLzD z*{|pdn7OwLaJMT4Lro^ilWEz88Zs}74rOWyF9EAWeXcKZ(nB<$cD1 z`?mdN7Lh3E892pYBoGqEf~tucPQFAE9`jVJyBh*kK^!ZEZ@ey6iVO(`Ywa?h@0vAD zqq}tvhrIc1k9+_y4}c^q*!Y+@ZF2aawgI@3YAGV2Aw&0C@nvCXUMHR9yG+ckKlh;{ znh5CCc&F%|#}sxVLUs$hPASM;F9=k`;KNSRttY-OAsz%G#VBC*1nV^q!uVmgn4gko z)lX1d??x{3BHw6cM$Zr9n(u1i-OD+k;sW-y2#xOsb(`WCoJvJ~uIo2DzX*2To zqmu7M6bJTC{EI8&{#W9`4&!W}RShy3oRt3^i-xYiN7*N^v)S-*f*=l?~NePNG;3yqq5@~lFKVo@L^6E?m zsGbairIleD2Cnfi8`eUEy=TZ`WSyd8NT|+vGrplJkJ1jJ@$M zdawM1JSGkyi1$-tABBId3Ze2wN%S*1g_7bMV5(%{XsFU~P%Bv!@kG0$8kKkOL;m*3 zOgKT4B$xP`*qUC&M{F)D4H!`L%-o}y9qJr6Z+2Dr+X}XLXJ4kpy?Ac9q#f$8VpWn8 z^_#GX_Xu2Ne^Yt?Lj5mQgN1mrothfkRaM*V!G7YkG*kQ*iF`Jy>(ADr*Qv3b3hS zSf;-sc_6I++!h0K{|3J5lawYP%eddZJJ(Hb-fQE^(DY9{9I}EN#9dft&zOI+KdO=_ z(>+ZX>Uk5Oh!CQXbwyoB|IJ!Cce?!;@C-`ras;(!cmp?Rb!BO!t$&mw67fVH^|h69 z%`VpZeulDF_R9o%H3zOCZ|CN5dRkvS4L9(g`UGbBrdy(=SUCQxYUTgtDl}f~Sp(+w;opa%fVqwvwlQn>^^n>u6O>F3`mL5ze$9&=gr2 z?Ar*D^BP@XkVQo5@9r#!0<|CcG%B=-asL`q{SucG6UCT0N|{yVxw z-Zy>;+xF}_Z;!l+mt%}#vTw3|P+9uYo8gux3Q^c)NJ-OG3JqVEOMGyG@ajnwO-I!z zLLtVlfP@y%EVoi9Hax*#z2x|U3#>UT&`RidVr;88>XD!ad&Ls-mS{%pE(|bIbc$?Y z!w3`-V+CJ_fqb`KI+3aYsT>ICDLS31I=pQ%s1%gKO~CWaCwd`@jB%9zUIL6MQ?@iF z^Y+Pya z19oRj^f3e51K5ko6LX>HdVelNgFu2`_IvL~Tf5Bv`cw~n|OhfK+0#g+Z#5x&D)droDRN^c4H;D?dJ09ETJLT z=IJk{4^Y1qy65jwMgwN~)@OeQsQfp(Qs)2V%O>G0PxA#LfrOE?D1ko!;muY4D6okq zpDOS9V~*!}SKdw^3YyZ$pB?$#YTmg)zXn5jxQeTj2F3L~XDdM<|vk zxMOz0g3rm5dx4ok8g4# z`9jVavGZgt*y)r!rxe+*v9W%#vpQ?N6G%`<^e*#oPHCy~Kxt@D5z7MB5d7Z*`54olCEZv_wJk zx!seVyyLFU6(QUKfR`zprdAT0ZS;)Z_GB0iU~Nw&D1%-@#fXtT$U6$+$O$2%9ryzl(gtZWD=vrTf&@STm8YO@ zXgG9|zD4TMK z7w5kWeL$})FhBk+an64kHcTws|AJE+nsT{p7t&*YI7k(bS@EG*e5Y`Z0R=5vl zO(WGT!^Q#g<6cX#vVS;?AoPAfr`z*&>Mp^2=wGX46r8Tk?v77}23I~9F$2Jw2|Erd zI@mjdbQ>8;v#(!d4B25lJW4?DBY?y(Xt{rG}O%haGD<^qRoK1#~^UO8QzartZ5_h z#*9-q;i5T#TF+*@-w-!@uNg25y-GJ==%W9w7IN;*d5>g@YIN%BhhFW$a5JF;lD5S=Fp20tN5298;nuo*Tq7)#P2bu_-&Yh3Ss9r!o8)-g~I+2w2!2sc!77@9Jlk@R!74@MSLP#oFV0W_Mp|R7a zgB?R>U7JgilH{aG^f;Rkz3b+>Ou=)SFP*||dz=n_RH=rRkuli_d_5#^>hOfuOiC$% zaAFaL39R4}-KGDJuXl=$MBTzgJGO1xPCB-2+qP4&ZQHilv6GJNq=Sx)&PlJm_j$Ny zjXUb4>bd4W`GNIp*e?LMiNof0t)cm9b;r`KX}&=JiZuYMqD?~vMQJnaNdK=0<=RpC}z7-i$Z zWAO+M{gRpIeeLJUkdYg&$@47~;x*YM3mJg^Y2$oCnEmc-!*>a$CW=4pJgj9_)~bkJ zT0@pY#6ZQpKz*QBFQ9+vf<3a6Ghj#l-qd{vpEDm9mGX-GsDXnd%clV~*C_^f5)mcs4 zteV`TL&uLnpkDF?@Zk)kusJM066lc$E2KaUD@^o(Q%^`HvK?q-80>WxQ_E#@dSMgW zW+;;#A8@s#lb$F#JtWf|Jr@R~A4AWNvriM9>W{?sXn>oRmsLhw7qzv-HtA0qP8KMnC>+Wu=kFBl1UwAH z2N1pLqQ$y9c2Rzt6t_kR5u8m44ju{3+DHu;gZKd9`&KD2`>MoEJRl&Og? zz?>8>v_uOO&GaL=>}99Tl@YZfR*SquB2Nkue1 z_WZYDxUKJJ1=X~hpRNyiY??FEseLq?T!(F8-B7L1Rp-INk+-N_5u~_B(ZIqlJb*7j zB}xwiDK&ur5ArE9coLr7J%`^mTtih#dO|z-sm8mGoFQr_4VF8rTRc2zY@8=thqNit zzHh2(3!LX_a_7yS6L+X^N_jbQ4U7=V_0!GjWdqic@1{QQm4>kvLVB0AAM&dB0BUFF z&xCq}fNT37>yCF%qmLM;pX#m9rfE&1KuEw`9RL41tgNh@|EVlm|G{qFsZTZZgm-Z_ z>4zW@61Zv;(gD)|tz+|zK__6gkO>wB9PT{9*>*Hq78TR3OTmMOP3Ej+JZ;Bo{iJ8X za#~ArjW#C4A&!mBZs9cBH;ITLvj-Iy*Fzpb@tiU|paKsvE>3PYNTTctcP_x)Gl$c( z=7lYTA;kp1l2llePtqHJT1Tx&CseO2*$AP_^?-_vfpHH>N`&L>n~0H*EgzTbWn>R9 z4i2p4rz!YLkNqH0HhVzEL;}KU@gkj!1_sFr#8M3_*9%8!V0I=<!U2Jdh@}3NP>dC*Zcm#CozcCN zSs0K|?*w_669p!Iv~v1Im*DM=uYHOKyCoxN=JdZzQbOEIA^9m08n}QIkCB}iIG?Y> zBn1s1t~F4=_qo-g4uP#0rKr<1~#V-$kYdLQ98@5iNTu1{LlY6H-iuIYjOG zc7kh2=@Rb*>o12C!9*30Ii9bbKbj8IiU|T@g@HnOiVPf@c&0`;HbkjOcouveBH%C% z0u^AP-2WZe1SK`h(4d_Ao2;f{m`kH1oO&{VZUPCCTiew=)kzpqB#Uf#e>b0U2cWB> z(wpT|8F~BQqo&^E1=a(#ep5~vSGn}=#Pua8!N4xur%o_SUU#hjYt_I%2!}}5VmYQ7 zh60S3o}vj24_AS!^fp|tn-bA4qFuqm&d5L_%};o=_VkT}`t$wVeh9NdE1)j4aP0{2 z@-mL@4CR_Vn|3g4qBY+#*(Ir~K8e*)wUF1h*cW{+sheO69(jvc!ofvQxaDui@*6Q^ zt-Fb%2d|)H% zPT@_*OB8`_wrbm>b-`q7Z^{+EDyN6h1+BK%XqPzP)fQK4i20iH0&VkN68{Zow;;;Z z2Zj2(o!6LvjZRnYIl4(*jgBz8myD~nxX*-<2C#Egh6~)~IgsURpL#o(;&yaEyiw>k zB^!4_?wKtH!i~seK~ySURI0r2j1kfKEaaL|NA3Pr@~^DhV;_LvZ&#ly^ywYGA@U*q zyev|}u4sr@XoOI>59mdFPzd7@*|pQh7qv@3`HgrevXYY>>zua>^gai(IBnM;R=<~k zX9qB{OG0)RlX`Jt&oa(9IA#ex_N2%;_EPN4WZ+kuG{bXN`lIznv3Deb%)2l{R#6G} zR2-Xv`S#m{8Al|1B^)#Ip;9b^yfrB8PVV#{Dt&xh*1gHJ1;DEfAI<1dSkcb?)fW+| zN8>{!HE-r@Hm$7a^c`5?s|@@8>HMBmi~WX-`oq%6kN5fQS=iHykDrc#oMukfFIyh% zm;B`TaHUVwCVkq{^ox;gH(y@0+EQgUJ^J|o>yPocazAC?iZG=GWg1tESE=5Tx*_3T zG#$EUG?@wN{(wFr-y`q)l;W=5?=xEm>RfBhEvca%{l%Lg%M_oCF6RY}2P@w@wq`sb zerI3#Js)lgcW6G^qrkO4@YB}v$=JcPr7w>NM`L}n`#|48yZnlv8;XTbdx=(Z(pIRO zAVg#@MC3=apa#Ji^6RW%o?Ot4OK5Hh^;LXb!zVNDKTNx7-c2ZW;p>^fZWOD6-v=Il zf#N*gYk}pr}b@wA5b`W&dH$ zei8I*2+>y35qH<+ZA&Z`sN3ui^c?!B38+G2Zm+mU*ojEjYFoX3f#G;xc-|dPe-q>) z{xu1g5yKbF7u{zVl5ZgCh#MR(hWh3bp%vT8u`>hz<-g5Doc^ObB;@AkO_}?OgX4-1 zg6S56Q-hXj-9|B5xKV%=K+1VDPcF=#9fLzAci`o6YBCjavKQmh(;tUR)tT#BSn*)p zq-uO|0bZ4wLdNhQx31FFi{g6uXVb?Q`joa*9%om<@q`4MCOrHnC`w{}^Y=?cA;tp3 zWSKI{5HT40#>;7&T!iD7V1f^;xoyw~0Z+&-0Y*hsdqK=}r8%$&fCF-9=Dqgx;uIMv zO?K`*NH%T!i7~WZs53#xh1SiSI}gYi$WDdFQah{DdXOK6Qk?F<1F1E&VpHdWJs3`O zQ;)Q<_-u^s`r;F*#-T5FF)oe&w+Rr~Jz^#$s&=+EzjW+!WoB2yEbX%Wgf>Eg7LPgf zLi_+pW7URcM7A`0z{7c~Ej6!p%}UzlPEx#zj8uC!>)xdctXh9@jrTy83egYRL^ zz1ztUYc~^0i|J#N_-fis(A0gqoj`WL2=(5LyGLqSwfq{t-h_%nVwF)0i9ylfQ}xz7 zb+&#izytS}0k>DSbfoiCMIbJadK9EbL#hiPOm?&xgUDi_0JIxw+vYK??ow5kEbLk0 zBQ{Oqme$CipNMf5Mt&w=2@I}2pu?PPx~0Cwt`sq}w|E8%qsOl)lF};=)D#019B-Q? z!|&%2GVY;&XOntMkX?ZFhtu=MNNGhzcVn2uzl; zBz^tn6pLG%28g4?7LH&o(6xphnXj^l{i6+{eL6AaVO_s6jhH3L5<<&+>j**X+S*9e zA0!!fsGw2ZoKmu}s!2y}`L5)xCO8*IZz8y;@UG)lRA{UJtAt3WX~$(a__xK3o6Tye za27UMf^dyIiE07C+kA@B_@CvYlqeMBkuKSCZ^QW?ZGet5>jZdRT~GGJo%UK<#ZM2dO?ixB1$jI82YBS}T|f+V>ed|a*~+FrAeKfO5D1(B5yH;^ zEQPIy(oLyf`EyAl5|c{aYgPu93}s7>seB-zGyNjqusH!-pW;x!GQh81eUzUIbSr?+ z{A(z^N(B0bML6Tm>*$>detxc&{D0mj3+q3(049$AuBcSM#O?|I0>&LgAdm!vMtI#a zUDl<`@SYY=1IEUdRe;3_>=yKQz(}=IlcFgWg5;yz44-b?2nsENC|^B<%~JzR4G0E= z6%!qaQ39R=QASu6EmSsrc)9P*q)7{d2RngA_YO9!6P~KFHr_;tQxi$2IknIvkOh^$ zyu_pdp=#FmAb_=Vv#O=_5y3npqJdDGoArKrh5ye=NS4JUAk`?IDCr_E{<_*PpZbqAU(}Fs(FO0Xo zeb3E*bb$#6?pU1H?pHvZ0W(TOq4pqrS2nN$sNFv1D)RA8AUDb=PC?3xaz1bU>T&BC zLvL^FHLmwkNMY`9lPp-4SI7EESmrH<_nKWy}k9(>5RD$0-YcH;<|!Vm62-qB$*rG>ogqI)$Q zz_oBQmPY9FVpiJ3BKvKy6|&Wr`K7g?^u4O{XXZH=bY``;^07L|-KG3WIS9K412FTI z#*^JMa|AaI?)6l^Lc6(``m7>v-fcPyG1gd~sYt)|360;cu!Kj=IF^|Puj>Qbr$KP< z;yKZ2uTB0~^V%%Al!FTzh6ua4VDrrwYX`;}zl*&iTGb;q!63|^rv1g0vgx0V8_NZ_ z7H5Q`{?-DgXJa5xUcB>j{#}pUxj#nZL9b6Xl;YHJz^vW#t52A6mTye*sIT91^uBG~ z^7AdMBp)3S09*JcPC=2wtY>_ue=d0kk*eD%{2P8iEdLmT0Dnn<2}Xf_RfuHizY<`h zSAS5wgEI=xV%pta(;pYz+S#}a5ZRSsp~eZP z1=k@PW??yKE#)}oA(~GVfN%7Rg*CNcLAk!%I(=2TF%)3{-ed98qWQ^tqodSf5HN4& zmB-3z*A-=dCodnNjeDRQyUIfkzUiUbuO!GSU9g#B{uiyBVs>ZP5u_}_d`A(Lem<-- z``m4hm@}n9KuMD=TDquQb3>0fgT^=b zVCf(3A=+RjVp%WP4sM$zV^tZIbw^n3xWfS!zBxmggQ$BSxW_~_&+ZhKZaR@S>!Lxp zOzv6_*gxNJbY&3#{`F}IiGQu5SC?7BTKh$F$$*b@@*tQ1VDGROs(vYY@(j>E90 zEw}YdTSKo;^VgRWJ{YkpWd#EtE(&6N97KF+YE_ZOGnYZkBAIemW{##vT$Vsh9*sjC z`D3pRIo|{qM&sY0$__BM^@{npBT^v>!QwWIa24#>Llfzv0nj zcH~7}djtnFw_~dVEWZsU%f$aYfV2sPKj)G=Pe6G9p$c)|Abj6eWp{0@YPNo;DsO`b zZ7uKTI;Zd_0lyt7kzdThu9LO_jq4Gq+B`H^0weMX|*^z2z-Q0C-$m~QrbBPH_#1R zF6a-rsIFKa$gF+p5N9BRe!16|M8+0>-|?mSR8BeJuLo626g#jJYcIX)N7LWc@B8hy z^iRcNWBh+KZ%=3-?}>@fWR;R>bD&u*)=}vaOToP;y%cDv(JSc+7dZhtdq$ECO-l~` zUu<32@x8234rZuMF;HI~V(q^-6%P=k!1wsQF$Bac78%WOt$@ZNd+w}9e8*r&GlMon z%mg%_1BS{FOr*)nH}nk7sw^AX#( ziD!L=nP^3IGjgWO;qeRUpDwOPKA=##K@jy=90iJlEAMnHAQkUV7?ks*;8vVy8<{*SEMHP=JNC#(lQkp)nrg^x z)pzsMUBhl{r5_c2Jw^*jx2~)KK#ACcd~frw3_#N@YftoS8#k^6MK_*9H=l7gG$aja z2A%vP6`7@{lJgM7gZ8ubvjI5;YKQ9eY@4&{FwP^qh*@%&{5l>H26k+mt*ln)pbL7^o4Am7{!JeJo}S6NDQB@3V(a3-LW|Nfy(lNV za4n%Xkxed$8T?!a(8tIFFnaWAN>ck`g_5x_&igh_Ht??eH~;U+EN%PW#4f^Oil*~s zI6Bj`s{1$oyz}H}W1QN$14cXS1Muz%d%YDtW=}N6RFxvXTd%)j*CE6kL#c1GdZT_g zJ*-V4Kx`=@Bi>n;_fvwpTA^8Vifd|aVGCEIJbRsaUW$-|&!q`T5yfmse-NpWTS%Lh z#-4FEpic^0bKJE37smIFzf<#+f5b|VSl{rg-n*x3GG@Ehm<{9~upH|#dq5q-bp02HG%bo4}Y z1Bt<`7DXe$UAKH83pI%4K*q-*TDBc~+w(~emmDBBCJ|@;T&l`oD*5VY5R_G)}EyqbMo4Y6mn@{`QJVH_S-j#p+CHRJzyf)HCfQW-TI`(wg5 z2!Ksg4#E+i{5{4tt-F@WEOMFju?hbie0!E__k+}a>yKuZZ<${#lH zBz{ypRkNuCVez1E^_)ITG^;VOc(@mV3}&P)cQiypi_00NV+osV3i*UCM-wt4raVk9 zm1sVco0r6Xo4ckOC6<7#Rx_)tEWX>>kzQDz= zbpX(eN~TE@0a+sVi%kI=KI+Etu+_U2O4y)Abhvly0$pSlHiIm+i|FVp|1hI~3I#jx zH{u!DsSqn;e6GQ6GK`f`fV90x#mI@$Q-klA5-%az;)LhQfyssMsmv#24!>>MFx3hV zsrE)`fLL?$UwT#T0;{P~uQPjJ7tWOl^Z`_B?vy_G7>5~FYDi<;ij643KWs=Wh2J1*vJq3Jz*}rowwu+6?Z}Cjr&35pA74(cq$A;xe+D#J zkGiv6%SN8aFZAw+508y?zbTqI+Y8XIdk<~NQP5B#YFv+{h-li30xOmJkx!A7H-7Xb z{F!p%HvKYqbEknC?6&|?ogkK_{$}@Rw$*w&&qsgKB}U*gFqAyEVvoQo>H6w7cSFn+ zBrOmGermn~O`E(fr=~(#o8h|KwyRTP2Xy#a`pE z%=*dAF9pgqljP*djSG0V0j0R6|1>A?-APK52V7HJWA7^P!xYRrTeW}O%0q+#fS?Vx zUjC_T?0@ST@c%$rM{3&k8-#zw40q=ftbkXaZ@*jSe79U*7F{p4Ls%t>V1qFKLXHFa zf{0?1Tx>IIe-`tS_6Ioc9c0gj<$vuPB2O_3_eGIf3U zl)ifpK@q19bb|}_wR_r}OdlHgppk}$azc`XlCvZm$O!?4I0vXilc+i$YE_FwHPNm3 zZb7iU3HWs5{A9NIkFN=v+fe*}2nZlaM^g-G_%DzOzBsiBLk^5gPX0=GQsr_Vc zw4?w1zMT!I-V9B2vNG>%G&a0pgE_V|_Oku$9+)x)$Sg^p9O;K|cw_D7k+G%Sfubl} zY*Z7%aBu9s`ru}Ov4mrLlB#w|(7x%fMxzZ0^n^Pu_uk?Z=<0)C;vN6CeuG8^WFm(x zS}+jpB!dj@sFdwwcVM$_`Cv2nYxyMF8?j9pZvGLla{4=!rFBkh%^%NeFA62+RRSJHJ96h+iMl!+eHNlanW|?TvXWp8IT9lcrhpVaw{nc z*TD&<+xT7KCQMj|d$u6`q9M5NZ^J6>l2`K`zL3qa?j>THBYNdd)NmrO>yhptX$A%N z%IPXZ4xNd1kJ{6+v%10ea&Y}|1U$vKi$HF`?~pFE;=nz{;JG|C2W}wEg?dJ}Djw6odU= zIP@QG^tKY`%sLNFlZJ@$c5DM1V19+_PCHi`rVDDhp1?)Edfhzl8T2*>u$-A)f-(EjzTmQN=~oK z=*#g4HJax9!yIMz(aHKF5)+8Xgu*1CBK=tE^|Vh=GfbpCgx_7#$S&MCAso${PfoNR zmn0&Z(uC@H5W_=pIID7WiqXQu9kxz%LkW}X&thUsX?0VwvWdA07EiHi^qF-NR@#Q- zB>{&VCP}PhWmV&LwpbdjJ9Ck?Hr3m(9pbf{LdqfZ3&&9PNaB>o9YM^_-U|#}-L+a? z#0#{}JNdBm!~w(F12P<5tD+VoNB9>C1=yXvGSRJcbksHC6WqF}S6jce?C%xj=+@9@ zv+!Lf_#|G8jEW7mci^S~l}_(LaSq`b_K2lxWdnS$Q+=PN-M~h!AVmM=7F8Nw_VfIz#5`r|F*p?(c@>rPf{l&@FfnvKTXmK?C$7a|eF z6{F%x#1>(YuU~ydVqh_0o}h3%0aNxfkrC5i@)z)zvhUe#|9%CTBHRG{931uFc&{f( z{ygd&hhLDj-w%lq$Wacv|De3mMd#M3 z5W0-sS8FXC_au+LA%Pm-`k;e+9r)<@^NK$wDwVe)P*566oN5sqo`6}V7}$8`A@r5r zLL9&AnxSFSbw_M#ufB7o8?%B8XBnJ&76%hSHzUdF@DY|u1{N{0s{kWp0)TgUvNJ-{ zixIoqP9+~J24g6rV#wR~=%urdmt=iTr1BWWmw#_2PU=FK4%A|RPIa`Do$Y!d3YZL& z+<+8BJ9l9mYurke(=^R^Xr3_xi%8T#94Qin30mb--E2Z-L;w|RW{Q%isJF5^<=~iZ zk5|)KS;m~>_c1AMmF_I*0<_17w6t+LfZ8KSV#9-b$n4K5!^Dou)mEw|CvOs0b%b(o zd_xqQ;nKuB8J{k(^{Au|(uXA^)r7e7NHrte^q*E+L5z4Y&A+L>kK-vB$hm6-ugK0Z zh^?Ai*+jMDXT$dd0P%J7=(|d>s=fR~t$Cn4MAf#)r;})K)z4|<02m$gI2n}I!Vhc2 zrxk9=^R_u0?OuO(wn#M*%Hp-;hI9?>H{K<)oN`E!X*cR$%D^Or}O5L9tgGFZhyn1(4@HxJ7hN9>dSCDtiT!h3aL2>HMiNaM2#rOsBs zB4+0cqhzKrepzWX3;<88h!g5nDHAjrJS*N*&|31!MoKKu4G0~`NVA1SF~IWZeO;5x zS^>2Xs7}*D3!2p8 zCVq3zseWJJU8#yF)TkV%PviP)_0Ir6JrJElk8Nb5HO_;u5kw?U2#aNwUf?-qqH33f zY8}mdRqH%{0ohD}DSGdP%ASQ&zIVq(HVb9&UCJ(PUzC@sWRLwlXl$>_E0n$ zvRB&LQFY0(5miyj9Jbo{Dm9z4YeB;ASo*aW_~|(hH>tYQb=Nv<|B*l?o(W~ZF0@5Y z61H8b?eSaYLDbEQC#!OPps1{|%d;cStq6+-O4qjrPl8)gthM8Og=*h3Ar=Hup zH8de<{^X{XJQWQ@9PQJ`p%8reN3-}wr(JgRV><#HJCK+!8!Orv#*z->h$NH#pJC&N zw~xOEe;*w}*Y2-jB#d&(pZ0kpeZh8S0n<)*QU^nPD^OEM3XY#|S9|ibOG8+H100R> z-(WlcH5&TjwBQgS_P(h*=t%1VU%*tAso7GiS1yWu3CPFmx?zk@c1~&~yq&{v4~@ho zX(+;T!LjsDjdCDBS|JMVm>r5J(Nv@WYC`f!&C@BcjKBtyk1XQLb3eW=a!bh0B8*Mv z1K5uf)6U#^>P=sm6_U(-(rGNMrSOC3;&4+?00X6#>M!=!8)vRKlnD~kY+7QVGp(&@ zO(Zy@nOlq1>Cft3eu#b0%axRp(Rm@H1N!eq#r=|j6PNXZUkTiFm{=zjZ#^#@#M{T| z*ohXx-k+3D04S7MgpOc{Nr}I|BzwbvNE8YzvfAY<`YHEOL^l;#v8K)NwYN;l)LhSAO5tUb7^|0FjnLT3dw1_Vz;8}F?&}Y zjP!`R;u<)W?%k+1HFriubGKntH?@&hBg+cQg~9M}1z9P9aGBov|GN z4>C!4xu_L$c`?%`roJl*O>)p1+a|VOAM_Kf|Cu<(s+k9)KvmW|+>{H;YRsNO)x~pr zLkWi~`t99ayXMF7P7yP=ZmV$JQlA5>jq54vPR~;f?qLU^R%$FOzddJE?5Q7*>)n)W zGzU)S)%fHBe~B$@p!13-7psazyXQ;Y2ZD0_jMU$t!{Gdvevj>csalytABbd|SwEdv_U?pyp+#(9n z%UP73xFuozdC3fNpK34>W{CMK%_`Q-AD-9J+U*rdwN(Nv-f4rbcnDH z@JM1!a7Y*lB7dzY0Fi}0F?^zJs$I3~H5o(MX7dHkiega+_J(urqryv8EH8Hyb5d)!6GO!UQ!Qu3WihO)-rt`A6)In%&#$Xtc!?lQq znATuJ$p*3|^FetyW2iwI90EjR*h^Q>JoGV*seb6_){svX0%*u-qh7D#eKt?^AZ&F9 zck5v?0vZ`QX?iV3H!@?tmJW&rg%>_+dRZSsmt<$}F*s^4d*G(_N_lbS26qFs8eq6c_-g;DZCgJ8nbJ#n*XY(|%8SQ9r9djx+(g3D@SktP`^SGCZvU7$K3Pi%T2LV&1;12aET>S# zwPtm?*~E63G3}u!t6Sq?I~v9(=}Q{#P$E#S}XsniUOnEJCN`;k}4 z^Vv)N=C!-XOG2fS>bSr6#Bh(mkzi!;Pb_i$TLSyPYp<^qmL>2vg~weyf_mFw!$LGC=!86-uebJiDfu^ zIJC<+HBLH!`o5KZ z6FsH<`k}XQf2ykUUff!gTwj>7{RYpkhQ6i@gF)D;qJu`9sxulwm3m6PL1P&ikop6l zHJWo4Bgu|MKc5E+0orXZxL&1~6C!sYySyvi^pk;P#9;%kfy#^PfOhcEjOIAv5=Z%w zY>of>Hbm}PJhVCTpYH`H6>^{m$UeJ;{)s}&3ePvMZr^>o`*M6gt_z7j^Bd*CRD#t& zTIh3B+8|1k(3)j)o?cUZ>s;Zo_zL_q9?bO4%{a$SC_NMx##ZJ#swTQ^T0awU_vk{W zx7k%}Q$_iuwb|3Z;T64|j!$*tSi+{8-^Xsl@gW}Fe(J*6pVEvn5T(0`^*ko?@_g}X zqOC6&Ucc|r{f)e&1X@%O4)*^}saxvOf6rQfd&6Jfno7e=C(8XZEU|(buDUt zVn$s|#i4K15tyz;9IT2W9wOc&E24@j?uaYF3&w@#$ElkzDt#?-00P2@2w15)gy>9P z2MXwzRCRp>_8cCg2uL$RjQmx(OFb>mwstS?NwPwqW5KaGqX<%TQUQPrBCc|?hrmwv z+vcx~*^2wy5K@mKU%&X z)T>=H0(Af4FE?It+x&2**a}ERBR86_elb0Kzc(GTx#im13t-^v!#FzF%e?~j`>D{* zAEO?c5e4QR`6mbq_!>ZvkQ>r@ZWd1|h{Vu*bw`pb7UWQQYB$*|PZZA$xUJBBjFMb{ z-cgJ{XS|mo0z{5G$}?5X*v}$bc!HaT0P@}h!I0|}de^THV`}E@XkFrr8$A}UGkYs9I%+-Z~Lu!B}bQ$`1n44*hYE>0S znGq$HiKdGpX76ka)&t6c#@xY2G71l4peX9Sr@voIpcq+?IR_K<+j()!5;|8i^C?*; zt*hljL7QT2U5R9;$qmxl?%TaN(@>Jc{a}K1_!B91OAGV+C9_sIECn!VF zEp~?e)#w7Vi>!HbXYCaB^KX)z{<%Toe#CM6)jZqQ?c3>jh|=f?*9@*9u@1^7wom3~6^v&hufh~akX zT++lov)o`3nCP$>{eN~lMpn>lX?wOnC~2)hpmcyG4O{syir=4^y6bdSvY_JDJNh}k z=mCR5BjJ=sAO*A*OPm_XPExReKkqXPl1ZuP68;ScF~6&PpEjLOnoy!gAdBj>Ju1lN zkSzc~pd^E(oz@!R-&H6U#Vq~Bw7>EYo1H6$VU7?#m>?JD&NBk521kdEo8W6l+Y%SE zjtD?zi_tdHi{$VOogJyA{t{^srx2!u14?C@u+}L8j%C`3-68plZcfDe3-{sza!sbB zHIe*aF>bMHyDe1Ud5@?B2)2fJCH0sG ze@ZCJzfiRvlAcb)pKGceC&hmt(^iUI9t0qkFPOahUBX@z!^7F1Lp8_@(F7|=>4+Te zPC#ar559CkE|1NSQfEW-fc zJXWJ$si#oUlC-5qD3<(_A0EG)lZA;Sk6&zZ&xxl5(#h76ba!i=Me2c057%19$Lawt z8$c|e&y67&p6DaS6$N89qXJP72;#CzQRBX9^C4!6(h(n&V`jZl{(fFY&QChrc`Cuhg2O#(FzpYF(zl?6Mu_?ph>ost#?-V`aw?tp%q< zVR>GNyWoNZ3w?WGlU>{A-NpO;q;-cH>1m|*W1=@L$|wKRA-dMd*0)+Fh9DC_-|)>? zhzK9LGtBspiAN?IP)9bC!7NZfZE#m1!_(7HUnngeY}>f6+w6*Pj`oegpVLkHakB-3$?l1QF5?X3GRq+N|A|u zr#v@$+>dm;ikjGd)srMK^b2#TKwE)qxkh!94jtz{_x> zI{)%a8UKOjfUq(BZ=U&i;c0XJX!qgG}?M+QvOHoRFXe~pvtf@)54KJ#? z{p?rWQ)n{%GQD3k0v}li*NFos20aBwaMGGTK{c>?z&S*lW*y9=qo_a` z`g3!m!2D}%uTNbRUytp~lB}<$mi?W%FFQP}I2U+v5E3ZxhYzvPus@8G>h_Mq3>L&` z*w$}V$w$aCWp{YSKKo<6X${BkdYQ?f?o3$o%F36 zQV@Za@MhG&Ab`jP$6G68thxH`t?E@T57adACQ5Ezwz0S@`;eM8e7Zh*rg!(&j#X6G zt)C&Y?NzV<9x3EWBps*k{84F+*xG@|_Se-|?U5u1Y@~EmTnEEGy*?G57zyN;)+bT4 z8LJQ@U+B^gkl!}J&-X7u>suTT7I9a1v@ zRLrbIWHOu@ak|nCySSH4d!#&=qSBtX)JWs#Qu!f`*;Q-ZutQB_UrhD>2bUB;rN*}F_^?WMAe^+4>2JVYDe_; zQ2haq_ls73;$MPH#Xub<10^Dc zOTs3D4j-Q{6#!!6K|mw$XtFrvdwW`M<83ebmEavfEm2Y8(fox&i3KnNslWki@IDB= zE@F1Vk)Hl-{7tiUy3L9AL`{$*WyS)}9w7bM1L;Wd9BF95h$6vcr#|T%yWJ$&M}teb z8^fD3=T3Dfcrz;UL+QFLK64V~^}Zp&jQ$6ZjjWDS_;?jdDXDB(c|)kB33LdOLz^Ad zxQfcSER%bjtPfsNv(|fEv>6r@ID(rY)CwSlaAJaILR58IKKXXO&WCf~85Mlh?t55@$mvO!2QaMn{S=XnQ;XwN&n8tHh{Qk_d1&Aw12_Xm5NAmO1SRjeQ!wE)~?D!$> z(DXts>8d?Dm?6O%fa9LMY`JXm)J-f!dm>ArEuzc)IpBfG0Rg43#JJyZnIs_R70ic4 zNH{Psl@$I^Ww1%fN>XiRGo-TQpT7*b-;&Z)cVOf1B*aZ+)CPWYaU{WsZYJL2cnTBS zra{yw%G%%RlYI{KgProTQs+|X%5IaLPFQxS0}Ix0LT1Um1@=9Z&tgv+Km=n$%HmA0 z^cx-ZjS^Ob1*9kOKYA#=*Kb~Nug#ykW9JLU1WG9MOcH0US}8yUu)*JzhPDzy0?@e@ zW=hm2l``qZli(?~rtzEdhb}%iXT6`+t`;AEkvT-pt5z+&xh?&9-_ze?yjwTZ2!_#+ z;Q#Yk&j@4z5hBCTOBG+bq34vkmu_D;WwHTuIMiET3oxI>Z4Q4*a4p@4{TR)n28w~x zPU1xnspQcRaaQB8J>>gX6T#+@tt4nw%iTgwT`~gT3|KU(vTq(~@K~1OR++I_w^^K_ z`8r~(D^=rjNT@}IPwRKMiBXkY=okMW5-)0~3PmvH!^S=PQ(|YAq+fAvIAkP_#@l0? z715mV8_@JNQ=}W=XNWyKlPVvEjLD+q+~Nz@)$(YCew0+^o3?``-!jvmaW}Z^+>y zEZi2elvb!k;H_y#9-i0xFL7d^J2d3a!D~Yf$3_!@->tC-$OQzF@CGj+(oq6GtD(-}F!Fo})+n7~7$!aoEutIFK?pVP~ol742 zCpq%Lo=<>~4dXGvwpzJd5Fh4c?AdVHD1*LWgvP9<-Ao&ns@v!QAub0e(-j;;RaI|i z;bGd|I zVOv6|q(G(f(&7sn2m5$>9ZpaSw*+xwpqP8=sFRvOQ)y{*=l!9@}m`+Jb0p(NF1hn;pboqLfEuS6*3uv`?z`P;39Zttk)ESx|YfiU5?D{<&j@9-1R~F!9Nba@G^PI%HkwUDUW`huy=gcWC z*wP#L8)`etP3I&dnQZXdP1chU{*FvksTLD=kBSLXeLS%|q;_ISQTk{F39GJ(Um_kT zbAs$K^(Xm8tV|fUSgW*sB!Gu@)X5s>^vV>;gn#7BAqDgt1ESHKpltHowtg#xx;EJw zwUFm4blE$%)g27d!G=4BJao+{+dTSXOZsLOTr}-2_tA|*Pj5~5vgr6{G1Tp?s$Yc= z_Q$>XfaO`7VC|Z$m|V)8x>bS$Wq5v$5X0MZS4*_Ez{;rCwZHA219C<{g0rqcHPj;Ykf?K8b7q-PLO zMNlb#gjq{5ErPKF)F=SBbY6~8C?bq0A7XO%L6c>EFRq?WZb+n%Lb$<5qLXM+i~tg% z!suPpIc9P7?Xpf37=HgD7<2$Vz}u<_MB*xq8I0ORfK`#35Spq~GPv>gHOv>tb7J3R zbGo&>?OOcqPr&qS5LP!96cAX;$c6L_8Cni%Bp{ErZaU@#ThSDZ zB`vse^}4A;wPrf~sO4zrjoPT5azJ$uSWFMZ!d3RlQtd=%VXaE)EWHM6sBk<2kQw^e zRUSWf5y+H3l;KY`oUN&sFLNl)Aciaco-EfSDUO7u?`*r59HOlaH!9@ocTIO~_GLuJ z`YN+qP}pv6G6;3M+Ohwr$(ClgiHXz3>0E-S+>qy$;vmYIDpv)*QX}>v!~@GH$?r zxrGny{>hhBO;QFD=uC?1sOp@3*NC+qinu=rdK-v?0mf@|3mK>Al>!cY|2Y0aR-_Ns z$)&xU^(>!7MJdUwPNy(TgSUp;ba*&%PduT)@*j(vNuI6St6h2C?L48R`%qrKwZV$g z7}=RF;WOu1_b)9YHT=JHJ#jS(2HF5`M1XQ-`yvEN>5v&5r%cWpn_;2V+q` zS|bNO8*vgFq3f?bqbQ=2LH)C_$|V^t7!wQC80X@k2diY`R9TUni%O>L3h3zbE=!L6 znX)dTcb)^JTErqw;4??doQz-`pg2ozNDYtj%_sL=8pg@}$A)NHOFZ%c!g-*1PPue; zQ5kGK@EiVWZ-P3G-!}^zL1lokaA=O9?9F>;3i-)$d&9?)t&~D(|b zPQCsL_rtp$ALnvbR_yciFfoe_0<*@gXz^Uq@b@gt;}m#ziI3)tHbs;-T=s_HBZsa| znotoYlyG{K8d`o2T?qNrWDqWY&DPf8Aa+_DOkTNGl3`$D!mu=R@+t@okzGLjYE7FYKvQN+iKdqQ$1X`n{1jl#f7WJYB9X#W2Dv;}Ft7 z&2!wBfS1;#DVo$R3J%K1_Mcdv?>_W@ zcLO(3OoX7HHpcDyQ>=Dsatu<#ph@`y&|>Ajl8@cfK|oZ}#<&tn7T&eMmFNUd;Jz|J z>Jj;Q4kM$IMn#N&L6L+0OgoN%8w2+zzm_RecE0X^Pt8c+k$T~d(4fKw_nHj0=p5_` zJR{1&TaQ^9WtAepZvluXU=pZwC=au*itN@$&e+^r7uSajHF&PZbDzk<`Ff=hp>JRV zoWR@QQ5@k0|KwC5iSAo<2nC1nB(_sHikMvKjmVzE`#8&AnRS99&KnO{JAryQH9T!rIE@;kOx!VSYi}%;9Rw7Xey5MKE zp|He-Rl(jS1#v{v`6Xt}`%KObS39!|eRHCrWY*_7D#G~SPQQZ04Px3cd(IceRD_Kv zFTD;>!PK)3r}S!$t?7NeTADGb``2PWsqY0izO&mY1VE!=uFbaMBrelD3J&7@%hc|5^$xw_J+ByD1Lo(xuzL;&$yua+)vII9+8VmMn{z&nPeFmL_SsHRU4E z_wqq-8>oKzZX+Vkg8rgzg$*&&osvc!@&vlCIosj4ya3bvDB=r$N!JO9;nfrACd|aI zKzoTT4eP9N&72`ZCATEBdK)y^*V!n9l`QWzJ}|FHVl@pU?$ z7}tAuJ>jf|1e_DbyXjSnEj3imjMmO|zbQ&!owkVG4axP%$Psr|$@*=m;b(c{YVd`< z3t+MAfRb#lXq9%|e4Ty2VgQI2hp{a>L6c<~rVzinfVXO@$_v$9YjrJep<7wUwEt#< zU8Pvr)x$wxXofumexZAiDBQaN*$QI)w+@A^26wDbxG7uv80@k~~mXSO#7<2ygp$by#b+kXCIkMHJ<2IAj^d2t zw>?qMOi(J84P{>Fi{D_G!7K9#&5wue8f40 zhh#(j6#0k}vt-2csa}{0d@RMu&xw~LM|>_z!QJ(V;@)^OMBcm32DyqbCQzlE+$k%6 zmM~!}bEv{gOuG#*b&P;UVQKtEE*;z(5POA(R!MYqqRiQ?n$CDdT=nE6jclbP4mJ@` zbh)Gt9&9L8)3e}L+=Ry{GV$W*$ z_^?qMY)=lTA~<~&(r;|&PsBB>khE>W@5d=9_WMy?U9TXjfbCAQ5Rdo8>a9;NOraF0 znuCKcZkI5vzAqBrR#LkK+yHE{l{jv~i9y4=hdlL)@8J0q!~cWX2{B_ui2V|r`MFqBsGt@al?zm~{%utN(=!p~i$2e)h|t&kuU3S1rz5^< zpasJ#MZ%71xA0RF%I|e$PEtF%=jnC(RQ#%YuwVN08?LGTpxIyXT)puM@lK8Gwru2B zVGCv5S{{R`Yp8fx@y4=^YOT~ui-DomEbFyupcZLRUvaOFSBv0{!E1HfYj0n^PO02K zv!onWz{&5$VS=E7?>PP+y#NR^+y8(55gXfoun&vc-`0F~#Ljz-ofUjpL;_e^?%auk zLVK1ZQ(hSDtg9+l~IvS?$d;6jXu9hyOh z#DLypk1XB9bTgGdVdMLA|KE)`o|&qMpExnpnh$ zV6Km|l*gE3GcKxvSl)3vH9v#2m@_1R=mFZT;5@WWVk^6(orV-#n&y9WBJj|Y2jOL~ z2jX;_pr6TzwxS4yyk91e6B|iKfNnZZ#5k|OjZ-C6=6lSr7>opcshA=3RD{ui+0nqk znDT;%kE3LXg%F*`DPh70clx2?U`|K_1SQdm3Arl+$f|*644Kd;L?Y&B%6Ayiu>hqY zcku~GV?fBlcfvs1Bbrb*8TlB-$iSV*>B14{R^fQC##$8am>{s99Z~ulU=$=>%tWR| zlm(n-F(8S?B=%)dpE;Y*~WKm;taSF&sY0`m?Fb*4>< z1Sx;-Yr0cnTSYW*^z0GW@0{y6NA)`p^@_k8?ssr0 zzNx$xt8;9fC>UrIb28Gfa5h(tn{@(fWDzlc)@`V!om;h9jfuN_<9gZiI?B7sOtAXE zvsgZAXQhiMuxP6(#^v@e66TiaWtVEB`(_7RSX@E1FTk`3Hi3c3Gyc-mUc;8((<$cQ zfvO(21+OW2kShK-`)QS{4GIv-UPByKB0R!JyuaU}tCo57LG0<8G!lRb_d|g9X;}{q zjAAe2#VmU}bk;IT-#3RQ_uq0VZOuE?SbVG0<=981c=Pxa>J|sJX+pWRjfw};0h6I` zsYjJ<9Tu4f8>HGTAzqNZieVL*3u|xZeiDR*iHc-o>jmprY`IaK3pk<*$ZLq5PD@GM6g@M82DE{^)MzLg7m&HJ&t%t z?yxyWbn~IV8@$<-Egv9*u8SQU-UqD$wlsFx;t+87OM5bng$m%4v}nzgCqK|+V_HoCWr;ZV@XYA(r*Nr959{aW4J~cjR@KLNn0b}uZZq9WT(585v znhLOYbFQxK0}tTEy_LSZNrR*By}!lVn_r7)Rji}c_Wsn`&4-Sf0kiF{y5fpNbz{obs3-HOh}|8|P!8cKbCchQRHsqskcqlDQbc|1 zz$MMSQn8HyRXWLAk&f>Ww~M1!J3RRg;<*5<@ccPm=LWEH6`ri;=CL~n+q&efx8;oh zezOGEY}JF6J#(O^bM`_oVYuvvF|YQVF=>dg4pzuyUJJ{Y?>p!B^kjc{C2aAIJL==+ z!V3VJ6L17R__jD;n2`28?=*5u3m`p1baW;^JH^bM)kF||kzqS0nBJ#P#4WBkmJU6I z1nANO1SOMmT$2Szl<8|uWR0AO8!9ka?<903B-#x3X!GN@=Ec&_h#lL~aDa6Hs@1KlT_R2hJ>_B06gf zAFy!gdcV55HxilwL2`8x*Z!YCVia{KX0HA?D-w3|bmlnaz$1%oe7iGVV@RY4L3tx1 zzSgzpgU<(BS9|UV?UhDOk$Em>K&>3@fNhXoR0(h!JAa54`3JA;Uhhni2}#EIEYsaW zI}RKc$G|lyTt|wwTy%=!oL@RZSsm_%Y_ zRr4ZZ?}lzg5izAN(c_a@VM@zSm9nHt1hI*PFr-3UmSJ)KMz4QdJgWhKR`NF_Z5ue7 z^ZwMSUBjI~a0NPv&y=&+l#1v~32+b1ejW5=VPL!qXZumq40Q5jk+FjM4W&rz*nGzU zw^*tLM?L;LrgXGHc6iO`L_m=< zl|-bLn)0aZj2H}=>NhI@(X4J}s@N29;ubH_WwN=IXi@iLU>cfK*L$E*jVGZM&FQ;* zBUIxIOL}MO3XMeo-+XJW`_Y~n=6eM(C&&e`FUZS=TTIKWUPRipcC|Z6O@5pKkafDL z7j;Kj>=O`dKxSMWtelApGNh6;mQ!_Z17hDwf0*r1j-ar;P zFu3s#0`ggU$MK77u71}#F|B?v0yHmwwudRTz47)@>!nRF-J4y2_MSz~4^f1Mg=m26o4i}i z^<5NgPGK@e`&3q%1ezz?ecT3M-KpE`Knni7ftVW^Sd$Y?$kPC-UUP}qs{7Nui3TeWNpR-_YEgSaXJXwzHz?o| z33G*>kUucplhDG@^TE$LGU<+#kv91@6U#+?_0TLe)MfWZ~; z2PzEH6=G+JGd3#dahy^{ZhWXK;fELwrkVV;T(l=dmf38;kaVVkvH9N%Uq3j7AFpZf z7SfC%z|dfbf{IW~rm_}*`>UOd&j8Jdvn*0YhXi{$Qr0^Kr zf!-r^G>K1M;5p2blZr^m~_M%_J3 zCF~TW*g`Bcdn(jz98AK-b+k(;&HBOzXSYq*YN_^y*ZQ5*Lf2RV+=g5lsRRKor&YV? z*`X{n*gu5DuOkEEP9L_L3a5!9OAk(N;Nj5A+yrNaCE-U+;^X(s zf9y1~ZQERYY7&`rO=8;p0VEKnh^6oOJ*zz6A1{rd@ah~-FN6)buH4`M-R?myK~(5UTFO-jJPDGH%lrhWW5F0rP1Q0aM!ZfuPgSKe%>+%cs;<_X4AD` zhhe_YxNN?%dsPfqGGYruCcQ!liSZYE+uZov>i=kFyd~OuFKm-La+j$&Xk6$*1Q#BA zWcTI>^Jycc27eHI22gD(Jo{l+NBJR%>#$JDNbQ6(J!97;HEyuuD{>=#DWptx6fx|J3y z)?{Hz5`@J|A*k5}cm00s*tpEYWGBkS-a0sA83nMVg_4j(yijD#%pac`rhK%BOoN!M zv`KH=e#I*iXxo;^^QKU}xzm=|;dc^j)aH2kP;%ZZUCD$VCJB%g7DH+%Rvq{j?$FI} z>VE!X)Q{{^@y89f95qTl8N#04Pa*o_@M|%Dg(lf(b#`z{+pe&vr7s;BCEkrTS|A(YGf=XSPXTvVR1ZYKyv8|$||(L>wz$2vYyt_RTG7=+=> zdex7>UPVGH(WBcJFXF`YZ1Sxp0170^4D;yGUk>7krPcNAE%&Nq-RR-Z-5fsNQF zk63Irx{1rEaQ&Kh-UwxnOtF2|2$F!{B|rplVTS@hOKPJ)U~~v(U-#&ferrK)A7n>Z z^s)s-8OMh{sjc|O>04M+p|~*I3T{pgK`#DsJaLbepME4f)!q4PKH2=}l`Let(BiX2 zaG;M9J1<{3PHT%{JVe3u+w4I`D&y%d*x!W{6`AHq(&c9nX~cOpi`Cu-fz-yfrhrD> zF3C>PO?>JTq}malpC3+toX)`K&C#Ree~a69vVZK8>w`hxHjq6xt%YwWqnRo$gYREI z!QxF&RlK<1T3GRDeg}#6C?i?%S9-#ZS*K}`Sh^4r-pQ9%MAUDd9=(>euDRKOstAFQ zZCJH;+m{q11F>0c{rTwnuuP%|rv@xAkko@|;d{lCep4LNBSWQV&<-Pc3wD2!@Qt$& z!Pb0`QYbw7<_wqs>LQW`vz9iC=l*!*ifK_c>0j{6sj{-F#V^x=g^kwsPfByWX!J{# z$kspRd(xopq%31~xaKI?8G7^KK@ZQEpyi=A5ovXE5#bnMQV1PP2IF*J5djQbyAZeA zuTGpPNc}U(W>KE`#?Vb|pGXu?kdsZMFZal6p`g2=p!N7YDIS8pd5q?f80t!G_u7h# zWd0T{LDN!Wo6qg=pdED3ZN;6690PuxFc?cZM=c|7hu_(x$9k4V zO{=h~Aw6^2-XtLWAh(TG?mPY<9;?`xxY+=Hh$_j zCc7bu$!f_C0O?3Y-R-~p;~Pt|V$V5}V3iz5dh{f^za5LQ)CZ(^{)&E1ZEQ3h@U~c{ zk!6W2hPq;o1wM9!p*OKYJ@WB`W^vTol`0AeZf_t(LYLkn6=N#qOmcSIfK+(%A(k>^ zJGA_8S(;EUA>So!STZE<+9UNqb74Ue7X63zd63^qJ`8}DF#W2j|Q z{%ar+@l!(UmPl<`^ zU!WoPpA`4Lf)#NQQMF%BW*Y9thzNde2pM-Do3#_NDH71xR zBIw-a9&wKsFZq*yPoXoE8B&*j_KB^hlq@_$M!1DjBvAo!_tIh3JOcR#m*vqxP{_Qc z^w4Y^S^G02hmYpN4z$5lIq}|-o2B2eCw{&CRbD!NT3UDPj_cnpP8TkVo)Eco-2&%+;+{C87Sw@V|7&L`hXHPht$x0FLFa;6E@*`4lua+Vg+ian zj%xvbWa$ea3<_kq^!C*YoegBWiXV>ML)sb;u063|*mt6~3WGdsDV{ZHN3DWur0G`o zp&3$Vx7;{05uo{E+Fh~BXSc-5T#E@tYq@vzrgM6-l$I1~M9_7LFos;9mdANG$ zRftCZV>{k97hVs`T1N>6`6z<5GT~-Z;%_=@aJ&R~Cz4mrUb7jRz)5M9PX_v|Wmv9K zgO+bNk6znQje9DT1BYX5 ze=5EMQoW1|pOF9(cKqIBBiKTJr^$Z1schn`qAwe`{L8mYGR5mADQ9-GRpG|D6P*eU z-Ot>{csqMZamU}%La0QcHNKNz@&eP$P1DX5GLXAn$~%{9F#jOi{2Xrcel0XgZ^3h^ zyP9E5*)Y|=%jRi0Z~g0Q<{d+4UqtWU_JRM%&$(FsJ35&B zUqukz2cDYclEPO7}6R{Cv^mP#aog_)=Q-{VNZL z0aPYQ&=x^_Y^tzK!6!Him{+3FA@qQ67*$f)jHUaHm(1xZo%(mA#3u;(TJNEW!InI{ zex5gsQXC&WR^~)}Uy$Pf0IV1IpG+L*sXL@3`2vY*2|tQyaRG7y{XB@u3bG#3QQ!q@ zUu>NMe%2&Ssb=Ih{JsJ(nHsd#g?Y20*bS27G)_09_7-A-YnWl29pi~Cy6tDr$KUo|*ByxPUR^(Ar z-)3AhRED&QE-yACs7g=6NsiV7dh>Nltg|c(Awr+k?+UiGGdO}xDbZCgGA7e%EP@%obCPP2`$*DUj$$m< zjXIzmvTj1$W`&*@M;ZlJ0&aSUL&S?I&+@ZIdMIm3Z3TW1)py%C>sRA4w&LVyUvo z9$0o)2lFBh@<`1gb#cOjbZ|vp-OzVM)k(|Z)KCW@)TZ}lmQ9~1UQ69qnf)U?JuoG^ zu59d9GgPWI6S`Gk0dHxfG?-s*)cBb`TZedP}aY0-q2+)i8_#$iIlgyuT! z^?7=?XF8)8PfpUa**svl?FTQQ^j^oRoF_6>K2GA z-tn3##4m`7BZdl|JGo{ZTuzA11B+nE^6QL8)S(36lh8AVi&lFHOY;Xm2(`@_e1FOg zUxCiKqG=okarQ_9wmQ55E#$r&a@%%iz7_`ID^$#Qy2G;{Jee+IZ1hIQyVt zEyEt=PG<8&`Q|Cnl?m}Kx_0R~H=9Dw%9($G2V|pbj}}Q8B&gGiE?ob<@h$P=KCfI} z>2$APp_Mj);pT{~08#!QaM-8kpqD&Fe1FBhbvWf!UWXz#bC12dYlS(1-))A;|5DDJ z35cj5Oicd=hPCPNEm#9=8|)5)p)R}I4$lt21`)*zGODfh3#v$F(2$RaP0=j{qVfy_X z78ctFVF8hNTh7t;V!S#$F=gVqm|yd?Vj2K+4ZFIPN%X;FxJqM=2{>Vq=?en^Q5@{_ zqE+zF513(PXB3M3L=Z?2uqcz=FcbEBP-(Ehx#%GTuzi!uS`__~d*k0Y?WeD+%}jKx zHg&d-b~%k#kvR4sSXopQ)6sMS zEc(acEs(5zorT$!$hL8oRqNIIxu*L5Q=2J#Q)T0~#}T94A3=>?qn-@dhTa)ky&c5( z-LH*QhUa@e55dAt6FWbg>#rlfsK)jQLYP>d6RZLn@wEP7YD&FgnA&JC{o6|a&%yP- zGw*+>SXqpi{{tBwceFLRFQ`hh(kb3lKU|VilQfiDh>r_Ebz1}E#pVbSLihG?U&gQa zUoQK9-1x7E??4LfxQ9@a9g!U!D$on{xI;bILPW?AlEX_1BgS?uB{#~P0E0o-Q0O{Z zZs?u8GUCbcujBWit}PH5Vgn;Eivk0Tpx!WLF?P0I{%qLyd$ny0l$e<;uZaG7h+)m( z%R+aGxiM?doyaX>9nBTKe}5P!<3&5jE6JwnMT8hY5dV!G+jkmFh)C`-F$p!Qu@Sk#Tow{_4|aF5if} zH>lQREmrFMy;gW5VPESDu`$Y8Y!%H_8xMMJ*)n$R|0wqxfr=ayks9ySXgp(d>H1AP z7D5o$inGnRk8(!UY)D?mB`a)N1gWC3$^?;c+D}ZKso{NWnoX_e2fguWWK9kSk0x^(=zDbpKZs9dw-+^k4&lc zazOuf4UQlhUHH2%tiKH|cv}>mJOYKCbAEhoh*j#q=XPxoR(zbf1~dJ^p6;cDC1)H?#)yMI7)kzM2CF6d*T2=~gi4 z^XTUJIvZSHoeeaKvpCv)*zxsqWX)BW_%^vR^RvTGX+Q$trWktXXD2wZef_QB+*5Hb zn-88-FcL8S&Yv11dS>2buJC`^9?UTA?$&+qPxeajd8Pe8+Um}s7|5Dz|zK-?? zq(>}B90+`Pp5-(U{Ba zgYee~ycumZ*G4Fj`=C>0Iu^+5wG)jK->dZrGE3%V=HSN=LF*wU zfTOLm9jLH&WiGkxR0%bsq)teTqiM8x(~G3NMBEwXf+&8lh+IVsmI19Gk?sRB!v69+ zolZ21eWKbCAwN@WqsOg|3DVZ5XW4JD`GX7|N_eNu(AD20{MLHP|1VKPrU)njX4~$q z!v;0{C^g`(al4E~3rNN^gE{0Vz%yon$6}H5Xy@nb?(OW(=u*pYngX?go6^^JEN6?l z>#H0_*&B83MoP=&kZwO^0OVbkd~LvG?dDI{?@m?fwYyc`2hk9iUNOaMYv>6QWBkwbPkFesOr z2MItX_JU=FtPZq7uwhd~WU7kDT@Rg-R6IPS8Q9YmHnz-$xD*1P1a3I>!UJ`Ju?h4< zX0s0N)BIx!_exh%paV8Zj1<9(r_wg*@nVBa8C}E-B`i!>(xRjBq(y(`l%9dkqrb%U zHk4OP-cDL(Jfhabm$1}@x_{4L`l+7G3>2^)8-hS{IDz5=yMd@&#%yCEB=fZknPiZm z%LG|NHgMV5FCylZr`j4_l98h(IFRSD4*MN&2QuQB(CNB@UZG2G-c>U4`zdu|jNPOS zQ-M%HfmtF}Duk_m)c!ZGi!cMpyRNLCH*Al@@&Or3K^bX8$AeamdKwf7yOJ}_^cNUw{tFE zJpc8V4XVNusE%Wp#TR%UEeZG?1wj`Zs^Lp}cPREndu3n2$IV~12K0R*I&nif*CK$S zn(pwWx~TJrZ&-4o$FWxT5JyZ7cRzq*iA@p~%dAw>z3@4Ae>(x++4g@0vw6oR>8a+2&F38=qsvr zsn$nZgJJ}P{F|M9TaGcQUjr+TH`RE<`-7vet8ccs$GTftTAwWaR^wUhg+Bl?#+OEr zooG&Hd~?N2FPKUKC^zSe{r zTxGi&*`PEk2`TUFFB(5C1sZ@1%~VS?fk}0ABkFhrV*`d!1QZ3+WikwluUVsV?j%7r zFAsj!kZLg}O>sp`G87^8bNrf|nY2IKms-{Jz45?-vmL$g^r~20f8Q$b7MitiJ$GGl zpsi^*DX@f^r06tK9>Ua3W8L~*k>!*eCUo0Wv#aO;?uuQ`ny;f;LkFPMM{Ba< zkNTG}SH%z=qCY&bZ09#62E=`Eb^7B++f31g^vbk=mJzBa$AcBktxwd-Somg33KgGx zl*%Z!Qej>*o2tA-D^S9szTeu?0h3z6SxcI=rhCh>PeQw(*%gh*&6}?hDfU#^SAf>8 zXH;JxrOr)~`B+S4ImFABoEJe0Q?9BD$FZv@sQ{QoW(m7If&(CIXThEzRcE+SQtrq# ztOL7ZqHgaaI+Owk{$$0LtTzNV93J}_J1KixQ19~Z2jQJFLOG`yYgoE4b(UPxLK zSz++X;Bjdj)s+BzI^s14;g+QemIZU%RBbAn%3^=<-yKHh2)Dip`PUIk*V>F~JCe(E zdR|lN7hKvCIz7(0X!N$QBqhkkCbcc&Ir$qv?zo5`4gq4!Q@J!j?+PuNk9v`e$v)}W`j9A3d5c5tlfxs49~{`B;)4lqtIhO68a z^FBX$CVw9?57_QSK)7`|>3^9$xZgGn&maf9uXh7tGPLlAZJRL(GKZ;__RZ7a-r08Z zCp3;SicuG!-{*d<4PWbtllY1Euur#vb@naJb{^mP(GMo@Cwm0-z%nz+3(# z#A6S@2+fA+Ae%EmEKm0weMw^(|0MlzVosXBW&%RWjS)N{nxuDZ;QtHtEafVF=r)l3 zHyFM+pGI7CQ@B)|Q9FrZ^*e6rovHEbN70FSe4RfR-{;FagT=M{88P_qY z0+B3GMn_Tz(XmPlW=j0c0w;`ss5)ZK`g#$-yNQnr_*)F(_rX`+5#p3a=gs{O$|fn2*QF`vwTKL3ksy51wv z!3$)%-veFD@sqBRJpg-0;o<6_LPPrDT3PyM!nF5WLzYkKw1$^(I}T8DI2^;m0njhN zF|HtM#eU!#4c&&5@wQ-0PPQ;+vD?KUA4RGG;+xHQ#vm?`$6*91)U6d8>@FOU)602( zBMI@Uc}D?67*n^$_0H!{+_U$VpYT|oI`xZ8VQ0u^3Aa7YWSYck2A%A;WlfO|)(`-D z-3-DU0Z}`4Qe?-Zd^?@V_&W`E{rPu51Cyn_@H*j@VTccn*#NQ5mOZTQTXxeB9ZVUi zJeZptoh_~_KOX3I8z$WIe)LcPg zl4im0v9JV%v+|$HttBMtJN`s6;^qSdB>5s}u^g3h)ZP~M!G89AI=N?n(KSDy)Fpgg z9u^oJnL?#I*h>IgrwgIU4jre2SZQ=sRL-ntCAhzE_V&48D>Om&=}VUV-9XDGE4rsa zU4|xxncGu6Pk{|$gz6#lDcz+F5Pqx)$OmC&whrsxG|&8)4db>oMqPS+^(~`#clqU- z8=2K@wd(G_UyMU|d-Bx|AP6{}NV5aU$QDl829*BiulTMKMTC+OF-SkKmvm1J-anUq7a$*eweU51nZjW+IS{*>N)c=KJL}8$D~0I z#aR}o)1(gnB&UU6wm)5$HFZnbMqhXqnDa7cN7SSC-4E==oXM5|=kvOz0}s!+!D}oi zx0w;T>O!bQL?R@d%Y?Nv?pBX`D5vzhaTGdX#JtM&va}-ENu!zB=hneM6x|&S+hcVx&s}L zMTt6$s1CfRLidsV986qB5FIASa+ z1l{i8so2o*YUGu)m!xN2UB|CacO3<($39%!_Z|MZ z6iZsu#e8Ya`phCUP0NJd{B|sPL-2&1TX{#0%Brl&%3Ncz*!9Kk6uUScZuSoAXT9oy zgc<^Jb=S30`umi)r31L>IJkhTX!Xl^(>}PTJL$3!*)aHedU=jfZrq7CD^C~_E zh4;Z)Wz?F_9mP)}*VYo$r)Ujf=iw`+7 zzVQ!pwmUOL(%`cvo zQBwdB#)}{{6|PKL#eRHiLAJr1Jfq4So0?8TndnfvkSNc(ehrpDf`>9UPZ^v5EEYhL z-X7qCsh|mRC@*t?KT@v<_8k*w{53)u@wF)h`(7=y9gwGRpIh@H{#5@NTZz2c`Um+9 zawn)Ki20B3+?1z7!K7bcSzT@jL8P7s5z~x+(&W%f0)llnVCZ~*ii&#bazVeMqELJF z{EV zeTg2hrFW_y%&{az84$As+D-on7pT0&>ltJq`HcQ6GYZUQllM>(_lF!vZ z_Ns5mGD+h!JWTUcCqYu5h7BAro9!SL+ij4&=+g+Mi?uT*3h`SYuS$IdmoKp6Aneu` z?zSX;=ZJ3;5A9r7Y)?8>e3{H3oT2@;`-&D$t8=~U5kz@%dq{3zo;H(Yh{)bq1Y1hV z%B0h7l}E0fxS;xwgXA%M`UDW-_*kG9TzP>!JJ}2ENPIqNLf*veOR{E``4$Jl1Cd#h zW~@n5D_X_b1Vc>h&S6%`tf{Vp!C95rh;;RoroYFhpL{%xBmQJbhT8am!~&_YiF!X4 zxAN`J$E@j)VW>U~uK>af&lp&+Zw%LXJHyncd8XUvO43Xvia0>>9|APfV@Gh!n!a&S z(GhSi=R>}n6 zYLV6zkx%#%yP9T%7PdCR+@U@X(w>Mk$j#NHwvrEY2G)L!6Glqz_sUx{ondR;RQ}RU z@uI6RF-Klss8p6L{SBbVq5`PZ_g@F$n12ob`rLKTR5r|A`MJSs)e0F9UCC#R@O_Wy zak5C_QQx!-`3LttqT z6zdne?jk@pVz0Di$%ew?(1K9JA3o`Oqq}7koK1u6%Wf6^;}k%r?A*8kJ%QQhRj!_+ zPmWb_Hmr~-t;HI)OMDao}+r&P?*DX^g+rR-2$Q* zU#@FdYY<80NC)t%aP>;rWoZrHx)#m8)263sy`)Akle)28Q#HA2S;zmq`(mC%Lq?#9 z_bIDrPm1BTK+}KnSD&SE`d5Pc*Q#Gx7smoYS3%s_2!Tl%e_;mTEHOixV*RqLjoPN* ziPw6Bi}LIT!qmF`;1SYz;eIs@I|Eo_;_R~v*855N*f9Xb)Eby{DGqf6J>>Wq%YzER z$hMhli(JPnB)U|Rgu5}5PfRik2xw1()dVq((e7TqcI*yuCU?Q9Zr+R~j2_%Wh{&A> z_j415WPNi3^kJ@8eMMkL&dSMNF_!Dv%rMhCwskrEeJ1tKknB$+G#+iUse5ceP!_S% zMG+WQJ4FEXM8dj$SwT(hyyUti1jUaKJLw(mvK0|YhgCaVG+Po5Lgo69 z<~qxb&p_!wOgt<&oUaV%D9x0i*%wRhG&ZRDwnTrY!X8G*7Rn`dsp%%G{N#HuFWvCHBUNr z)!O-sgg;O7T_^TxyJ*zzb7t~lQFI4TIj?rql64YTfK|o)d|svE>by11sJz4RFk%*K zV>!SX(b(k5cutMgenTHCXta{%J;-B@%Wf7Z7?XG}2T=I-yi|7+e*ijo=@A|=YwxAh zn6O!pe90KGy4%q1l)+%STY90b-Hr1bQ0Yu>8n<(H%2FSD+xxqZA7rpu7$HpaL-8$GGTNum~nI&Wz@2Q>!z%nYe>Gd?zWA zQj`_tI*#pK8LhoE&*!cOskg|YbTaRO6uehpw! zHIiO)WfkK5xVvy8v(hca{Tya5HL6Jx8edT-In+}pnddXY3q>LJw`0Q<<&Kw)BW`4o zW7*|PFOlOaaQ}3eY<<%v7{36-M%S&Sk`>5-O#MK>wWdt`RhijdV5<^Kp<>&J)$i`Ln1zM-Hw(Cngg)=1X4SG-gma<9x?qWYbN<8foHmhu62P55-&XpHg- zn1mQ8-Y4=<@1RX)CI6Yj=yh#pz9ozaGrDtk_?z1Ls;@RIORF`69>A~S<#4Bt!%{l=NC#s9JHg~K4;wF z##a{xFo)IJ|HIZfcGvxXYqznTG`4NqP8zGRc5Guujg!Vs8r!zf*tTsozSBR>IQKZe zv7hW0u&(v}XwBJ67}3wAC;~?{;Xl(X$+v|h4J>IhW8G)2*W~1X9EIkJ;SRv!I+n%q zcgN?Tm+7c~NXSzLno^Xl%dF*ycTPd76HuP&VCEcdAvWGcS&c6H^SiV(0%g})j@O<1 z+QJyvA-wlJ2V+aZzy?xA(!d>eiLhZqELSp)B5_?8@morYlcuT)u7X$|NAP4L~4K+{e`qOcs3Upd@~spR%RicG%aB7JWuk z!|*r&hG9GhCoG)E-1=_$05vXI!9QhRHPRm-%aLL1xG!MJ4(=w~5A>;?vpJW`&wlcL zjUlsw{5O`@ib&YdjkMWIz&N0v0y9d4-`WDA$-(%6J+-7N!0f}V3EYM#GHG=Ofc8& zEWl}|xL9$B9e3EUEbUKH#+OcXGDuGN&|jCc?{V-D_gIOog&4g`G?ZVOJi*C|QopP# zR+PQ$&4Ie{rpb^LoNQq#(TOAw#E*cM3}YR4BzHX4DtZIDPqqAFX7ZjMQ&Go%S-58t znto0?3#?1komy)*05=M&`-dKFX_kgss;qS)EDFzZihXa@#9HX03|ssQy`tD0PUZCR z7bCoqb(Jl?RUYq}&9K|N!(r#T)!R-`_1v)Jb}si*;L8(RHoIlb8^oKxB-hhlQmM)H zv;3q&MsEHw)Mx44~t1o}~Krs2@iHC_QT?C>w0Zv+iLo8@TT+ zIOi&EK_4BL09w%ea_iQe@fR)BuOTtr1twq|M;FE)yF;c{_@`CZ7q||r9h)j-y=@B62;XLsfb6PCiW?*$__Spln~p@|K84yA;!vhEB+SW)Iw&Qrt4vF=yV; zG7{H$1|BigSkg#B>#*Zx#|-1V?6rZoa3&rWA>*X-OZ17#Y_@prwR)~H%-c+!Ouz=`mF1MwRK3J?g~jLnc9e)8o40%f2$DdN}sQ7kOjW-(a% zDx(^@8sKy~Kfp{)=CGEpLrMcrf&KV8WK|v-vvVVAa zKxY)ZmT&t!fiUzI_4>sT*?P^btL|f2czF)Ywwk!pyB`wpU@fEz{4Ki+r{cx8$Qmx2`yKou*N7kb zlqn@zqz~4V`_udAh>zR2IB42jqA)y~W6|pNMRsGY_FZkXTh^CZ&e{XC)FPSPw0|&5 zyr|)0to!>E$a*eEztjANS<{Ozdvpx*YVo|T*XPUm6S76D_qmS(M#aS2qf+(Q0Au{$gW zz3QP|vR_LUhrxNaYp}F>SN-J2@;OtE!z9e|`Y5FcZbLO|E{l_2M5o-|=Pq!uP%Kex zqis>YH)+wJhwZ7TD5yRL2Nw)G6%G=Fb2IR=dUP!4`dzONG#TCoWUA%{s3_n$81^UM z1c37av~?@dJxe`cmUtX}{$MDdz|*@Xx<-^p*>*OpcAd#NcVxOYC=-6SpeMTQSR^Hp z*E+=a$4Ajofq2V5s%vtMU|{IDCz|h=wl}+hX<8_4C<9~k`bbUa1#lXLj(AP>iv#wZ z=at^uG@<}NcK$<-$rhYG_>S9C0=RiBHpGh52L+3}ynUKHaP{wbk!!30Ut#y}8Rb#6 zZ9`!(COjk<&(FPFB~^>Dt4bW$1kxr}{i(I}3#~5=y}=7)eDcp~#761^DI&EosH}uD zUtAj9#~9s3NRO_k`(Z1E7m z^Y(_iN({yV{@;wBO3F#(e46yuMRJ4-U7n^;QGnhf*RZ{IH^|* zV4Fz*tETX9{jD${qsv{=@IksdY@q2|A+uu|30v6suVwWD}j3ItSTSJg7`Sr*8HR^;q!t`eU#fddt;?HYO8u`18(xxXuh92I6D298eo zedJWi%^DuL3kXe3gR^V@7{W`;fX}Ny>Ut{xS_&9*kXBJ#a8KMfb8DZUmY@ZGk06`{ z5cw7_Rn57!OH)@%FKDYXvzYH^?6Y+mmpLfaUk5rfQz)JE<7GdJNIqID--qJr*I~Xa zTd_9=8dR|J@sB7xO5xUWtCw1~d5qR+pE|iE;0M1h+<48@rZ3@1NnWY-3AkZ%RVzjS zSK}Rx;8&1;{VECmdW|7;kInpBA@U2f&Hh*8dlyh*G2vKYNfZ(EkcKcyNqc;0yxQ8< zg8VS7G;jHFR&C$pcJJlJXn_tb^`BawmaE8FzZ>qWBN(4+Q+der)|DmofzZRn)IoDF(5z|1M?pdqlU5(UYf%>xS|!-X zb34^znaGPpvcW}|Ev2Xkb9RM0#-V6L(SlG*5v5B`^}@&RH$%=BBn|=>)4Xif5MXIT zenrF+6NoWNxML}yYAs(4!Z|c}Yh%!DrOKcQ)qiWR2hbChONwPEN91ii*h(N`0CURq zf(OBl&nU(is5E2~jQ1*438lOqwjdS6GapsL&^Qu_mg@7_^V3Ym;%T=hwk9G%ml6#y z@1|!8Gh!*dJtUTSZ%g{;isF1SN|o&D-C$>+E=1Z`<#xvQEEp%Np(AP$v(!%Le4y79 z979jd|M1`!t=Fqil%ApE5czy-1WNM<56@cbIo=0)<}g!d3quIvKT5bj1#m9>$o@k2 z)G{r%(pn#+W@jYSq|K4E*`hJ?q`xyWdgj~lk=o>>v5}VK`2w;N1qWsQqzqKAh0Gueeq%7~1 zJyc|b^E`L-x!o< zaB8_;UIEi(I%#MU5O+FE)`FdU?2gR*FZ*t(Xj$J<^8@5C$%=sC=sTT3O~#M{rVuIsWKs zDGfDN|KrCE0I2ffR;{b?^i(3dbwPy%hwlBA`d(b2_%#P`Zx&2w`Rf!sw zaJOfJR10WV0L2L;zw~vy- z9GGUb5-4*csn$27*5`IAn#kvZ&%bc&@Oq`b#davLORaXb*Q@hwwfmlB&Ch3ivQG~; zghUT`LURTk(o%1B8H^nJB*6NmyA`?q>Eh}P{8tkroqF7~$jF-%H~&R{S14en4O;hd zi$^_DT))j;^C_$NkOlT`gGk5cBf#k8RHn*`pKQyg+Si)r5pmj?D5C*ZwTsOn$U5n$!a8ClDJAG@T1DY{*S}0ljam!$Q?g!lggkU5{&=S-k? zuo*RMjU>1cke~-eypH>GG^k*qR8#J|f@o3dH_0Ki1Kx(DThcsuypEu!L7bzR=!P*< zUw(%k<%RfPbwArc@$mPZA&=l$!ZiUTqLCnVF?nR0V}HjA^pFt-$Rw@c+_K#yIbNXZ z1P`P1aqD?QTglIR=!Qn3YNefsm*%pP7C?Z zr>0!FL+I!WFp^lZ9!Cg+BIIm&dj!Cv#ev!Pi*sCs-{6U(z4ft%819)dgwS9qPztl-NAN_2lq`fz)gL33V*O7YFeHrt#q4a(312NQ z`O`Z2^}f+t0uL%zap7>3v3d+CLvfQaioM_HP*=ub&jrEu!O-*h;jIZMy)>B9CP2Oh zB={wOqKG*x?lw|bRxXxWXleK87tY|4p;gn2y-hc}(Hy+7rddFP!=CI)(pU;B)GLc) zo;r3WMvqxa7p<*ek{%<&Ygf?NNZzl!Sy~8J{Jlq`qCRP0;sy9|jqwE}{B3z-Be{Nb zSC`4Ca62vc#>NK$!|U}^vyO1fK@Mw5Woz>P6D2tQ(aOR4UwO9b=%;=Rx&4I}`c4D{ zrLT#K=y#5ga=p+L$Nl)QaBPWcD18ZwZa;9dK*QChhzXQ3gdm?hWMycKVPKeadv7+B zP!mMm?+pT)azy4JftV#;PD<8Ov2^+oQ-M> zca$OcMKPQSN1*Va3QLZD8(Ct~^TB~m9L@HT(R|PjcMBnqJLzuEak2`567=R)Qy9Xt zMh-R(XEd@=bJl#!7a6TNMOQILvr?KSWe)Pqc11j;!Y>%rS^KdQ{@$)@32Ct0&*-kO z_S{xC?TZ86XvI9UFby3V4^$a({2(4-Yo~h#rnF^#4)DA^0#1IkQ7gz-3$mXQFrroS z-Tgc3Agb`?$M6~We9GQ8tWe4ZuXKRmN{i%hsVP`E2-@1$aW)1rgfM4kjKltEx8yq8 zk!8mdq+GM`SDy;fOX4;(XgMQ=kgEG8FKBOBijr~s4nNnLU&$Z(a64;=X>RLgexea4 zn9V*ZJwQFPdT){D3S>qU-<&Ngi~Mv)`1lsjT`B}t z_NNoNF4-5^#+c;us3OX(317Jm*PtTLph-1oU0HaTc$nV7gHX1eaA1F4`Kg4w|ltsv*pbi!E-gAesqALGv3G#}RKm>-V+>Cqv0-B2y zOC)A?peO)*O3o1;>D6qjPyoE?Pz&FRzb>j34t19nAiUscp_l$%4SXYgzrz|w6Yd@( zMZ7Xdla|N`4Q8OkT`U;8k6o1_&nSi-vV74N^;ffykzd5od{gcQ!#s{9`f_ovw}qn} zxV)+6xu+hHTtYSpc?&e%q)N4<7INH$K50R5EE&R8m=oZZ!b~PeFGB!eiCy_gdXQcD z|1_`qzC;Gt>lGf+MViztxe;zR!^TPqW&51|H+>m|G{--+QOb5kw|xE9r8eVcS=%ia zXXz}cX62+ZfB7(AKY>@mxHC3lGc;n)186HHz=TSr$&k+EjRkg=jmicZiils;o*@g* zgpRO?Gl2|d${>WNgkcg?O(Z8wT7zh!U`T$V^_qZ9uSuzl|(#p-?kw+0^I(D9rN_N zTnjKo2_O0qNgt=JgO85`r0WwTo{QtQ|0zr&o4fHh)M^AxDWBUe7s1(Go zZBrfWzwT+cQ-qPs`$(DFrP1L~vh>)s15T=+H4{#?Nw$2X)wwhG+A--}vZXGic5WI5&6tL4W( zF9~qXEgDA3W;)h!zYKBi6<_tK7nSAG8}wh^Akf;lyr(UUl+MHCpQrHm1M>7kd@?N? z0C}24r%tPY{70|*cTGEmygyFn=;8%xMdG9Cw35Dr+~F^y4(j<=_2nlGGnZjIM1t>^ zdu~t6-gUmY>35`_&mQ_Jl7efId0cUiFSZ|wTG1aa19Px_uHwxc_xQKBA2vXc5M$b> z|EakDRdD}lh1~yvGmU>1+2vsToj_ zSGPfjnZkl|R~^KR!K|gM$BoD#LlYS+4^|_nq`ZAVS58RmH;^b%X(G@O;n59X#x*)*v{xDIi3TBYzsZe z`9&{#h4@h&`0|`rG~0!b*-*EV>dPG}ONdJS?e}D?SRdHK$jJcB3f_Q|@I} zOMolaZFdVpM!cz0D=2b;rxx~vo<;;8yx7%#WHhHLu)TUI=2J{2pkne$BvEB*m4DJG z&|B>AOVzt;FrlrQ)jagjH?9P&seblO74OuqUuA3)Ti4VhR+IL|9ex~bJvQVh+zqXF z8lrFl;_wM(W!{FOm7{v&ttgZ?_g4%3@Xn90Zx3sW3dO9xBr=F~9!N|N(@P#Vr_kb` z_0{R+o@nO3x)30+9Xg~=fG%rWwK;Dyg)-zylzBLXMEN@U<9O1S=bR{P<&e1>9(tgP ze50Xo-t+s{RB^ShHWLYs1qCE_yFpZU&20Aw3Vuz7^jkXDJW{yUBBE1DP}&XI%NA2V z1)7x&{{SD?-W7{7E&~5#7)Ul!t1_gE4ncH50>5a{(iQzfZL_lfYfJUt%KjgBEr1qs zNidNb(5D_YX9B}0`W}W1 zKX4(cwvE_gw|^`#RAjxl0T)#>Vh~Iawqnw>vp<3nU!`q5&^3Fi3v}+dRA%OeU_pu{ zI@P?JLv)FH*;U&b_sSpg*H<;*+DXhl>ijen|A7{_n5SKZ#1O=F=cB&0!Y zx`SCXW8eifR1;yEGQV#B4tCCV?sL)Svt=tdLe9CqoB+ZT0>2HWMrockUD}E9-g>-*w& zXp-M$yFzq&;f`oawP?K(Z7tIphP896wJ*o%X@2gy!88?_c~f+Eh7nqPe-tEJzrpll z7)pjsg$ZjEOK;AOZfl_753PvTn>)cCqJYCWcm}WSVvPCd7s+o|u@NdThflfaOYS}M zn+LWLN;AeHR=RjT*CwXm`Ejl!H>J}rYig8mYUzH*ZYix7l20u^JfaP29hReGH~g39aTPjTb7_)aJn65=)8 zPEOypS%?zPO!0B?lD65n^}4-yj?d*Q!-DY-0byhMx1EdiKXxunt-p3I-^VJVILwl2 ztT+-EB3m$Yz|396V6Klnm}t_GZjQD^Szj>9U6-3XpkTg|^ML?BKz5CV*!5)Q9{Cqa zBA^>HoZOq-`$z~N{*8nc?d`Amo5>WUl)Sa;kB`+j3QC{zzJ}SPzTksIM{NoO;NT6KX5+aG=n+Lc#E zaF*p*Lvotsr!?@Y)-R-u^bg$zlP^?!;Ek@esM8Q&%1qmI@$G9^>rPC2rsb~t25%tgxi4VXGu83dBb4B!a09-Bu;%lp;Au@#Mfs_R+J%P z7!DGwfRo19bDL#)yH#=e_^|>7R>x)ECB)5bo99lq$GTHMD~da*$wGTm8BZ!Bng%Nv zlkWMZnlhz0aYP_Kvo88-f;Yd*pZ*jTS72>}xiR@9qTf>h*6Z~WMlp#cB8q9QG(=K( zp@U{dq^6^#@~E0_!6tWApNtJ6rK@9ff!bo$&{ft*_WOe5{$XklzrNhkNEIuM*O8-!!>k2P<14R8va zdy{8JGFRncn1vQmlX>HTe#m}6)$ z;woaAI)7G8gQLfkDeRv~FMX!5v*z?}KeTZ>6*J`u#dBLNQ%k-cbiYsU*&{8i&v!hD z92mpW=Jn#OwH74Zw$sQjTe5_w4FENnT`uN+Q_^apchJub1q?yvufs=;9lq$5s6QIi zTU-_9{r*s=DKPXy1B%CFa3JuJ?;FSUxqU5GXCaRFNFijCa4z`y3NZgyxJAMUkbpoP>}S;j{GR;rh6D?`{h=;1`A3! zga;|oXa@7@bbZ%z&t?8i29hNYCG~b>>KZzDtbs|b&-%-Kre$OS?Qku=oX=92{R^RR z5O@Fd@H?`%qITBDF&b+_S_01~*zTJ{-+dDh_YEp901fq@ItKgyy^mn${_mk>qWoVw zeDLKH2G2@&xl{uA=pyKOHAtU8%<4*DI#rne(uK8o_xl^wtOCBdbYNu&0jg4h)bvk1 z=>VG)1447785wKx#;z&S6qfFa8F~-HGQvAR2B?7H$j06J}2UM{9VM>udPB>^+nUrTB^@q<#7&m{{j1r1U z1@N3wyk9GvseOi97fpapK)=-ChP??c|FA?FashM5B<%{_O84s|sR%TK5asdG_pfJ6 zq&QD`!x^U$Ed=+st*Xv!rLBP#4(QXcNN!PaTVK+K`|YiE2{>cZ=z!c^@t}h_%GYdz zuXslC{vJ)t6fiVhh8s)G6UKp#`Y}c83k*8!b1j(KTZ+#RN|4W79s7`1qg0Pjk>6N~ zb=#^&gL%#uJixpkK1&F-IZdLJ9Ra?~(6_vPG8A$#Bre^1pNcB)fSMHH+|`zou64NV zCS_z9Aq6!WQXx$g7Fb1Ytw**N%(4_5Tg4&j#J=r|?GjmYx+weuT<)5OI;czrn7|H@U9E4{&Kf#d43c5|N~+0VU-y)>eO&Cmo`IE}wH zxUzVO`p&m+`xG1Nq1LtXwQv|aUB>1a)*SRc8YhNtSiG#8oUFXYe-V~Hc}>_<_*MAz z0WiY8-S&HM{UX8CaNi21oDWYEe-iNRLZgh`wax_iIyue`zYI@YdT= z|NF6$4p+$BrO_FcXvtC}HK*78%A-ax>7>>sNb$?AmRH1`X_|mJD?3ztkjQ9->E0)S zj)O7?SuO>7cbi2k`#qR5#w(B44XSP$@>@H|SNjMdRbGEAPC4FgwgWDs_a$Zlm$>hh zC${u(bp{hHrxtcvB?1b3>GB3f_F^r%xk?M*EZgP#{mOY3Tj4K)Wkw_(-#0Kb|+8Or|E=~EZA{>1fGY1O*SK#R<=!co2 zxkBZknbDHIb*4E))CSlQsWk-Z@%R3@ zjq_SC*;VwD!?4&mz_@_~0$I};flUpYD;cgx5X&_6DJUw7qGRshG=jQij^A@!o+4T* zwV!!)>O03x!S-3o+EB}CVQCJv4`_9?M?uZh*yi^;PaP&Q0ynkl1Rdveson9>{k6^n zqx&#*E#)JqqN72`*n%B13r6CeO?e!3t{8dK$nE81#*4>zFKRu2AVC7ZbL~Q^rRG&D zZw6J%bLmu9xf@qZ3K-P2=G~;*iVq*CmB)hll|qzGvY`2$t~6T;WnG^qdj{~+cuH7k zWva+M?(s>*c=?xE56CT^KD=1X3eVHpVTgVN2gI-Hmw$YL2((TAhc?Xqw++XW!6W}K z{?w-3D$!p6x_>L?VGlUdQnm|Y{6a~5sfE(U3Uw`BhS=vdV2s85+=q|B)z5sLMzW}S zsFyA8HJ`_Ie_;a<_RQai7Kk$e~7{9{pdWq5H`o8^E9DlQAO{lK$$cu@@z# zZHsYM9clC4Q6eTKG%0>kX(3~thZ3U8b(&|hzXC6l+{ zmAJ0rp)p;iq0I5p?s(B;kXbsQrQU^UIEX7C5*ErY5R_1e=SyO1KE42wzV~8=6^k!>zM7U`*tAH2IRhXm* zx!eC`@Eg;?moBZvW06#3U}SZ+vK4POWYf?Z_*FyQM6N$;yeF$ew67UuwIGP6fJaPr zaQ)kqlSXaV{%uIlx3w-o?)H;Usz@u%OeDiK$M0;7 zqcOD6o|>7dZw6r$U?SqcmYx?NMT49SOaKI%VLZoq%e2VIjokL9w>G+jv#OKb+Mb0) zwaPNXpQs-rpZi}TCSY|}$a1qL}WW!W>J%)+*spx=wCS~8NQ5I33EMgshqNbz!k{T?=# zG;pQn@O;=2pZlY!-Sb|T^V21)q}eMEjm12Rrb^xZ%33S+%0_`knnN4z=5z~hixtfl zNT-N?NTcljFXTU>xlT9KRZ=cu2H#f55x7;z2!0+b3$=bfD-}YglIvF)1b8jenkL}|4vce@C^ldev|fL(`SP-4oyKcSUNY~J^yF? zhn*GJ$~_VlzIYjpjy%P~sUx-}AM@K=I(H`wLT^tZYnI&rS++fiiK{n~+(<=OHIzbK zHB3&C{N4VH`ss>qLEJ+i_IGUiM46HO);EGuj~P+aZm-6}yRdq9Gz=DDAV0C~eI5I_ z({1e#XoTq2z>56w71QODX-!)$(UnlWLqoO9YD05v;C+;pE>iH1eAKX#A<|S)3Pgqh zx?|;}`kWg^<GJTn+wqr~)^F-Enh3S%QwsL7oSca8Ws~>JB z$87E~Xtpnv2xC|KgMNSxMSm~8YCUi>4(6_y+f`SPV~#6qPP~p;Bp_W7Mfic@qyBZA zy2qcwh7CYPz40KXjkVs_OoTJnF3T zeg%@0m>4WnRHFnLz6Ac1eqGWTvV2{rSInxvFG;k!faCdT4kP{rW8S(g`*r=KpmTq9>-*+o2_M9ls%cObrkG=-@(0FDGrDk@eE zdgxhewWJJ#j;^`8da@8r^v?GKp&h`3X~E?y9poyAd;f)dDj9XV7-B&g+AToOA+>W#y`2y(|8E_(^t2*hSqv%nC`ApDjLqnG|@)Dmd z54^YS3yRlDqb}TP+^$@4X66lQ^9_1k@V#6IfATggi?M7A zEzVhm5jhTAYbHrZjISRp-#+R`ei2?orlZ;rkQ^%<3NZ1%-+h2O`p;wjyEIAvw_|~W z^B)`w|8rHO_TN+dKPgiEe@F=@97Vfjp!;j1>O+2rE;w+;-YB6aV|-*eoSVcq>d^H2 z&$UMzA==oT;`w_NCPLp!N-X)BzbK}T zriP`n(~Om47+!uU+KT7zWj$ssEt_reftg)2YkH?&Wnb6)UL@R?qUD{JX%|J3bjj!K zVW&3ahunjQoi!HZFx&9Sx%P?qPGFo3n*EP zB;b|3T(9D=qwuv-qb;fU@dXf%X({dGy#2%PH!7?k+`cDN7J~%YYKK+Nw8yyy`a}pn z7Dr2N_0nb}a)V`~=bgskmq4XG_sL^l)c$L8iEngL&zGGnA1cYoA86ELh(l(fHJOL) z_AiHm<93JDaS;E|3^_Ue9;b8t=gq~rrmS7M5b`GjvV0ECb8Nl2a7G*;nf@eSo&${W$%ggH54Nhe55oAe!K!0$~C=lqk zk9?%fLF5C<>>z{^aCCG!BU_=WAOYPA2tYrgVVfO2p7SUP*^cn{H${f`)dh@`yq z(QtBz+48M~IU|JvIR2rW0BvA|ND#gsKP-PTmE-0>>aQ`b%(&bcvrW+3^!hd?l>rzS zjL`2c@wITQ-@iu64LL>C@n!b5A+f8RCJ$W}idE%68lszf@7fsQ9%^ht>GLf8aednr z88--OTv*f3@gTyXnWI0x1>cNW(l+GY!+}^-|8XjQ-8iQ~vPV*y18t1yiWKxRo#UgiyP8=5Kj(HBIS$o8(NaIvV27cZ;ao1lI}N685a!7O@Zp>3dXm8{bB~_g_srEE$YW z?Q_~jYe+^SeDGQ|`)q)&e7ip53vfHd7kNcC)KNNkvj~XO%av;g3ab~l!I6Z7i`?D< z+vjUM1cU+7t$WxyqHX-hY+h~vD96qVLp1Qk@eBg{c?k8&%fjzV|3laD4*r5_6?>^Xeb?^R!!F{onc#VlF=#CyU#^6qXQQaD)#>%;p4gqg(0 zP+W;5up4zTpN74EeDeS6o^2aWDT@=PXm35`&77FPSF?f0>65wpM+ddF@c|_o_9OAx z-R*(8s~iK;SQHd<5_9Y|r%WziFWpauSPk@*RWFt~r(P8={zfb;l^U7*L~(>+%#RM| zxbYNVuEjFoMrf&1!HM+l64vR56NIAJqey3F@$ZPKzWgg<-$ec!F_mJSnL{9KCvzR_ zk9}k|*vcr_#fT}7=g*9Nf=tC+|3+-*y1rpSWJu8nL!CoAM8cU8H+;R_d*?Cyyz#o) z44~WqJlyB~QyrECRJIq$xctl*QV8qGy4Ghv7t z=u$lh!J>IJ!z7{5q9anOSpL7DeX+FSjuP^?f_{PLiU~XP7>Nyhg&;Phd6qmCWNu-^W8%CvNL|mMl=N?Ryn&Tq^#NdXBgBFygflj3$B>`DBDc}LfCz-k{ z4{J?CN3#_C(wMdMXvK<>-DiT+r}beeJ$2;PcsRHTIei|Q^;AtBVM9n2s61%k;Zc6Z z%Fl@?T&)=ktFJM|*^Cw({0@19iyOS`e8XbH;ggNlbxi8yJu698b4;qhdCnq=^D-O{ zl>0?jc5R_m#tQziqj}7^?@}?rx+W6-JRTxWt)-iYU7NYM->%oAavLhw#h@|o$<3fK zE;7s3LBmLyXFlJ+Lz^(hlpdjMUEfmUoYk}I$fnN@u8`P?U|1idU)(tixIMx=p|du6 zKYtY5B6a(g6s7RJ3-zpp4=axLOfn9BA6_O&3UAt+@eNPjA9v^V`DhH@ZNG zoVeR>{Fe)qn?3^vh*$qt+mx0csQ~1$8mNOm1I(ON?zfjImk9??Zx`quG0LgplXM;nz%GPv^PjMa7}} z{IpY*x6{+)o2LUCT}JgyDe;``+h`P0S9$dfW1R^?|5iuE$Zqq4>Ej{Yf0~t{oTp$N9a#P`~CW2AIMs?C1K+U8wHAGTI z#>MB{(v6BcPxe%O9}W7Er?W~vPYKIV$FI^UYZ8`)fs!>DqqHz->t^_nr~1Sz3QrZ4ZhewV@P;~ z*K0cbs(RVFZpEH8oHbH|w!qZ>_Jn8q&*XfoOoBbAP>`**3!TGCA0+MhPC($kaCaer z!7Q;4v$y`|63FXCv%*Jb(HB|wg`oA}-HL(c5d|2~MZYtp-b^S4-0lqN^?6Qfi~LZ2 zxky8_kq;4O!}kR6{OJSlq&KlF<2e(a-_wrqD(1=m7_tr07Suv}BHmJY{8ire;KK&{ zvk-wB0fgK2W4P67Q<%sOp|)ayD^%MUsbTQy&*Q|JIsi{BXH7KcJKxvpk1r^s9jE{Q zw*u;4Z!EbulBa;+=pg@5hyH!>w#xzEFYi-d3IF-~@S`Btny`qK?sdyx z$%@yD-(9yOpxhBg9b>K#CpR|kOSKwp?miulXN?hNBd z6g@73AoIwZcCz`W*7!nd`Txh(JBCN1W^K5!ZQHi3j%{^pn_aPO+qTiMZQJg2oK8;m z%PfXdY7L2Y2QqqFmLH009iF6SV^$)-+k8N?zB6!8D=s;soA_Awu;ldvCH)LytVp zuK08E1k>gi6|Po8jD49-vzJ}xz1@Rq_<4BKv=6k5B>_{-7-CX2=~Zp7CR#0Bl>P!z zo@4fh{5!Wj#VgCNf#1DCcvDU7x69``!DyN)2)d7vxvF+D)Ftj4Jan{oh;sB-Qpee$ zBW}=2@(`l+PdHpwS(25e8i@rHC8Q?sAokgOo!Vfb4^&4lkH}!!Zv*i>GTO<&4q0A& z-Or-=xdDD!79@AFsKI|Md-_nEQJB^#=5W<3gxfRUPgo+#tSFZfpIXY`#i#73&e8+kc~96fH-L2g#HV@^Um8Uiq*llAXXESO=AucbpE)14lJP+@?7$Y#~D zq22-Da&W-eY#HjdoX3H@L1yBWpRM0-kth=;PAh9PN*kR(7T-7XTU7QQ_Q;ML^rMY2 zsFTd!j5|Zx=?zFMdjbJ=(6vP0`)Cw<3#_jV1#b*94oD$us1GsMZBlZiFh%Jz0r3Y> zS~gCbX1D!?(S61LSWszB!QId3RXFS(V=-nMqJQbM$7|A;$V34^g3uME$@%ezl?Y{7FRxQvUFGsno_oF9x$K(E65EAos?u=eUqx1-@64q6wxy1^6*sG4-SVvG(| zA}F&!6GtYrbl$-t3L{1m?I0NaY)=6l z0bYN1o^0jg-yvued$e<4``{PW@IiLc-z0QWKyN06F*&Dv=@^7acncYa-i6l1g zBqG_tbmSut!Qnp3&#QEGyeW29atR-y?Ddu&Oy}8=K{@BgmNr`Hz+~ieO8wRKq1ErY5h-O=hv8=>>L&#+9p0js>EGwhv?orUwQz zqcrLYfETT4+Z$vNH^^G5B{M0*M>foJ^tOKxj|4dwgQaCQ9HGGKq0{)X{Thh#ym@$3 zRuhV@z-z-WjnriKOHCg4lcS#x8JeUE=P&JG5Zeg z$s(=vfBv3ef2kG{&b(j*QN{y80f#gS@ft0lZHYW?TR_2*PiH2oi{aol#s9q3uE0<< zftt#8*k0Gq zQ!WFk+m6F2Q}ydML&@Ja%Yt5ygdKG1LPA)?j8ElE$KBnRX>1CRTYROd{YTV}WJpc1 zi(438p0skPeF`Ji0J4o-aE^(1%9I-BoZ?F`FLcGzTVsJ1>O%~i zPC@S;<2%#j>W0^@S0@BU(F@}2DU3uHy(GkZcq)36uwe zPjN&|PqA7|tWyZ=v0JLz9jV^QgWa5FCEteZ@=!t5Rp1IOUt{{lmAM8X{dnkcY3!z0 zIHs2SxO%ALv0h#M3j@z)fcKU!n0&6)axZ{detsmN)i)N9``I{Qz;xPa^SV3KB}0Ry zo4rACCgLTLSHyGvlP;f)llx!(x-B!G*ZWUa=xw5 zCqu^qq4+kXmt`lW$C7BtEQ3xI^!-lO$MrFeCgQv>C&}ab#>U>44QF#@xmbd*VEM&Go*R6RvA(G_4VVT6JvYgGFm_6W0>PRVv3v5`b&4m2<2! zE9nCLfXD_BA^?_+BG9Q>$1sW*HKM^^r^Y}h9_+=W_Shp$k25v_j80zwJZsRXTlXG1 z%^XF=mJn6ZiE%fN06fr38SGq6Ezl*fvy8Si8~{!1bhxzjOQ40t>kLy*Y1^yT)wES} zG|U zAwc`87ijLAAKm~n0$Xq8#0qI-eBB$a2Z2d|22M?gb8WiO?}OSLZ>(vz)0bRzfNbybMs`qEDJ&r${j00irt;LC^fiv9zWbAV<3@e$=h!H(=~qr8i&xHoA3-BYYO(;gb5YY5Hm80$7H-Ccz4Ld{zjsleM=gU zxZV3USxn!r`SBAQ$3JRhKS|Ky_v5OGg{4S$6O6`*XaReWFZ!*S)d(VeMg@S2eWpb{ z-1o_AhGss{CvsL3O%$x#!AFo3m?&_xDNBFlh@=Uw>`rmekqCzr#2G97*x&*dNF%mn zg2JP#szg!j9D^8L<^j2Ya^?6VX)(!kY;yHsn33gDyw1y(DSlx{;WH64bYOqoij$mLClHv|QE`MUU5}PlMR0ob7 z3%u`KPDN0wy?2tfBMcCWqs1*Ly3!pxj)OeD6|jfAa^KQ$Rce>}01Dj9Lr53GE=Jgr z+s;7gliWh+hV-Cnn!sPl*lJ-o?-Q@wG`f7#20ZmlGQuVAly1!Lw52xlYK$O{V3jEA z&6kWl1=_wdtZz<|)wd}quX5AptHbmu6v|y~b)_uPVn%@s>iaqE&+ryXJCo!p86AOT zSscyGN5oS2yvn0mJ{#XW$qUX?GXjdKTT>@~kc{g!c^bWZ&O8&2sA-P=H%#FEE4kui zW=%|n76c^s#eCJRLN{;dDQ#RR%q^&t)97Hdj)lL6ulwY(mJ90LvwznSePlakwX79j zteTQDq+`zv=6JF?QfZS$)YP7an-F1-dTZuW(Q?J73w0d^-GO*UuU9JccKtSyA}a_A zyf)l3pkwgdW~klv86`8~9{UwgXc|d|%=S%JQ5nG8WHXETK21)|SSPc)hiklKkR>-C zm8`i&L51>!*s|?mKGk~Kt@ooVX;$j|Ce@FI$S74I)*@+I$t+wNw=EU zB}~%%;!J8Xmr_&;7&pO((5nt(r7_Vc1221&+M@6%>3*izW z0Cr85DamyJ!ix>38us=*XrNgs=LZC&x_4Z6&yFQ87W-TbTz;BgB$;K7bJynS0X9q4 z9b6Phxes}gc|{Y7;S%(-`C3ll7UT<&Hd&Tr*8 zpI?$LjxdjetMF41>rorS3Q}vSH(PuU_n0)g;>BN;oA0aOmD5M8=A96)?xQ%F;CIdU zkK9+(QLizJ|Hjq-C#<->ZdK5L|4uh4GJlChM{eGzeZb1w9d!^1RUh&ty-y!^ zfwUC0B>hd6?01+U`j5#w!WVOFh6&y{KhuG++9{*DdJVlvN8%5jVdY1cw2vYIo|)uRt$PmeFlyjilCxNR}f;s0XQ~Dj=G;j zf316L-Iif!pNz|hsr-K+|U@P7hFWm;JS=~r4>dW)D z=fx}V#r*Jpc(Vyom)HLB3J9P^+LW?kaabNy>$S*r-B^BcT6m0g%;%I_@8a>gJJakm zAi)9G%+q5B(gG5RWJDJ~5J^hxz zdy}O;afB|Gm3M3X*}0o8L};$SEn>05M%~hu2Lpb$X{l}k#?<|yKoV9oDv4rkJ~l*m zD^twX`zFnVbU=AT5HR=ivm~!X3!kL7!=!Nt*gTVyo8dtkz8`H!k;=6qahs8mvY}6Q zO+RZK*Tv!-$W~*_y33c%lR4oY>RujU?IODb78V(oT5?WNSAmmeu^{zIa+*TwE;E_B4{uBx}xvK)b z8~9)r5(dI5LQWveV}APvu6K8%XG02_Kw#l|w4II~FTYpEHim|d(CLBXiR4&7FlZHj zqLF`LF`=fJHC1=5f4AE{)0MP^q1mrS9maPoHQGla#s+8kdAHV?aG@{}1xJEINfDE9 zQ)Pyt2E}d3+`l+zaqJo(BR(F4Pu=(N<+g{m5xDRPZ8@f{t4yFd=~h?u&*Es_qD?!W zuXR4k%86(%FXU6U5I~19lS!Tb>9J{Rs#3uNI9j%$I9=_edB<5;2iFDEbsRSfD*DiC zzpVGB!Mi&$)-sV4a%n*zyREqjx3Hd2U8g}G8>_N~Licq$YBgpb^HX%IbX9^-f(1ht zV+aIW`psyypXF0c)AVlFb;d!32hIIxbWOh$ZAiHJbC<24SZ9iF1XB*5NWS8LK?xlW zz`*_3J@6xnB}(^(8o+JpUAzI~EP1He15UT>9&*Ci071myr7mI$SlXwo-PIgzWM`$1 zXj|b3Hj&rYL`#42K5#sXwjUsPXV!QRo#pQpd0o`j+JPK4WwQ2IsIR^5mfCVVOPyR* zhM^myanM*P)qMOFqDY?T%iORn2NCBDAcrGNxRMS`M=Gwc;eHs`57o>6w!{bDeU;a0 zJ(LkrsUd9aJ<~E#kgQE7P9Jq}{sV7qu`+sf*+;YOHGY&q9@%l}eq357+iS5j zTXjgYf=}|{8_hZA$mITH9uXGBDHEj~l3WspLR2JMp!g3b-6Y#av;@RMJdVsv0F3Q7 z8LFA3Q3T$OdtqIq6L2zad~24dR!j<6!WLFrryR$F3qx&3_(zXmQ; zL#_0ASiJ>vrnBNYpU|y|wR|@-rS?9u=L+^!IJ&mxS}}7LKc81Zr-?<*a^LP>Qth}u zV@-Q~q0DNES$6aWqRron|A06WH~oXgm6PQ!{Wcm1(|>yJD^20Qdhec_zvSB%5%u`I zj$o)LC`4Ss<%9W`yUk}Q^TU1A$wK?R_crFX2{F}M5Hw|>9FO<+-i*1uht9IIW z%nDdI@__ik0Ea=aK5X#;XdLhx(G)Nm6H9|dpPZRULUB)Q3Fc&B?0KTv)R#f`FW;>U zi<$u_oQ{ft`@PN$nv1Q0N@4@E`hqYi9TX#70|^y2eH)3tl&!JgM5OcWD3D6G?Z{y3|0Ezx*J(R~HmUE|k5y%F8)qL+f+9MUOM&HPG z3DbyV?c?j@CQqivd8K(zsu&P2wAn&G0&o`{A;Sv=(@+>lhP=Go!cR3JoyhnPQIOZO z^Ulv(+J|}0ikPh9?Y1r}9C5AVVa}5e6(tSE{xIj;x#j5q?P?tJ?(r$L8VWgkz&Q}G zYaokQ;?7}mWCAS!WY;PE0nZzU2=u-<#;k+tVjJWpLc_&(=TgFRR;=^7B(b53iRsRk~Ahfi$t_@?2~3{ zV*iy_*NjMKVY1m09;A2xtI#H}I$|eo>hgf@4%EOScEljHcNb`qXvD9#LYzF9OAPv2 z6ovX0!_a}8@9t<+WdHZg`g*ch{|UeG_o2>|noQ(Z2lsXH2Ib|LqP;wjaAcjYbDBRp zG%MPQwxUFUv{AmVD82T%tt26DbHnjwfH>91iZgYPX%7(`k8XH45G6+jIVSu821}|8 zYgI%-f|>*?mqk9YcH%9}n6ii@0e*@AS2{3NoasJ9*Paq`n>4Npt4tT3A5r`T+5sd8yh*6+(L!1`pd(r8=j zYx64P;!W7!J&$D#5b(3~Y}a>90GSp1Y}u}FNQ+Hd>mAM(%b_3#K)lB&28U3ER++vh z2x?Cle>m~SU$LHW5z&b*E}@i-0mKQ@DeM{RM|JV2Y2Y-1$3p5Rl`SpZnZ8hpdi}#~ zmJ*ReB5FUXqz51@(;(#D%CqH}S`IS?1aLB}vtPNXDp<>gEo;!!y&Ij605D$B$%;xk zH(d!Jmi#N*n|S9p`K3A#un2@P#RDAAEKZ6LJikKWWZt2f#LxY)-|_Ne=n#Lxd$Ju% z)gU1R6IyAEK@F2rP^9V4ws=@=ye4nR=kz`3^(qB|xfeda%}5SgluTW8lYFYA0D7O= zZ*liS^BieJ2c`}_u>in#n5*6!{}eL+257Kz{EZn<7yc6BV0}?qeC8QiXjp$@+1PFw zelf^HEhBv<3P$C!CZ+Mbi>?s<+=E_{1yR?0@y-!AZv8!DM?e8s>VA86LD>mq31z=h zBMhLp#T~JL(b15{g^%m-d_OM=`^Y1JNh{8k!4aYzHD*R!em^>6ff`boWQlYHVTq>! zM+2Cop^9MBJa&A>yC+Ng6W7cV?ca&W>Yw7SeDQ!vcpP|dWO79b1TYA1HlC(;cHI?ryLeZ?se>MqEd&iG>& zYNfH%r-r8JtgLvwZlBtF)YaUO$A-X5D!;i1Lz``O=1L;>@OZ*lg;b$S2l66#@7bgc2GC_~ z$Z$B?mGi#c>GvIyM8X%@-cEP4@Q4A@x93^(XnJrpP@-B3QBICYIBFKvp1xwldn5`p zY#dmuGns8+yC()yCH2WoDY@~=*fUqg)}|Xb)jRL)v{@Qs!a9r386X#N){B(nNvPjF zw^HZ)dBIi(N6YZu_q+6DyN8CANQ%rYX6(|Pd_`2l?yBmQP>vomM-JI2IfrcD3%mdG z%ShUVA%7j2$9FHf&duQ6N#q&QH<9D!9|9{iJ^>63l#Tm8XM$5|U!LniNZoH5QH!v6 zX_E|>AQ4_?-)J7xBXzX3bfWOlK(8}$D&C*28?d0H@Jb1l5gxDME;#MCaT&pMh@PAB z*2X8tCtn9lphU))p}}v{NDok4fHQ|uAq8I#&{2sF&1&V0pi)Y6x+X9%H4H)M`k4~& z8sN#OsJ={LRR9ocI5pi*a%$|&gh6z76~AUHbaF@Z9dCP0a~0IG@7~g|`Ku7%Jd7rQ zPe4?1MD(K-!l1$MR;ed_PVD=m)I$XH|YaO8u zG$1`0SuGK3K>LCRqK4kfXbiNIx)iv02r4;@J7REPzG&L9ftah0?#Vp7He_=gf* zv(mK-(HW$%5Kf`vxc?B?aCrzLCrR_?haCAa?`?S`)9dU#Lta2|oHL^ch$6!n6a~m4 z;VCc1&i#B*O43VJITN$2i2jL5SL%#D)InlC%|l%})X*gSvW!)q zk&fb7^-HM$WB89|zFILxpY~Ic>h`3T=w2|yusLo4N4zZ_ML7MZP+RKA7%(71uQn`x zw%*JUa=N7iN z1|ICZA}U)tMxpzekV9{J`%;zvuUSWQR%77q^?JrzHBLo7y3yZGqL5 zSfZUeJ@?=8y_}ywz;xRGnn?Zs>5ZK1@knSOEPrDH{uQ`rT_<0(MiNkDQRSi(V;DAY z7?)swf_Vb0@Rnfd0@^vPkDgc(a+#LQB5|cdBfHqxfppI#Xl>Gf?VR|)6cL&HKhUgT z;>-hgnNU-9dD27lUF7hBP*%IlMcd0;OI4|hQ7KJ zz+dgj1c)9aVUt!6vK*+ET1z5Wdrd1Zsa$#HG$XwxO4B%eN5i)Ixes9{l%ZOBgH|VZ zqh&H($I#PJ=ZFEJ?(Gx`RI`}!pmHQ8Gln5Se%ChRzc3?XC0kt*#$sIQh=@ixv8K6G z#L!QQc%@%)9H)t(L8{x&T%2zF0BE3OEg;4IV=zMsM`v%D7i}dAy(#z{$ETrp($+Lk z;*jk_n)$%>bwr4Q;nMJUW@p;0vej<-XFmdkLtIi(l9a9uQe)yxTBkZ-GkE3(wU|qe zWOWfz2dva-9?qN(yMTxsh$}t%AnA@6fmefXW0Oi2TwVwo#~4`sI&pSE3oz?S1qCg@ zW2AQfu^_|-r{k-GF@7;yuCp{guH<2!TKvcJbTeu$$9YRf=cxkSlr%ac;;{I(Z)>=6 zV5z@M#y;eJ6^aO@?%7r+b()PmuG!7t-)=Fn)j*aXMtZB_=+s)~RyZF<0Su7AW^ zXr^D>rAwFSywo>B-tGief z&OfrX9M^rVSk`&t`9LHeasFPAayipv5^L42VIBe*3_V?7@d2W{ZdE;f;JdxPRI6}! zzC%dHTeuGV3}jV70c+>>ff4+Ru(9M;!T%AL;}5W{qxshUs};ok^ZgL@anMKS%!flkaeNA#gSaCv*!`@ zAlO~dj1r@Gge2fg9Gesb_YhJRyQ6{dbl305kFl#&{4Kb)oN_#WXgC}=(!w$>S4p6C z)h8pFH1J0T7XzY$@{jjRI4?p3=Iya7Tg0YC9G1c4k_awv^X~<1t5u3WsZxbkbB4jS zz1!$Fx9-|`)2A#$A~yDoz>>rKeN6mcKLbFoEd7kmZ6dcNzbeZ4Y_v~VD?RN*w@-_$?4p>N#%x)HdOfP2ST}V{3p$JS z$yu_=6CVKa6|RS^U-{dNgl#dS1vE8$jBfm%Y{u^DY;Lh75wV!qNUgsaoV}~dXm%+g zsE5%-iR=zE>?a4gCr5dq5;AwqA&SP3m;%@!Ydu_+dUJSFkCT5W$7e>F&Zzp6A_+Y^ zZ*x}{2BRO$cnE~!4J{54CxH5a4_vZ?7RR@GmXxJRNnfSwgq~3pqvdRO6M+I z!I1Bl5_E*zWIb)G@X%fj&Ve61o(DU)-#$%PF*4MlWh+h2L|w)ka^dAEJ;jEOS-LwI z`Ja1!rXGEc$BK|(?Q|rB3>#aJJu&Q#VKclT46N-&=?J%GKqpLAK8I7JXUFGsejnYL zDjilcji6$9(m>!71j%K}C%AsNOOk(i0%bjXZLG+Z=t1s;cZn5L`UFyb)cXhQ1}Dcq zfM2-(d-0*MVfVG{_Px=l8jPgBb#wQH*MJU>j6*7p7!=)rJg`YX?*ZGeghBtDt}?$` zm$#78CL_?ki7_{y&cfcHjG}`gy@^>p9p4+@d&Q@VYV*&Fzd%KX85b9lmOB2H+lqki zQ;ck=`m+N}w3WO2YWm`A?oNl3WV5ga6N)7}5fJt|N!oAj(m&=RFlfB=U)1xOheVbyURPud^Ru#bSYqY*O z7sP#UCzB83EeRDVl?*0}urJN!MwCq*LV#5C6pYex*(C*3e!CNDziq|ZE5p`63WDAv zrk}{!VE(0@`wK)ta9{a~kd;q6`Hm#gb_BCW&4jl5mWc3J?$kn4$e;#ROjy z;$Wc6J}et5p)BEKC&l=JF-D&y%+hqB9D0@H`5@lUnC@S!~{2pgS z-JQ2H`GlC-FY1U8Ia-_D0h`}Y5~@~O9B-*7PyA=i>A0!P-Jmp zG?5qCJo!ulv`Hw%bNpIal=ER3sGXgl(*2U<()!~KbM@MU)%WM&eGv&Q)d29pNIJEP zl|}T74zNSJC3R;j$PW2B%TOb|gJWe1_5dY8WBOi+a|VCTE+rS^U@nLS(ss~oPs|rT zmO}4yQyYgWTFcQ-lhX;ooYi=1rKX&k`xZsEFE9p=M#^k&3aOkM$e-%`64&s%pdZcl z*284eXS-7C`M*GEJbpWne2N1iew}=`LCr_^{{TnXJ=o;T+WwPy!}*i@FP8TIy{6>k z{5P_}3WksGzX}wrNc`!A`(6HXh+AZ!exmO(Y527!k$I`)r8@&U9GOrtEZ zC_d>&`N<2ac)1%;hqE0&<9NbOqd^}btOJ@tcwIL*6B+2mo2>wB<-{|Lh`dw6-7_`H z7d9c6H90P0$yT8g7kECe^@+C0IE0yoOF5@+E{>1)k0*DaD$C0*GhUc4V<1T{)dlM!2KfGgX~P`vzg! zd2ST{O>nC^45tgL%U64ZRY`2zr%Oz{?FrU_wxB1uM7{Rl<{oh-f4lPL3h&v4&xu4D zG8JmtBB8j*&K+YI%anw+qFl4p^z7c4eMpgrx zFhHBpSm{~`#Dsh^QB5Md6fU}~R|3VFTlY!LmG9zh6k}GC2$yx&uaJwrZ09}Iiy-FFQII6 zv;BdKb%e_8zLo&*%xcnFKuS$KUts~vAV41vu6+lEs#$tZlxCi8@+C!X?|AKiZOacN zNi@ypi6oic5&-=mztqJqnaE-;2EfF6#=+lW((kPnns`^7$6D(*?aS&!W}4${lQG3Z9p`& z&5nEbWORa1&)0`P+njV?*YU|&%3E*G@%k0+^=gW9GShfUudMk1?qk#NVrze zO&n-KXhMJ!Jj7Pf=rVjBhImj$Trd-^&&K+qrx=HUSbLVEIJcwwpI>vhUEOoGL~v)5 zMY~f!B>)g+Lhw;|Ao@V0M-|(!}y=(HY~5-@lzHSD2Mc(~~DA z&gL?3csI@>F4jMpY827St`N-RkKzMcMI!kdK_ZJ7W~lZWL?M@i94!u6f*iqAm#k=8 zBOpSME+9i7vmdTooo_Guj0rVs7kOqtFMB%SF91wPrg%it(uD8=Tt?!;V^XZsbLaDm zMIVyiY%HvV`z2Y+h{M7c&Q33qrIR|tpVqp2UVr=gXchhOaNb<4TkV47!)(UO1Vbr8 z6x^?ar0e~Y2D<0)`m;YeS(8gA%$AGM+$V82e96NK#Jh3)-E+UPy^l=`+Mk+M@cw`+ zsu=LP(m&I`OMt5h(w3CvfT#NnN6HdYd~Y&gIwwY=Ba{r1SwGg5k6%?6%>^p4?>LdM zs>IIn>5rv9v+#@wK@DZYoootX_^y}(6JJkIhAB}vzIfmBW*wnH4mFN2*P`P9^f%Y{ zy;&=C(<>h0Mz#xtaO)_N3=x;sScKj_sCob{UG{zxk%;Of@l|bZqN>OG)ULYYrPg*y zI@Lf8{_meaJI%1xFY5%14C@S)>fT)o2)<8iDBwnSekasTZ6%~;Sv-TzPV`&a4*X8O0VU`yj)7c^h%9?f&` zrlIC&cnM!T?Pl5s#d0M(OSCyUGD)tYARE%^*B#zrGT?bL)C9d^e!r0crr4Q-`p`UT zlGB!N+N8np{m-CWVC@70^XOqOa4N_7d?JoF7uyQi-~QU8p5kF`NPW(xJ_T-9D%6N2 z8K-)~!5fj)69Mp;g&lK$~p(Xw2nXB2mhhka%*7*RaH3s1#osa(T3MX)gAjg#_+x z>;Qhnuonpz-iw`OI&9ADj9l_MG`}RSTfQ29S8j?$Q2?9Y+!K5gBZeoJTK*LjMU^Z) zTVFMo9=&@CuEjN5J=J32!omHpXoy(aHuFWf8~Wggj9 z3^PA&)8Q)fNdZ-8hc5XrlG$A&ii*4z75rq5Jq}s2`vPYm3(fudIfsNyJlh;)2hJ<| zhaUbJW73olo(wnJB%h`l*}ix($sdzK&^8AyYWg=#-6yBbQd*6&oT$3pP{g)XJu9 zM_VV+qMZ%J!Fn0y7uQ0B(~@#r0Enn*S?EJcXYgGjF)r2Le9-5aiq}U(jE))c8jb8bJ3K@%P}1`1_@Oo5v)i)k-*` z9RcCz5Qi<2YyIII=$B%V=~shZ>=Ehq=^c8gm`YxYo99~~h7h{rjvv)x@|R{F+NsiAL$tD<66`q6I(CwVfR>ib47)5=iAu1!uK}Kc?B1n`MaP7&(1Udn z2KUg=yWfH%iwkBTowEIye6$#R?c&T*hZiGPB;e`%Zbj9q^v;-OGLw(%5$#lK>Lf>q zqwyHle;%Se^xK>!Vx( z1q>xFZZft_5O~8ObbSh3Xm=**7A?zO^Dfz&SkUdKuV_GbZMtk|)NiOC!YmOleH)V9i^rX)r34<^0F256n>hk3rNbdVe zb|yVb`G;<8mEYk6-h`WVvfzqi9Mel!zmz2+&A^;%AzYWP<7u0=pJ}g51zOVGHv%G& z6@T|zGNgGZ75PzNPT$FI285_>vS-n-T?wm*2^3Q3i(BQ^{76_XmJ)ATBA0^1OA`35 zIpaArMQcb*z2)}ZDiJzroL$1OdUj4^6|KY(siKr&e>+T|M&83Xs6H!_03ZYRVL1ReszP|f40Uw$XeRC&p1mX>kdR&68@;b##r zN69z3yyo$S@Z3AM)el_5UUE1(yrui_cWOG@KZq_PMri1K2~k1B@UB}s#ybG=Oe9}k zIvh-sjCkK5b&5RT&HQ8B3TYP54$|4Rz3QR8kOJYOCpP0+vf39#b+xLFs)RtfV8)8# zIP9HKFI|1RKbUJbXo>lf^~@xO!k(~3XJ+Y?MQTp1_%JI z)iLWDn^LZ;`s&$w_smyNx}pID-*bN8)JqlGtd(xpMrhef#KU{rW&Qjnf|<%Meijh% zTHoX8VO-BR)#Cb)peV2}&78(U^W=_E%fYtMkesD;0#n?ZCFMlrrdh8{Gp^uz)soQZ ziuq|?^GG+M^?=1IVLc}Cyz5CvwP?2TNY68AH}v-O5bMXq?ZyF(-!umBv0_Cu_%f^z zK?-``#4eN&o*(V?Lu~AYQp_Gx%oiKFsB$me*m1irqinPy7H@UZ!Uf(+y9M;S6R%B} z1fFE#{*a8ZROYAykfs;>?zf5;@p}ten82lXmCsPhb*w6j(KD+J>DaUe?;Z0g)}GhBDLSlgY~ot^7-dAU3hEJUP-M$I2j%{>KU z%*GwN1eVM42%6)NY;4q8oxT4Yv(PuuTy#<6>n`do>!s)hc&gVJ>Nwvun$&z`E>a5o z_#-$M`miJj&DEInPbv66N`b_5Iyf+94koUD!6}x$lJOUu;xGLAJf>CyRRW6ZpgfRF zbtD>8LW!3%d?1F#7a0sDwJG5XIl%RJ({;vf-Y$ty}~I{fQ$C0lgoooytIw!P?1E$&OTBO^H3>fyH&_e=xNWa4Vo zfn|hEcz(D0nBd%>HwERYF&sF;(C=lt4Cf}`lE5VR(=*8W9TLyr_mz5!nG^C7(PK6U z$sVdndmXa9*82x(&h^%+I-x2rHUcD7vNggtss*4=l3x9rE^D0{@o|W5jQ+NmPExpQ z&-&O%;R^~m0!_R_-Rlu4oH4ZqGHEFdQ!4S1rO;@QaFQ>OcF=E8B*uO`*I*M30BjC0 zjUXJswt9GI^q_FRKbK58X0}jw7+;ZoFMkY)Bni>OzJYUEtt~u7btTz)+J%J3SWUk- z5K{w@5qL8{3+4F1Xq`x>{@^pj9Rn2V%)3mx2&puz$!~^}LEZF#n1MAtr9duHYgqHa zrJG$82erFl8I4HTcHOG(CcR4mz|J;s^%l{2t`VZ{lv|T8IEKw3t*vC0KpF9Ia4z-f zyYqE1St?V&Z3(E+>_#>#Gf-XUM@N-MUXFP6&K7LM;*lYX|)wd5OFE*byAFer#X#q4~~p#pf=pv zwskZ5#SrY)q^0N%5nYUf;i&}O1J*9G8u#pH(AS;@<=1I>jEfitd{So^H*2sWwM^TY z0hv%PMm0&Q;GNiUv`icj+L zT0aq}Sjw12NJ*ccl@(`+*6MX~l_P?sr#mFw=+dcsC8~&*PcKiSlf zxcu_ueb!VbiaOL<6JYfEbcw3e*=`NC1*#O%x^)I!zz5ACjAlCHfNGK9M_V8Fa_~C& zlNP4i^PIf?7lm|UF7MM#Tn)BGAG^#}c;Z?9;g zAK$Lc2lm8R@r8O#1K#J~I4y54P0W4q-1*?qpBc7iq+l*&yF= z{dcg)!+l5@Js3ErBG#6CX_ER}Qm1&$+s9ez&Bu=m79`w-&32uSxN zxO~SXk1F6UGA>}>xVaJRv4*@F$x&CWswhk;m)b_YnNY^E0JO$->^NIRou|iGR~xTo zO8%Oc<5?7^cqjYiTkZkVRzXZ%0khY=35`h)Q@O{@Ly)6bA=v;g`du5tSiS_4+1CL? z+ShHpd#}@b`}ED50PeJff7#$mJ4CE%pT0bYa=Qq1RE|8Z0zE`dg&^DK>n)gsz>J}P z>#~y5uW$wr4=5eOuONwen$m|;CP0|snCvslZ*?Q6z=w+!pKKdW zDdxTp@#~;J5M5K8M05QIm%p)Pwi~6^NOl)FR@KA1MOud3K zHr8KGY}oQr$)uMo3{YYY1m24LmNi^!96&#$ax$u5T2!;%G^uJFJ#}~eDa|{d@mzjwP zhEc-O$=QX7i|gy--=w6k>2NCYI2iSpMW)@?W8}M{acTS}a&78n-pT}Ws9Xx3v0R6m zSG^0VWr&~OKql$3`}@M%Oo}@q9jJhQO+FKnKGd6=*ZKG|0S&uALpykMTr<>Im4Ydz zsEV0}tS~E!vXm0xNuFZkNKxmfN@uYw!?18Qh+g*0Hvk$IFB`& z4m{~-Ssop54)ag18v~-BKMj9kaLvav|8ye4Qd%qWKWK7V2Zj_Z#pX&$X7w{aNothJ z=yC!pT?NO|m(V)uY60CF0vCb*GR(rxhUP+!X#|!3{r>=QK#sq_A>bsC%j6XqxXBb? z1o%T%kp*oG2m+#F{D>HAc%#T8TqcGQfQ*nxlEmPMqTqPoM8WY^QU@H&=e6Kq07j#8 zu*zG(f#2Rr{t&+>B!odIc#_TxgE2ffg{cru08S82ogiW77vp|A|8rhB@|9xY5^$HHDDq_yV2&F0K@BnQPIIAD+eSDha}zb zBQO&+Y7$G0hv>0-A_gZBOay~401ST_B??lL#7KjKgF*TF!=h5<(dfJyl+Wk0x|-E1 zEV+P%cswYNtJVB+Ij&ZD73Qi})!WJF+5AckH7Z=NMRZxVg|Xpu0L0~Jwo(TNsytEU zv3l}k0Qe5o_S%aeLkSIXpyL3xPKv(JZEDG*lfc8El}cG5!_SZs|APpE}D zG_srcOimN)ay~w(YBj{HFAi1ty1J^>bsV3kJJ}B1N#C9B{I@Z&JKT ztDHF{Tle}5JFvYY<*`e7*b9H<+=FtycZJK2+S=ImQ;T-Uav@vSqYtlBgl#RX$6{Ol zQiAThCRn?h36iVy?h_=p1=}WQ+rIa{CT>k~?2;VzLNfOt`QBH-r^J4w{I;~3u6E5{ zv}<~3*VwO~)~;I3Ues!OsMYXexhs*)AT&C=HO028TzhxrvJE#WwmpB8Ywe?<`0jTe zcq-^p%zL3Y_MkXE9*VzoC$Ss)9d+n;b|3URw>|w^_Wf$^D_mFOZZ8^lJv8p(!(IS) zK`eFEOM6i-^-wQ8uzGj>=s{ie(q7a{J=9C_v5pkLo#=j9QLB*&_l`L0hN4f zBD-qky{MIYsFfd7t(y-YyLhOGU9Iw7w8}lS%IW^^cXxd&7Q5PwJJ4>7H;Iisv>WO1 zJV@+fgXq3~;vKwxT9aHpoz3Q$W|&`!ik~;!$?b#k*?jr7TIPRw^lAB1`D^(cCq|i5 z@obEcxbQkEOH{~vf5W=)LVciu;7fUOc~%3k{ADuxyL@_Zkm1Xx<9afml_%w!*-<>+E1;f{Z%(`q{TClC50<@uQ! zUVBl#sNPS;)$z+`rvf$fF(~=PWD4Jwo>Gw@xln}GP9}d8)Y`Z;h&V*2QRTERSb1b)BWqDQZnzO-`6@?l9Sp3dZj&(IxDt$_7>k%Kk13p6 z=R<0&m%e{i@|RQ5oJWxDn-SMN<>&G&WDPtbO+`~nN%}BJD2?5g913?j7&@zS-l25) z4hLm043CU90u<2<*GUUQjtcAr!|c2q;SS+wqZgf!&CZJLjgz46P&PPCDQ?+MNokO?~5c6Az`VJ)|^$I(EZ$=9<4;Ye{4y zTakP_W(6hJL@Zd__Sx*;l+HsCNya!fh1twnBfW3sw{qL^bnq`A`~}krWo~41baG{3 zZ3+ryZe(v_Y6>_rG?yWt0}~Q5FgG^}FHB`_XLM*XAUHWTHj@$46a_IbF*7%pA#MRE ze|H5`lv@`z-5@O~14u~@UDDm%;V=v^)W9$_Qqm} zUK}PSJt)Ev@*g=4lK}+o28B6`{-z-de}{k&cQQE;;!aT=<_u7AcLV_W06;-eppYmJ z4}h14N8~?>Ft{i{4&(u~2B>oZRA9~!HykEen2R?YYHNqMi}RmH04tac02C1s;`qxP zAngQ!L%|?tfI0|a2XVTK2nIOo7>aVlMCeJ#s!1he@d`% z06d`xJAe+v4FdOoSOb2h4A1~ML4Kvig~J5UvxB<*4d}vb5S}171aK#Cgn}W?Zg(c` z&ejk(;4V5qS6L09=>l>7TUqUI1rETk;sAhL!2i1YW&P6;)cG%C5Eu+|asfGeL!E5_ zHc&?hKvO}D3*m*}0DzpWe=33;f8Ahr_8<=s)DdKLr|=hb5I{j%8vwe?@K=6rU^vtT z;l||#b^Mtl_fI!>WtMlgmW4SvL7Wk8I6wKxLE#YaUERI8e{HUVGtAT3_dhEesI#@r z&or#vUAXm~p|0)_Ww~EEcOsmBWVR3lK!8VtS4dD00C5FCyufzcKLhA_f4e~b0)aop zcR4)ub%D76Z0^#4JcZgo?tXB5-9R1?00Qm~dFuN+@aGB#2n1L|!3cmA#1`s|^N;sC zF~sKY_1*r%p z!Up)$mj>*vbs+%Oe>&ZaM}P->_XYg_?)Se;{%>vm)64&})Bnv#!QIjEFP!ynh5x}p zPEbehUmAC<>yEe^19jNlFgX8P)e!Rc?5aboq3%xqMwJnuyK#_qf3|h}Zy}*>3Q#YI zwH6csw)?wd{+8?ioHa+NGeisK2K{-u062j>JpaP(1`O z3*febgTN3w1k}a)e{LomAvTDgI=^7Pf5Ct6=s!6BZ@9b5zl;U`0RLsm^UKr$g7|4B z1GW7x#`izaKbQdTADGoY9`9=OJA!ceEtbH4p}$!C5$u1l-=gvT3H}}q_$TyRJP~04 zw;c%K3AKj&BfDdFH)If7ILOf&>gM7I^8SaPAb{KD?lgA(e|I5xev@1MCI<@t3H*Zy z^8b$f!&dP35O+QCA36W;o_;Tx&@cJ_)C>4K@CPZ8e<8oI68Ho9jn*Gwe&_Xvv)_mb z-w}3mcXImcO8&i&f6N8g9e!tv`0MPt8=C*D|GJGJ5HAQAXKDfl77MYj3TZm4kf!$J zTpf5k%Cu^jf6B(`I|XlczaYR&Vk>_W{0)90o%Fe#aCTXq^;l|}?%H>|{yk=R!%OYP zOTR127dpeMjX0A-)$9cLF%a`wKvcMa6KJGJDqQ~p@k@6v>2?Z_cG4YpSW?z|&WR?40eGBvFbI0i8 zY+P;?B~4HCf)EbwZ&(D#HSx3nz{7r1t}>a=GD$l!FrLJI{4WMDtbIkXpxi^x%x&u9 z5?OU?e^pEx)G%f$pYMgMQvc^~dm+h>?OA#nSDoDx%3Yy&L4km1}C~qBtUiuWjdL)!@VQg;O?rw&4mho$hPq0xvxi}g8Gk~vw*XuI)*g=l6CZiEzj2;B}+YH;WuQ!n(dp; zZDk}RJSP<+2P$j_v^di7%`O2TnOsI}#5_f}X`dm?#c|n!b@#g%*@$QGqPXF;rqL`R ze|90cOQ7*^H1EBbBUyrhB0kn zJ4~3z&dICK8JVsq*wnPj%XrBnK3El4?ogPgI2&+`U7V88JZrW$9V1h!8ml%4RZp!|@2RJ_xGTNv`-S<^2r-Xm_7)|c`AAT$HQy6C z)fj%`!8v{Ck-NZlrKguG_lUMpS?y_Ntt(B%QsIo1*H?+t6IyqIr_J$Y%$uSEf7_28 z&Bq_?yc&jy*44ca-^!DJ8C?pUDcCiAE^U!KczuE&(PNszeQ#A>r(f*TfzD>ESRw&K zw;*fRyJ}TxNm=@S2{Btcww7@`MNs{BY!PSecXV?f#e=2pukDAWvy#w>Pa{XfrcwK` zhp{~!*Xr&IC*@^+S84qq+0_YrfAx)VS((7Ds&=DeRpA7pe90N1Ta8AWF@+aG=e)B1 z#m_e%Oc!C}?Gek*Q+Cf4xY~B&bi$HR?m- zzb2g#m%ODrbu9!=>T>l>f8?+~Z{qnB86duRT3}xL#UrC^!U@ylbT2>aGhSLb-R0wE zLjybGd(1jFzNPfM0W-5r8!X!za6#J7mCvnWvmEj)pHWP|8(zlRthgc1y#MhXRsZ5P z%SWjQN?h=3b{9`pU0$7f5vwuFB{)_Vzn8A zSLGzRY(?S{Afz*j6*-_8nJEiQV{P8kJkC14RTCrUD z{l$c`s^tIzsG)|ED7vSO%wPGE3Ww(pOx0F*&x=p?Em(YGuC&+;zP$I(mf=VP@|c*@ z>RrZ;%kq*!#2!nHe+tbzUz7{t_)%sak3X2@kS7Yts<{Ye?`>o%Ke8l{!5?T4Vr9b( zyA1UrQx$xbc;(vOp?$qi;y5RIzYP2B=O?baW8UAPnManIxh-fyo1{zXs`RY_6|8Ps z6gJ|$@;q3LN}ls3+Y+|#3p)FiF!IT@fpi+tBJ`_b!njh?f8Sn-1`d`mZ*e|nm3zj+ zQ@3BZZ{r|Ki%Du4lv$QM>qQwe$MV=Ktj#b$>F^yc)E~!m@!IY3h={xQ{mx`i*M0v#^ic>ZTG@ZTa_?~{x6yJHmEfkWm zXwE6O%9VVWfBFWDV75$_?RuE8oUUvxH<{X9T*Q*|Jmkf|_Y*nAa9xZ}?S~_QF38kH z;~}k>g5tO9XBGaK`wViMt+wMCK+*+r*cci&yYNw2(4xewae8ww3YF0t_OijDt%}l< zkTR<-1lhr?#0CBc41cef&W#`7e?s=6CraQKD;}f_Z`g-W>DO$R z<4|cG2`R*w95x2(@fm|-UVH2?UWSEz zgz|P%^;&NCs%%9{65WRhooUIN$R-LMjO{(B<$b7o6={;_3l8J@i{L9$DID z(+iMoT~8i!V90&JM@PC0tCD)8qyLp7ZbC3(e?^>{k-wcE>q(54T=#w_GE7u{0x|Qz zS3N|KQ8YXytn9V9=lXocahj}jQAQGry?OTI%S%=M2=pJcd-Bp9kMk-AMEle=$3Wd9 z>;^`kO_$$Vam>*=fufcz ze*ouY>mH(|i+nJ_y~4SSs_9WFfZ9dWCgmOQE;?8QVBWwubc_6T_|y3jvX_6DFVc5< z?7m!|&*COX$FMjqgjJ7>O#{`wcI|?HMsIsg1=C20PK2Kso(xp=kp|Pg|28c_v9IL_ zv(;3F4P2aN1&$K@kQ!v86aTWO^P*}Re3O7x$t52YkrUa66E^slkwk z68%~%u;MbKT2d)i<9?m*LeK6RsvUJ^w{prXqa*bF_YLprm-nZz9^S%9zKOp_P^Lve zjgUbdS-XRz7`&O{*VV}hke->>!GZme&y`m~hwxSd zP|BW6m~b@4S4dXU4cH@t(tLVB74?x)G)a-vc=^Y5NW-CGHL}kxQlzh;36d_*bfvQv z+lZl%C`50%m#pSO)VlRUB|W5GJtO$*Zh3FHhZ>q5?psRYq*gPcmB4sPZ*HW`*`qB3 z*9n-Rj|q6mU*IYI#w$JYhWcuae;RBo`Glglaa;qJSMl2ePDejDSFjiRvXa7MsaNjH zhrb_-DHVwyC$H4zS&n-^{FH+`has`jaCXSistGSIlh{UwQaHU=n9?9dl1xwCOibUh zZB;ELplb)$-W)a+YD=5b3CGwQ9C?)Ich=fukNw&<5idwA6Uxr&ZL>hKe`jYRhlGc; zZyi*h+`zNXu)EfX=boEx&GEubYr4>e&2~>JJQzh$Q`P4<2`6x6?PEQ*ZjQx%;_*lV z2c6EiP5e&njeNUVTG?$j6uE-IHozYd1Ng4+v>z1%{&yPvH=Ic;T zk_pNsAsV1o^K~Vob*hH-e{@aXN68YQ;M@uszm%)3Q(x){cC?hI z+L^ri_+cPW5rvWZ{Oj0gWm|hK@mA^U$L7XD#E;&@O~9XN-vHMWS?H+^>??eiyW2b44eillO6?$%V zn%Xbp4fZ5R^&w9u=62Hez(oe$jCw1BUA|Fh1Z)4>x8CQuz zK#%gvvIQ`|t@y-df8+g?8JVqybCYeeRumbs2gRWRcERcJ+OT_+^0`?dWY!iWU7khl zQ7BW;nmQ*yju_@KFCGPhWY-ps`4h`Z@^>Zx3QS?G2gqid13x@d->?PIJQHCO$s)Rc zlx7{GD;O|FX%s8@G%SXFIYaHUaiPkfg|`&&ymy0u4zSH?f9I=TMRhd zwq8l4jT)n6P!gh&Y`4dIWAHTiB$*dy%@IfQy`vSYg0XPFBGJXS%3b~nt}c9tE&&!& zm3g6fWmN*Hm6Y$7&w0{x$XvP+_@8YgVqhOn)N;GC1yi`^)>{)hx=1vY${^B4tGV9i@Ei*zmN z&4KaTZ~i4~XBn#Lbm;Dg1l`*gPfVUIDC#_UpqFIJx1-d?znK1&Wf-EWga4#xHpn5J zy{>qka&exJ$lh^HEZ^WnwK{2iA{a^o&HYR z*_AdAI@}Zw)Ca7F*tT?xc^5k+%Dj24BOjxEcs4-UsDCf%04*D`yFGbzSdSzeCiO`C zkz~!ve;WPq^LVZu?@V(oySV!r6GO$rK$0)6fgRu1;Kn4jdsP+caqo3{mbjw{h_~8s zepniPo*Yvv& zf74#(#a|=E(8@~NI#KcV?>Dc4c5730yu&i_XH2F=$74GjU&OVXSOlFrfAt|Gpzq^` zU+KrZMHzp)$IsSfr&*Vvj;U_l*-u!%mJo0=fwv9hKYq_`bQxLa2CAUoVc0{HF5WJw z={bhLUoF-qIL4vMXxxj+f@cb+56@K?f3N$)y+xw!qPrxwz;;s^$@CW?bYL7(W3=^{ zP|X;^=9vp{9`W()Q`dVf0_d43$!4_|$*c|DLvHsuk4Y0tNV{>^;JL ze%M8g(2Q@2Y}O@~MXjY!3r&!i9LpM?U?8u?RtMI9(QI2N66K(;OSsl24V8WPe`wmy zR57TX37J23Lo@703nP1;_se8Kh9LhOfjMU_;FUOt?A%Ki-gXtUHXF(4!D1U+!ROy8>YjhVT8p@$o$Ux%cc1FR}9 zPO~g;>;~GS5E~igmDJBF9O(mGJSVe$)^Q>1iHXgXPPK0p$nd6uNfpZie<~2#A<`!6 zcWO?nvc7_HpFi6LVc!+i@j^B+^;39aP;@^|XDQW~A{}HSa+R9_@Bl4{@sJezn-o2W zOr0*w>QKIt=b*GL4P^vVU$Wc`uxMqRH`=@%eyaQ|=%n2WgQi5KM|`y@&5OI}wK zQ8fGI%D0;cLv@Pg3e_U9e{SLO?zhr!@Z5HoU9Zewu7wS82KMOM048V|pF>ar(nIIt zqhksd(vHV5@{Erl{P6!5tflIeu=AFk4-ewdRV`+7hONuR>H<<5Wx3; z{bu+J3`y+XJV~>CV z&O??>IR!#ntH+L%pDs0N*04Q$eZ-!NtM0xvvcMVgCs<;BZme|9-9z)BnEoc}rrTX% zaHnXw9r@^9Re&j7e-|6Ba_peYb50a#W~(?~8MlvIo9MIihT$FUzDnuy-n{;VP2+n7 zrGss=TodOmfAKA~x5y`h+%G%ZQ+a8jmYhJ@1KGBn4h8vWkDjLkHQk_q7cqNvf}ahD z2ewYI$v55$JvBrWuig~I3OW5anex+@i<*BG^mfD_>hD(kW3MJHQW#{u{KUUb)ZyWN zdU&IEd=on&+$@+JqycgsW5O28ko6N8UsEzull53vf8L052-|zc<<+ti1v963NTA`I zDLdih$D+ovuEypX&4RRw+Ppsh15A`LOC_M)IE9XMpQ6Uzx5!(bdz(>e*ncjQ2u2)s zsY;Flk){SNWF1k|zY2dJof&~p2c0_h;UXDqZ%OBYUpRJfK{p#Xj@ZX33xGQL!5Kmk8{K0IP> zcRIcJ4e`xQi|-Wnq~Q9VVD3EMb7aodqGtTFYC4|g$=>jJA^s+{8};ug3zWb|5|4u9 z6l(+ml?aVSn7iFRyrA&>^yuI$B|D+LZ$*rHe{02!wKSGdOJ=GCU>2(oGh}(y?SWgld|vk|ALvWcqgOXtTWKDNkMbSNu>?+# zkAUKa-TiW@G^aUg8O~wo%4?X ze;4t(4XSYYW77sV4J2I ztm|p@rZi3;Ta;C}Din+!gpZr)HZK-v0z9pM!&yejzvA9eR^ymFobtQ}^15+DcHF}N z0FeXo}-F{>pTnUTxLjoy@@I$m-$A2HVo`TxG{_6p{E+*v=&Mge;=~) z;WWdo*iy<vADcS?5w2oqKFRnAnB?5a&NnYuw9}phL-uuzWd1j?D@;+7RyEYA%RPe zW>47Vib^b5j#-vj^m$5aj|hWrEMzWU`D>gH{t7I&yJ59f2|!TEibNP z`bq4zZw*|n?!5{QCb2pl46V_Ae2h;NSb3FQpvAH9Oh~JUhrQ!K+v&{MM?+=Zw7O^d z*kJCVpLQ)##Ji2fL3#6;0UfTK?@HA$_?8ns^6@HLi~Av&Vx=T=ijgF8{F$2?R;4&5-OQK%t(=zPmAv7%|E%h)zlIh~0GAEkMk0Rr9lJQ`a z=nE-pq_S>|fcQ5-n+uJ1*QT_rO$WB4>E>Gcvig;rNp-%UT-TOGe;x~tM9vEtBaFN? zFip~m1#i5xP7gYa@YCds>dMV2{?SkjW@BF`hlgdq7_(NXjFK~4RISG7ouMj=WZy?o z5t3=#DLp(?#PsLYh;Ijn*w#uqnD33iz1k1))rb*gE!8f z3ZYnzF6@YKN6uiFBc<(4>rYkDGKQ0tT<1NkcqdPE41Y_mfBK@YP*6h7^XOqDJx&c0 z4bk}%n@FGt9ECiuS7bpw`Z(;Ja!gwL{SbAP)I~W9`-LD#j_A9q(r+f{*LyuQVCm>2 z&OJ6?r|?g(raD**3i}kZcCPcpk3xL?R+fDW^hb^Ue%Cy5hZ)J`i;VhmkEMjA1Lr^_ z*3~KTV9$#qf4MG%2@&x}*$14Yx&m16;Z_AX9bI;oQL~#}BCUjo8}T2Z{PUJ9*O-#F)`Hlc{VmyK9&HE@ZfUaII6EcuRA>LkL7C4 zpKBBAv4qZj2}s8M@YId0##FxVkH=F7 z!)?z39yg>LC>inlAY!whb{Yu3Gldp2|U3+6kpusm9Ug@4DJqpe9mAH6}M& zWV+3Fe->iw0!VMhO&SanM{$(7>Q%a^UGSOKF7rp1yMX3< zUwP~YVK7IE7U()zlQsENJ9g6+95=E?MQYLP+IWWyi@2zNtx$EALw6K!-+z0)wLUj1 zz)?N=b$eVa&@owu)zw7c;Yi~*h3Ks&6(diSe~)epYLPF-&?Yq4cBNGHgH%7*wW-M- zdF~H5`dd4SZoPNw0Eq@XVhTrgdz1 zrJxnC44{O`z+ia?Suf+&iiXdI6)vRYjNWp*mPqa8kb~sA=Oy0=RjVs-O{LQZVwZ9G zJUc1rCCRwjBS1}}YOU}f@?0cAM}w!Zf1;$VR2svF4}&C3xB#*G(CzJ$l!RnLmFb%G z#vH5$?O=^X;v3c7058?$o{v?lP_M2P?=+gZ^>*C_DW9UP89Idmt^!NV<oNa;!E+Ni-T)@P=JdFqZZ>HuSaZ=+_IhCR7ji;_<$M9eoCkoS;XlEz>%7*GhIMVk1X+wRhDz;@*BN+zv_0Yw$fPRTM_l$oP}CNEu8wNsfv{w76;} zx@_WYlF(&eAd@fb$F-zUPC%&9LC6m(S$8KJSv>EjTX`si4M47j^PCb`n6c;gMwj*L zMGmQ-41EXK=z5(pe_1ce9}M1Pe@u$1&DkU%gF_@SEScx@YnnuqQ;IyOLf@&*I|$1; zdaGyd!}bhLsYs>RLVEkDc|6m(;=fCUeOqx5f%1lCYqF0w^DzRZZSUhqqH*3NRG9^4 zU)Z)?z+)I4HBui9;Ml zN+3V3J%QZ-<;amU;WaOEvy9~Q;UAAO%a>v5Hw&6Uq# zHk*wu6DhQZjftZ_H75Y_bEQV$!WdUo#dUmUI_pi zVYV5D1z=FRyHH}_1TC$R^?MDkY!M6`ZRI)i2h=^LDIX=fPvtH6F1G7+l=w&-8Nv%Q zESdPLIX_dB4%bhBj39aV3l?or#JMKBB7OBZ^@&*8cmd0lNFPQ}e^CnV&Np>T8HGTe zbw}o;@8wN0nq8Oz1jLM<#S2JKsZjf7gx;@Ob~a3n=VyVbK;3@qn35 zHTta}kXCz6t}RtGf9nio-5R&nsqZ?O;3RHaT`F-r_ zXERLYti_|Af3Izh&vO<-udkAuyk;Jb+;VdV4!$3JOj0n?D(YjMrou>2PR*`JJIJJZ zQ5c-P0Ddmn5et+x^LJz@vNeSdHe@}MKh>4f4wO&ZtA z(YPzC1<ikvfrYzZPX#VCY@Fa_~1TKLkh~eHef394rCDdUOflK%Ba)3`9JWHM+^7#weuN{=yH1*;z zoVfDw%Y)U~1Jw$YO>g=E%ccA!HS9A*8W@3Cq~?d?%I*7TQ$w$Usn%QAYu~=igV)41 zf0^*gyMYbGFlNZav_z6GVXml|H@L;p8LZriuJNc6sOQ8&NLX<8HLNTTT(ZE04HZ63 z$xdX#)r2nF@4@n<1FbzD`Ma8>8y$Gcal+E9ZbNy6%s6vjKGKDHt$bP?IXLhUTL^^N zhAir-(K<__L^W44jv_9e@Cx4>3`vp7S4T?21CR}*`*Mnr?`|Wspv@1 z)M$<~dRG$hqK}eFDN)g!^&4*5E}iwM1od_>NVSkgpO)zacbR9S@jq-p+pNI2dk-9Z zM1DILpS=Rp=?3uzCsoOx{U3Na9ajovZe(+Ga%Ev{3T19&Z(?c+I5RdNFd%PYlPB>H z5j8e33NK7$ZfA68G9WiGHZ+qF(-Z_aGB7!lk(Vle1z6MT+dong1|Xu838^B`Mvdq=0}lNJzjMy-|G%#H-L7l<-k&?3 zJ3jYwZJg}71_E-nXd8qw8if}C3xXs73R+rfU=RQV5*7r3gs3<WJq z<_s`E!;uKQ=f6U5-*>>{F%m$aySuv}%mpWZh{oDW^Y8=Qk$4AyJ_3ipx*==EJVe_NN1Q0!Qgl5Fo3e09sowD@Xz{ia4Zso#|h$)&cA8|{t82Avl7Zy0qx>~ zK;dyzzw%Q=Vi9mc-#vkU4%Z2Vc1L;tCD|cSwsybDuziRD8lsS{4-smLf7%crs=qON z1RekZiQN+xg8&e&0E7qJ0r=|xs3!)0@mmT01ruuU^~Rtv06Rh%2w$Wfg78JaU#L{v-kSpBush_;)aEG=aJZ0QcWaHwQsLaKZ=p|IGK_ zF8@E8|BCXzGX1|ZQhw;{{9Dichr$1&hq)k~J^!>IQ1>C8um)Oa!ZM)#+te8G$L?w& zY>^LL{#&bthY{964rNcEv;bItL=YtM2ad!kBRvqdx=1|S;SbvUfgAo>IA;n6L1^-3}mmSa^ z_D@7qh=BaM?+C5>3ljnXfKD(B=5H&A7yyVN%o%O_w-JN@A^#Td0f0E9{XZjv35YWc z=kWLGgeU}#;Qx`2kSKsa#9v$g8~a!J@P}9|p+CQQOX$sC^6#4nf$%`Usm909aLFLY z%Alr$3OQzXfyKf5WE&}e2PQ%SgL#$|_%5YktGPIX%!~<*V&(Bpu(G9NtG2xBLDuxA zZSRFDwrWq2Jpnf5WqUT8n6dM{3#0Mg`z*J|uGSMhGftAfB~gg&BGP4zplPF&v9<3q z;JoT{x3c4NygL_tk@7wD;3n;swu+(*O$HWmqwin!@eMY;&_&39&ds~oUtOLC&PSSu zC2ObM*0|qtNEPQ^PBYun*E>nu$=V(fpzii~ag!qiPpc%^%c3Z9vD|3#`IVFGLbtUv zI&T}5YqD#$T_rO-Ne7kMTMi3bYZmO>32pZrtP|`a!QZ_OS!pqo^ztd#oZO{w+b>z6 zY$SC((((H^qmER6r0SdLmtR?()I)t2^M8g8{0?RnlsO)Ndm zfBsXtFcsuoOUi4`Ec>QR>Fdg>{SO}foos5 zqc1jiF+E^BsaU$kR8D@@``zM{ZdnrQ~-sb_=|5DYdgaB%uL%EQdii_TCCX4 zZO+kF*Ei)^jPhoVx*zvntlcuc&9=@+yil8&{$>_`wzYzOpp4Ur zmF;DJl>YW?E2~u$Lbkk$m)OfEDK^mH;{a-+Wk*Lcc1IVQ>H_5S)rlgM&9uggw=8+m#oGwxl|FxPGy z(M52S+EK%x1`SuR4C2Gb{aC3dt9A{w_ct9iPNGLRoa&wTra7&4TsSOfQpM*5IsGgH2WVhO&+E|-|K?CKLlK3qDSR~MSsjvp76GTHV-t}k%xR;&w1$`ra8RE^`xzh zs?Q`-e7>(>_Gte{&5X26z_p2mYG}^qUi1%r_q35Y398Ur>4=66D&Q7nSmQD)OZ!a* z&*Sy_E51QFq4=c?#7GVFM@$kuZ_wS(z*P%5 zChAx=%>_2naJF4~YM9v=JnJnz8U@TLEq|flf}U9f4RV(HKEr&uVf)S7fk8w%oG$#L zrMqImrYu5l$M#Am$tnzKYdT~(E8AKlLxH)36qaPE43O_?kYhz0AK&WP*}X)6GIaZ; z27G17ii&DS`sD2i6g?!B$IEi9{%!w}XP*D~9n3Xyj#R@z7FdCQrGF+wPw0WL#Ldh) z=HC0JDns7jgLAl%8e;{CD7O_>>-IqyKG@dZKX9s8do4uB0*-{u?TKukOKq^e!=}D= z^ZZioV9DFAsxkh`$9(6rX_)(ewHhX^wOnC--kQ#EH;AubalUxLjf(8sqOhiF+A9dt zl{DWN644hzz8Zm`A67;c41=#DFMb&S7K*m{^@oABoaE!4be}o@kamxoHd(#@Rn;`A zqMOvijjs8cum6Iw07v@mbQ^kh?iw#AMLm5Ryku3+l@-Ul`p!%z>IH3o#yydjBjh57 zsXH@Oljn|dyy@2V!=ZY`uPQ0I3%*JA2UO@?He?G@z}PImla)N^P#_XVtZ*rtaYs-+ zKM6jJIV_MFycvZh3SUoYYP8xZ3%*D@Y^OtEJpF-qz9_rs;UQ>hKQ_t<7+1zs4%xl* zcDU%U=Hq9|fL8)Dnx;m74~lKRi759}?(XvwT(BJJWJWkTSeR|l$AS$|ZjrjM#O&1&Fr{6_ zmB^H!^3f11TN5G(6epJK8B)8j`d-;9fuo?s-|#a~gP6qmlbxu4hwE$(E2Sq@rMvH| z$EvEpDqhZ`FE)uz@#QC`uRPHnA8H-Tek6P3RvI*?keGYc@StwUG-eqsKUS}0ymd&< z$JWd21PjhFWAK_BuuFO6PnZ13PK|fU%#KltSc{H9c!YF0|wnt0dZvRWN(vQ~3} z2keoIx3bpMJE&BDd04P|doeYvdXT-`N6?!#n!Z5kL5LIG_nZ4tdhXA4&h9ZlPM+Lh zmPq`>|J=}uK~q;Iwx*}=hPFc4l7ZIVN94xG%M;RxS7^K6?>||*wD?n)_mkrtrj-fJcuA{ARJM9a>S`Q+wT(#~roG&sZ!);(yDneb z&s8_O8Rus=MnfXbpHw1R_4S4o_$YV8&mBPhdhG>^0Y8}x_^C`F1@-or+-TQ~r!)8C zdCdzA@A0p5*hn@EW9WW-U#4thEOC(?=4CjJfzpe|d{Js3&NtSv7nn#IYmdBIp}K-u zx_JerN`C8qHifxY-su|};PA+;ASFj5GVe@q*fw||5c(?7Nb__U;%ThmOyAo_9RpvD=JkbUnQ}=6HGOKK3Oz<$OapyH>fB@Y+YGUD^e#AM_~zni1<6cHJIJ zLW2`ZsaQrj417!M@L_%-`Tu6;>~9^c~(x1yU)_EdpNmQFei0E*6i!d=KP@koIph405ZmU@=)2lZzd4gl9r8d%}gY7V91 zIAzr+7b2dS8`F8hWTp5S{Y7NDGY5!&)5D4VR3TNuUwq}8)u6y@Unf7!A=z*1rEf0x zt4MWPmN<*e?8Vmpsn5F<{GiKH!w2a-;b7+XV`sI7`#sY>cv=t zG1(p_WOUn!{CVJDuUrlr4fnW()&k=v+N+c_^m~&rmH{t^KI$aCa-Mqs!BdK5r^wO^ zq7)gx=*4mNxF5bg4eEC?2**5sZI;_4b|}Q+sDqoISd*#0i$8OUXlfWxQF0jNT9&yU z7!1h0He1GQotQZ-VLmfvOKZQnPP3=vQoVp@TIBT4ZZ%kI? z_-O~OUY6b}9@hjUPR^QFe$pCrRVPmAO-ZuzkBDgpa}{fw3wy9zVtHwG9Ng1ey_SZ; z*UDYfsz-M>=C>8dZkL>YyR}Bt?>e5*5=F_RWFJlT%dw8sEkOi}a1t#RJb_7N4msJ5gIQlb?3D^^Bb3Y_8e)81m7`W&?L%uSUfKDIG zxfb;FT{p@6_k1xGPTDZcVAI>R?mDfX5+jL6sM9wba0CA}W(a$Kaa>j-C^7u&glsT- zQmx;DBIU>(G097ho^4g1Z{tW=vn^LEr0f+opT|}QOvxNBuYK~mC>dpC5#NOMBm5;* zjV})a;!mhMwwFZ}Ufff$k8Vgge9J6x^-x|)oMZRm&z`lL9O=AuqOrS2y$;x`yX=9K zY=~0H<^C)F!^T~If z_yjA7lSm_tz?@ktFz=DgD(;1}$KAaEjyTRzc(ZYjmu}gwmr0sMX7{j}ean*Pa-sGl zpL7L9^Nn<;{an*8&jPa5a1UrMS4HZsrVixRi4qr>StFX#6kxAJ-Z|P7{;rD%4Ry=brIb+ zwCPgDjwq^PRn->ExpdSCq&)JuOBQgz2$9|Vo3zPHZF&jw?fTbRS0Kolohqin8LMQ7 z4DRH~rIxz)x?ZQ6D-4k=Y@QoV~Z8vH&}B3K|Ca(K~-}Hjzd5>g(#IX$&A`4qJS_ zpNGsy);U2d;y;cH`fKlWH4y|EC!VX1n}lUp^5%`SW`S4S^T`$%3chb#%z1kXW}-ZT z^mzP!u*d>lq_~*c=*pF&A_G(RXkAZ$5Z~{jkL0(wU~e!Dm56&?P}!*Jb4KHl@6zKH z!T^OI9=|r3D@$@w^O{?}pbg;C5?K2^RHtmlN=3o$`99E@s;p@Zo)YdP>=V7Nyeglr z&`^)3rG>P}<+P07%0uA(kV6D(iLjGHvO8kaNW=CMP5fCOh>zkyVWIHaYIilL?p1pHD zVv5h^O>d`zhTHazBrqy`$(P^WJ^t@H(WB8-@UeR#c>6OeSC1n<)<=ZsRd5ltBq7;Y z1ARj&#rC0=Uz5aivRhbH_H*~0tL==xtjq&~Wv3vipt`&Csh-aMbr)C`dz0LUu`h)D z>%xScbwIx&D+9>nXpQr5Jov*y3GUI{$k7({F;k) zt^==V))w6ptJvo*VfyRtzynbiW4yzzsPF^trdO$mN68YwgVXRA3x{D##dNg4#w4IH zmO(A7IS$Z0AK${769NI~tYq(Jr3ARs+b@P#7yJzuGf6)E3MyqKAzYu1BoU z&3Mp58!z3>mM&}dcl$c(#gx{#m}pWqjE+1_HI5Au%m6SgWWxETOxvv$I+Ynk3@KS= zniwnXhqzpL17fQm6|_>}F?x9SHvWwz30I&*AG_z-3w<$ z>xZV;`qcCVV})n`k#lQJ=*3@mUdSsUZPNLUHFUW|IpFnpA|;-K)&E zPbn>t^mA@+dA_*yd2xCp%^Pylh((gK;efw{JNs3wC7y5%m)JShE@qjkxzDx9D-X6U zxOk23Sb0wowe>i4GsOrl}!Qwe^UZrtiKjD4(nZZKrv^h8f zc#X7n+|Irko~7GNWD9qp;uQ8F|K#mC!Mh4Yk(9vblo%-x?!~;;XWm`t;@*~aa{*iG znG+JswDvaV9&x0?V}dxs=&!u(eH@Vnfilyc{zjHn<*gM0fzcHWRYn|I&^OH~?RVc1R z%qVAhLE$x=Qw?nt=zUd|#SPOuIRFqZlif~}xbPrj2-6_#FrDqfTeE563Oe9Z#2@gQ z+GYGlVY_Fz)B2Z=@{A9NyCG)i`kVijo9~T?+nDaKD|+Kn!{06DqaR>916*Dg(3U?u za2<66pkJ7CF7X;qmK;pWx5RAiZ`o#{ft$wPX#-GuuOPUEnrWxrR9%IYS^%mVjh;-} zyNM+tF(t~@6$hqsDHB3e<{dqvzY>W(ZF^sgAGP*6DEG}~Tej$z0`KxNPAnD3zrv@w zT}z5?lap>6Dmalr*8FwjVQ+nvbT(*zN5oJCcpqYvn{9`0lV}59U!NnlmeH}AkEtj5 zK}&*9yNl59N_ST2rUd@{Q325JU6Knla8ujwT|(~OEq6T?+X52F!AL)vD5A9$W_@N=|tMuuN>H?Psx~1AU&=cGX)ejdPiFp?u~!= zCi-59E{{Q)@omtn9qD3J6Tz#Evp;qH*!6%jJ9LXH|0|OpRpAr&_017_t`a(?&QUFQ ze+#BXT#!G_3sHuvH1=kN(vwV*XDzTcEh!GmlhViDcZAaYOYry9=;~#%Qa{Nk>Qhq? z*&g(nDh{?(UDgfe$MFj= z1QT{&sq_6Sm}2_ElDzX$2|kzg_Rkp$Eng!sc##JBAa6mx^J=QEbQb7~ls&9Nd}ZT( z%tWkY_gDwLmP0PvLLXd-c|+5F2h%)<;_=aU;A0W1qdlyX(kqQ?X!zJ@Aq_U^Jd&N& z!-qs=ZtQqu*cA}*J^64os317mtF=U0`JJIsWk0B|ay7e~o1d5axWlJ!m^#@r|9c;K zHrG;yZJFcKJo*o-vom!mo9~5N>ndQL!#O6419J}>kf#ic7otT(7RGsb?mqz{07qg2 zbZS;RY;)W7(qi_2%qYJ1a;2xX2xqHg`RUMs4E|Kmgct9S4~h}-Sdqd}_*=wJ>-HpT zm6b}{SOvg+4>hKZ&XGVRG{GMEga{QyS%E2GD=E#2<&@gW*9~ShlW5+*l1)Q}WWwFo z>BBBOO&ZxusO8!qqdSp4Cozr!iad9d>t zTB0})(a6kH@gnH3ck^y)CzH-odZDW~QKt!pb~T>870m8nNlzdZgNNrx{z(5Q*W*PB zTe1`st0F)b-JxE%t2f2XDsa!LP%a&RJ4fG{7uTje++X&LWd3x*6!JJXBM&SepxZJ+ zDFWQ8QRfl{-QAzg>2uP7V?FB=TrLqTc*@-}t8gNDO*DO0{EiV9d@YXRn4f3cCm|l; zUfh)y9rTV49aK}xXDm$zmf2metf3&A;PVw>d#Jdvt!AkX(Pf*k3cCAnZJMU?9a&_? zIBnYR8SkUqZ*AF3g4lC7M``B~d=mb&X+2((H7BNhHRSpwIR*qVUHqRc2F43#*Vz%G0Zy{)EYMze{ z#F6W)~~Wbc;W#id_f{Tnu!YILUax*K%cyYE!7!6+3Gw_=6YiXmL|GHn2;C|P)W+o*I|oc zh)c&_2o$4T=61&9s;JE8C;bK0D1x#7zp4mn3-G{bNGx2e|Jy=fXJlmjpB4frI~QA$ zVH-8T6b~Lx!?DmTdrGgx7nAT+t1F+$Bx?D_VuSf;K?>f?1*e&xO70MsHwo)Ypbh3 zkwC#AV&EYmVv_Cc(FPV0<9mL{wGxP=Frof{sV|0MUsAB((GOKY3tB}SU`SpUS1=$k zU_ofC{Nm_(MnO?c%@2D*5Gqhb;En+s;LSk)Fepwiu|ZdYgR6+(4UvU)et-Z-`v5Oc zeseSFZ=H*OA_^EZXwW{8Eh09IYg!W0qnjIgC?{b6 zG;#dP1Me4*J0>;^qklf3Bu6;i{^=wTUOdXtoK+yH4iE|DpJyI}@OqK1pm;=})dMI{ zk$p;=NSbZ*Krn+Y&_8U=K%KLQZ}#=8_CNyra|b~Ff&IV9pGn_Mh<|=)u)rWlva^L8 z6fn2(4IrI^fm~La!4~i>{D7eBK2e|mPNGEh?}QwP(?}*k;9uByV2Ub>kpA^JKN|?( z;)B@>ltQS}_ouPK0#gQYT4;9O1P4c80Yyrr0;ehgL_`Kx9o{hC9ouCN!93fa_EYc> zZ7rW2p*2-a@l3cUhcF9@??D5dl0WwwBJ)6whE!5YRM0@f;6V34A|Zb4j1Mk=&@UJe zKxYHbn->pBHn7_|8(}Xa57bN8Fb=p=5YW7&MtI%pFZ|aQF)#>lD&Z{+I4r3DATctK)Kw7Rw(s&RzyNT9_(P!qY8TdT_LYm_tq7RAdIchBcHH>+jqF#2mC96dj#hE z^cB>vyoMH&62J~Z1MvnBkt6JDse)?=>EQl$R|SO|oD_n#iz{gcQUu5C~8g=0CbNO za01a*`c4%?f{7_q^%o}Z`9r8 zhr9L)sMM2uFc+(rHubc!3tY3LJ{DF3fjb9E*DSQY!f)(e0##(^4`HLiO>$V>4Q<`-yw;PXiQph%kia>bL&e4M?&f$*|-#0~TPRB;0XK*Bc&c zJ@@*%m?Q{SgT`zLb&UZbY3uN4CL0fCz0{`fx=H!b764h|NouCdzToYvPf2Lw-r_7)wiE@EE%0tH5?S_s3Mp2_%-rfQ7)@20(EmkQ z)Mo^xw}7H4{`A#o*1j<;@>1#QmGF)xM}Dh3?oM1>9|`5dRlN70sZAEN8&;z|PO6i! zR_mUx4swi$$@1>L^|H(`)cvHz6sx#iFpm11U2N8xM?J%)##btLo=i|LNOte=Q6=^6 zC_v%i^kz7NN~so5)U3A@$^|DJ9d!iAqRoodx%IQ5dZHyneE=^4#U?=GvJF;`F(vTNB z9i1%VJlq^+$}b>o$whgnLiX=+H_GD_|Fdkq`*36vqfir1SG1$bH^YNrw1IKJsZNkz z5kQ&0u-7Ja4i&}p7L9SeDCg28Wt0M+u^$bAlbjbuT1!z}jUK-Q^I8xCS-6m+W#Lk} zBnD@ofz(A66FICIm1hDQk6%!S?A(wg!@}HB*d;17`G62`^dudNRhhyZqP}9G_E}wV zSwJr@dl9NVyqUXukql#Oq?iVQ=4|+|55TXzzbamC!%?mFVY}8-C~W@-4$1eOlEph^ zfrlWlSSO)ECgSh^3#tokd?OKD1VM#@5Gn@G{-BmlQ(KDr;7NQhVL9uq&tve0)d*6Y zn#!}O;24ZialTlQMNsgM#obU3{B^qf+&XX{0pbq~bY}y&bci2EzkNe@cRh z=O(YY$S($0{y>Xi1DPEFxfzeef0hGl~^NvEp5byrHC2VK?o-j;UK ztGfXrpl^z%@EwQFmx@|?pR7`{RHOxP8)rXOBQzynALEvoSQckcY=Kom01B^J__srQjk6v062d-^zWtCmv7w|UDa+rpZn%6I2ceo=G4qR5x6y%AEc6M zkeAG~aW@i--bPIFPX>>rshYh6S!r`K)lIX`K&vee{Y;IjB04yw{_55g=xSsCz z@BZ85TsT?e#D}$mNIhIGaTpKxKaYZ^oQEl1+v$nUEx42liI1hmb`;9ij#pCS1?}N> z#U1^g}hB$=nG&bXE#JOr+}yrz;b53@^mV8b(l?e*>J zhtXV+8c8eAUPQw26!6s;%s)K@EylD(CB6g*ymaLS+tn<0C;=1Du3+K~i!5CqaA~w? zdxFMH>og50xEg#75h+mlBSYrZNT@lk!j~PhP;Zv;Dr6mn0qn4e>Jp+7_Znvpo6ibq zqOmS7Yq=|Z+r`iiqcE1u6>b^@%iqp&w8;iQ3;uyuSI8{&Hw*>=_{g^V5Al6%UJ4 z81)rol6bu?07NOgV*Ssf#&=}M6wDQ-{wbxXE#PhXspTMFr(_lq zdF0{f0Mtt0TDJEL_qb>n`30_gHA(%_QTT!yj@*EY;gHCUN{=m%ro|SvOz@M1`>lq>NmV`!*BVxVgdfjTh-Yin1Qr3`-il=jOy*gur zct&c)w@|847P4gxR*%awtDvd~fU{yE;85?MxA3hY@0zwuvC4<&7-*{M!oe%vr_}nG z&(tFsn*_%q(0Rdj#YvIhgkiSxXhlIM!g-w3n5N{XPMe=czD9SsV*H6VFkVNT^-8+y z#Ovh3dF4YZ^COG1W5Z5&up0zdgcAZ58Rd$!p2pfuUQUck_nCS7#QBSetWK~ZV5!YA z?3R7c08f#4e(QzXCHx%p|I z-C`0pbXRT_G|OV1HJSrq_@8hJB)_0HH;BHi(KQO?HGRO#9n42aQmfQc^LygvJdCf^-n09ZPc@q*Av z%YUnzj~Lla4GVV;-t=Y;WGn+9SgG=1ci*;qd99uNxM#Pmc@Lr;oWDgi%*DSYL-RL| zYh`2qcD2n^7C5c-z!s{3ncdBy7MUoHlS<%cqxnttiXJf0GNQRCiwWFcx|R1=HiF)N z)DCGT!QVZkST=6;;SbxL0n%lY0S&26?MqB;b4IBcSj7f+Z+v1ODWWG~Y_XGP-}aYZ zpTZB|7ypV=DUuJ>4q_TthaPiL(N)J5t(hIkF7Y6{FlXbwL6Siui^3Khv&UO@nkqx$ zsHn(TAiGAj`94MJ-`7sJ$8XAWY_6lqCsG1Gh-XKNFkaz$ztU-`03t{Qv}`xUFMLN^ zosEAy<_SGA7XE9NYOvOqj@TJ&W;!QKdPc%}y zEqRXM0_hU5lR%%7OehMYo`95>8&VnMQDJ@=?1?q9zTT9yB}=}PvF^xHuail?*3)<$ zQI?Ry;r)-g_J`xO4$#!>^Y1>KgR=RJ#1is{-TTaWF<&dg*ENC={rl%#=JiDhB>~B) z$(xi7@z%v*RL4;-VQnNLK;D)gZ8?)1aV?C=PX1^VI#*p&Xd)rCi=HIM+uK+2!`(dT z;L6|<`(nP_fQl6STt#2l8aOABMB+W_XIywRZB4^ofxIVJ9x%d>O23RXKU043;Q^O+ z2`#TU-ZceA+|q6COaYJ5Fs1H|%H0e(<*$t1#quh4rWq)C;wsO?cPD?`5jrLeF<^Tp zwk)E@Iq?ifCNd>z;V^ZVeU>!2_#RzY8}nx2_^MJl{Hb?4HuhYVB2UNU;0;NR3!4QR&x(-YtWbI%UbGqDI_M^JEF zQ`z?T^(Ea=PuLzNY3ZwVQBt1B<(=3gB8;<~=ePK?qv|wpxZfawPbmu{efr1aCUwBbv}1EbcoC{hz}vcWA?9^@Kh^DA);9A)k2+Y-xF4n|WCC zjjxJ8Kb_@ONN`ctDf&a&KwKItiBhD z_|G1x3CS)F&VlF=Hx{hyi?cqCQ@8S$PZkj?4KL|sjoXaz=*`zbu4tmciFMHqwj9kE zq?S@B8f_NziQl$zl)`N>Yvo-YV)md7b0A2dK`o;v@uiJu0yX|Mp+x&OFJKj`!@sn#>=RSEgPm6c6<2R*5{DOgPX=74> zD9jenzgf2hGZ>?(auWldpGs`r@~c92!P#WQain37&6vM~Gn{qM;oX_* z!cg(FVb<;gSGq*rirk@+UT^*rSEn=Td_|#htZjcQXLhDOv0OZK7$jF4A_4#oyplRV zh8Hzu;i{qGgIDb&O`n(V$YNsUXE0Wt?PMRbR;?IzXzeXbpUulLAC8Zk0?Mb~U;#1Z zC+lvXI&LF`y|+7}Waf;PMr{4<%fj*Sp22`%NArQ_9zMfA9t%p4QWzR?jd&Gb1FM91 z>kkvbdiRzV$|;ZS1as<>Fo4%V7BhvHgyTWz&}hCqJ5_55KSSugiL6z!q~J?4B|rro zyOJ;-7a3AAf4ghW0{DQ8m%7@BP=ZP+y3eG$^Zw1wRd1vd$WM|Bqrgv0;_=!Wet~^* z;)|%ajc2mi^5d5CfHnzTFAoj5?Iy^QEyCJTHw|qHKYDPd`6=AQJ0Ojmp**loNnTwq z8iZ1m^cEpJ__7E`cfpo~_dbT^^3MQ z795xlIiY)ZeV_CiL=W=@kIHTJt(uaLkiC;wDuaelzku=J6l0vGWg4DFO*9rFh4HSK z`T@VpN4ZUyl(!Ep7Qp$jiHQfKAq6&`34Zw!zq4n>Q@X-Xt3vRac*1iqcfMytJ;&pD z;^q@t#P9LyRB%8npDt6Z+wkF16{LI2OIhwj!WzT1#}0-zpz{rJvwi+byZ@qQDk z@KHh|f~%7Xe3JDgk6xZayGF=>p-&XY6fr=pH97Cg43$^7zMplb$yjx6WkAfg4c;K%Gt0B#$K1h2iES{ zMNrSjodI2612C?7Olv`#S{RefaD0F5k#2tly^>Vo<)N!cQrqM47K_b&^`3GdG7s{; zW3y6kwHVfOiA@J7;nRues5gRlV>6yJ(%u`fgku6Ye=1U!^Mu z`k+vFxLx3X@XQ$7(5xY05pL;tO|ugnxe&w5(4IP91IP^Za(UU84p{=faVn@cnxEjQrm>e3N2K04h|g=#Os3j4Y&Gu zHuZu=yBIqI%b_&Tb14TflZAx~C2)v`GE*V{ZGE(IOk+~O5tupVKg!?QX!2+XzGQLd z?#I#j1oZO0>LHoDfEaHfr8bvV=8WVqsS8dCkD=7l*yEMPBT)E|@*-9*5Kqn|K+ZBs zO#ihh66WzhIk!l-C)1ZGY}wuXYxyZPS7VJc*RsH3aS(kvK6!=U;G{#`)IDNOzM=zT z04Q*c+kl}#K83gPol#1Fp#$%I>+lX4RyBpP3iIaN$SQ!df_63>kGutE(9xS6TFiO9 zz8*>&`L<>>rMsHb1Qa9;+qX4xU;pW?6#I; zcrm3fPtlYkVO>}Ji!bk>W@zL9;U|TP0YHJ9%PhnnbGq$NSO=&H0ZYVwi91piovIB~ zcnkH$alE@Ti!+YsDNa#!84Z=Pd|G9tsi3~?{>RvZ<7{}6kJWYLN-|Sf7;$ofpIp#w z)>YXO0ht}OR%F1{WJDTBT-N%NK>L;Pr%Ar>`=fof=Ui_x!ZP$^ z(zI96?ckIGhY+_3@$)eS^>j5b{@a5FeDMkr62P-G;iDe>d50Y{WcP3P7Cc~V0BDVO ziSz@YNK1jl3E@@_82u~DSb&BRfS@A+f=B+F2NNJtexwm?>;B0HdI{VFS@r~~bif=2 z4b#HExwwRHaTg?f{Cb8lXflNK7ZnTX_;G+7?-blFU^l=N#EW+DZzp8g^v?nAbinz~ zBJ!DsXoCz76#0T2=BreM!%~v^@msoe=xV^ z<(vlMgDfAj524?NKY()!1B&-&S%v_46zudEv?00yGI8^-jl>pQLIe4d4H;9LX{ zT!Dvj2h}7%0FvP$C?~8y`^VvbujYnE@h&3o_V4|<28=InvVNYoSYm8`~}(&q55YipuoZ-BSC_}#ef5T;fes@p+8!q zeL89eZVvuuw^j*bPZ}e70A>H{xo-o=FOF0QTqzwW|7(KWEMV{e_HoFw-;8TO``2&6 zmqzk0&D?JrF`0t-slby>AKZ_CS3T_V#;XD>sUsiKIGJJ?i3adXUukIkURD)+Lx@-B zSDiXIusD4X?ey;ZZBVcmr4YcEu=THEaLkZ>@7L%vz&4Ww6=EUKTM)2A2V{qc_$I)8 z@Z zKMfK>f>0M|`~?Kq&A~lep9KvZ*rR4=fL$K(2+tzyt>i1_@i*o-_W-a5qXbkpBajHB z9N4+#(Yek=g7OU>6mN1I@16PEeh9r^|$`7`{9Gm6%?az%C{U}4aMVvsI`-AY8hI5`KM16|b zw$C)(VW=<&9NJ8j_P4K8aG(;Iw#QEZsU)d@fV}0{ZZ{i{q1D~lQ;%-$STgh=Hl8H^0I05bO*s_ke%gx%pi!t#qiuD_8iN_!6osU6JM@E63~7 zrCpxbwpJn|kWTqoSNx)lfB}&85L=CP*82IRQvngnZvHvy1{#2T1n$67`3xjfD(5pVsRJVSX{^JFS|7k>W2nO5PViiaq&1%NRk>L)nx3(vD)f8L zng4QLr^!{A%npV^Ww@+%k!R6Kd84zPoyM*ZuYsvjHgTygt9a6EqXemIWdV*nibB9k zwb?T5XG4P!_L`+oEfi;T#7hN>1H6Ro+tH$Ab4W{Y)n#iT9wnBwWtYmyEy?MaiOzoCFHmI_uk1cthRT2OJe9{lVyr^%%h?4aOF5U^zK6072fRGr@=ZDDc zM}DV~;xziMUx9K_vclPFzESB}&cVW`LR0QDoITHI0 zoZMn|>8;;wfMCOI>7w!Fy@x2Rs!(@thQ5N5;?Z{o;$mcJ|y6uZSWbI!IO~-1&n3ST8 zU2pjiyQJ8M(M1N@i`9Z<-8n$CNfH<1318zzMYfG70Xh0iFzIRdDZgWpj5H5g%ib@Q zrlY=e=cTsv8ROvK&s7g&opHv zLEw@QLp3a&{5bu!PI}+HKm@bYj{h&XuP0(&R*rP_uI9^!HeSlof_fQA>=g+1Z)~r3 z`}g3#`;nl$2_y!Ty~K{!u_>%SLb)B99Wp(9C)2#k@s8I>Wof@PY#tv5XdS z3xinig-k?8vu#1+l8UV&iOqJ8zK|e0<)o54awI2+m;{2(;a`vB#jFP+OsPtjk|*NL zE?Sk3oN~u}MV`Wp)*Ez0)vZQ}=N8XFcKGSbH$12xC$00|>WXw6>>yTLcwLtwS9@?S zqHEVCg(Lv5gozq8M{GGxmKa1!={sb2FUS4LDvfBf*l0*rwCyDW6@{f|@xajL@E?Dh zP*CG#(C`h>@W@7@SDlJSj;WdwYKQN9ukhok*AiolnIQDu@s5$D*Q_nG!vzE!xqO#F z^ph@QkF#bm>LugNRm1oM+?Z!1%45i^sI%Pocpi|5n_{oyiK1-NwTj_`T5X|w-jZsH z6X3n!o;D{Z+)%{_X}?X|M@g5)_Yv_JP zXWS$wlbri8<*&FHdFQ<8B}!yRry;Wk6tYmouvzz@X4Y7($Fr&u-?uPXaZkce1)WBvlAk5BHLd$bLoIy{2RZ6xn0M%YDXB|05bM*uG4+Vy;n|;Hl2+DjnX;=Un=Y|^a?RWpRClb z^?wT=*p($e)?Cpj7|lChQBdS}dyN917KalX;7XH?ILcyt&+2x%k$TnfhTOg#YUgqE zvEO$=eh?kt0HF}hO2`%%xMkmSV4qMtshJ9RH&bbw$_e9U?nw?XEmTAXu?iGl-ICse zlsAaoWJZb+V}mILaS|JIUkUq$!wQgZAB@!|M0xp|VQqaZxc5J`lf8A?c>%;M%gCz) z)Ye?~JFo@qwn~_GxxyaaUmCwm#88$XAO?nyHFi^MXkbGjJ;+^z&1i|F-DRh`bxrk$6~V_tXXuKNpQO*w9)&Ol&K%rYM3(i40EK_$Qu9W1nq!jmF&nji_w%nL(78y!_hS4U;|k`P zwiH_H(b?Zsa&#y-^4<7h_5m+{3}E~Trv^jOPrOsI`IU>@FCyU|s|WDAwBxeDQg!{3 zSI~dV-t{;jny_KewOHR`R?>p(ny7-A6!WP1zl*61Fw7Tk!8kjTc6EE5DrGxOp{XTiiWk)@v$q)GFpy3cwg5}U8m zQqGd{+A_NyibJzjPXNdm6Uw1TThhro6@mf(>eEh#aRS=o9)R#=UA1!43KYeu#B=N2 zmW(vo;Zk5c3si^Xdcu%}@sRVm*lb#w$J1Q`s|gaS zO^*VSI(-J6Quc^gX$mw&q}DeX*niBhh^Bf>K7ij;O@Muu#j~SI#d@zXGmOneEtga; zU9Pk(HK#IMrn(Bwl&^YoJlU@9=s)RSe)kv#lKXwuzmYaNiI2$Z+jWKKui!P1hS_p_ z8O6M@v&Okq3c}wO?=xZYDdcm>6hyZp82+&ou*!6Uavv&QHPiBSv9< zlyFtCgMfU`&>)Y+DFXaV%({|i8tDnD=q#Dn+iU6gqQBS>0f>brCpC{^-0!$CCXSEPD#eo2dYI&Vp%<*(yHVaC%EJAIi;jMIo zb8Ba;?~cm?=4|KOq7@C~e~eZ;FgTTNOC0?IcSdjdk(jA*nO9>| z?(XU2y3nTAA#fDegSsQpfty&>GvZZ3KE1S2w6&{4oY9J2A$z%xuxn*WFI5HI8|Cl| zKfwK1!@f@agJ7`=$s=%C<|Fjw5VB@YJhraGOb)|?do-)qO?gUh%e=y&CsM^~|CkZ# zAS-yA580hnNDEg#N#FD(fl~u1i}<;-t1dTD2LsaL(v3Rpe7>XE8)icJdD*66WRLA; zlS^2i%guq?jb26cU-`b*Z_|D=$p=5TB7g^7TbY%ztmH8Il=Ak*(fh%Sc%Z)nhy@#u zE!!AKsr5$Ad}CR%B*n1t2W3q|KpuXFsQhR*`n^a(>F(&Wxx-KT?7z!XX`EYq7*YR~ z;}veBtXYvn?26-@LrnVM`qOS#q>+F~Tt;G}B7*zKPXhD(_ag-DQI20uw*d?W55R7g zyVlaA!$)5B+eDf02yM;N$RuMFH%nOi@p76fcmW`E8`lD;ltsD~k_ zHF?KTt*KCWp{_G^oRwnat=BxO7vP2fbA*8J>iw0|eVWC_21irTT@+fl)ej1}Kra>> z?d6vCw>2FWf&Qa24Xi1J#qOMMCb!rjLH`SyIZc&G{urNjGKsU$tngPGhee^`iJ zGV2it;zNKv=Ju>skmZf?6aum?-PWa2A&Pl{2=+_{2af92xwF;qqG{d>;JUXCRaC6s^xcO3o?4?d8JhQ8R15zMDD%1MZ z--Y;n#uj8fJOdXO74Prj2!JH$1Cm^_RSC1Bh&wN^Y+@2}g;3Rh2;dP=Z$FRF677vY zMb+4K&8BIz?)i&g^sh@MK}UIkL|Abuf!BV^)y(Mf)`(8_58=1n1@NgIltZZG{5MYU z?+R6W)W`fwp~F{hh}hxV#MEk?eBNq!X(fg;&2XwFMbi381f2Uq<-yX4VH_4%;2fu_ zL)Tc(E=EX?WqfpNZ-PWOJR(chqcchr9vFoUch zPd6l;IEIG`iCG6!Q->=^8%h_WX@?+l#_cXaDTUBVotp23B51TQFz+K7PWdgQdbQij z%HP3&P4(I;%VjKq!uEF)^^*KaZTkAQg-0=#%czhr?CzE-zBNk#m`|jTfk#*I3~9Qs zZa(9x*2aM?DKB<>Qs0UCIT&{Sql|u4Wg^itnFUPj!W> zIadxyZ*qIQIZKQx&u-e(EvlkhQRAP)3kIh_dU#%oo=j4i9arSjw~Upx-RF}dP=BVi$F8&H}|#r`Fx2i z<%r14DBV+AysDml+zl+#SQiV0b(_{;a;)+RV9XtL|1?j2{zaRDHMr4s+j#`%Ml(y% zAvd)?v8oIBUO0NZlp_n>MbEdwpQmStKGGia=S*4Hy{Tr&O!J4s_azDwa}>;hU}PzH zgE3#ORm;S$ogoExV@oy5ChQ>k#vLh~8YFA3q9!(<3S{e_Io$ki;jdvRF|IjpTf5!) zYnX7Et;x4gczt3AJ@&VSL~-8CkqZa&<-IoTK&9?meN1_d+a15= zG+I$v6+AZB(Sa>!f*&Xu4^R9B#4Q-5f7~%nsoF;A42&pcapHwdZjQPRS#~E2`ERW? zy-kW2%{I?cuIn)|S*ANS-f=n7BNce~b=e4@VTy|P8RNEei|3p(%EVy=K9b~TFgDcu z09^3{)IQw{LgW(X4p<3%HQIbsGvWgUKG^d2DAziu6ONt?{S-7hBpTasuNOLy_%y@t zEgwXkTquBUbJI4O131d=OG+w!d{ zelFlsWqL+fPSEn$y5=V<&-v&XidIBB=DUjQonVN}&sJ7A3RTn!DHqn3vTH5}EX*6O z7DVbx$jggA$+Y~vZyR03jwit)tX(!q9`(9-xmO*$ghv%xUQ57x>)s4P1kh~!Y=f?1 zyRCc25#qfOshG|yN9$%V&~TPca=hvD8gbUC$(Aq){&m|4QS6X((8Z5_1&!fv`R4MmV@zQT!1RH{tk`SShWQC+ohuheX)FLcT4Lxy3xp$%Iy|; zAzu$N{=6=uxBM@QX69*-rnZ2?E+Xpm&HLKuC9=0)fzNz?%s*K_fU;=KNa^wp+G|~( z%P_8uqX#wR1}3&S^3i*S;EBee^-Rt`!aJ3QX0s~P=Gl#-vhCP5_7Tzhpv=Q9uh?Ew z8Gd0D(9yXDnN`3=P zbnqF@SZ@UV03z>WKuFt>r|4xsWRKYY?Y0Y{N8EV#LNWjp9aG0%JYcP3^_^3bhw12nv5~bmWZ2AO9U=gYDE?p?nN-4AIy3^WZ zs56~Z`A{Sm33ofxEoQ~eKt9z-)8r;rXz1MSpS7*$nAXrgZmjtDgj!6rUkL5(o%hY{ zaw+%WAlJadHbHNdMuaAjs4XQ<(=&B?a*i%y5LY}HOgP)~&v&N0K$6k}EbD)>N~2_j zzVK8*)Pc!9Kudl=poXX7kaa(UR#6bm0xAiuUD}vu8wT0WuLf71)CkFflv*h%dExvS zUbx@F96fd6p(``pd?$pUAKAjet0NzvkAr)xC_oa3oX6~5Dadk*eT9mrq%^Y-(aDh) zCGGv;{;}61Q2cQZ6QL#qr@b`qyk$(_cSKKU(AxjEoSg9sb#i=&&=%O+=i$MYf5krn}M1s`? zT~*hG+4o$J;ox->s2P~wWa}<{1HYF)yO>p}LC!17 z8Uy>0a*C9kEXn~MWtby)BljEQEF)Y;gL=Jf*I2d z%*Eg_3h3FMEqAdmMW$n`=ucFVS9CGcp_G1A+OLe}H!L<*RbWj8aOOLQVN%?CqjtEBNY+dTY^mOUGry*1G@DScz$?PQOiApCZL_RSW&6C# z%|3dV7V|;HyVGLkjY$b?ZkzZu=sZ6hL{FzjYPEAX|M#iMP@_Nx%0-A43s>$jTkof?;xDx!-3(^Hd{S%DW1JVdGT}Aw8v~dSXAjBnT;Adv9HOITtiev zB5bteLA5mc66jJ92d6<+zu8PUD5)C+dGCEM;3cWyI>)`9bdswgEkD1&4X`b5{y$uY zorF0hDINm3p6w=+5R98Wf%FCwjfd<1;5sbKY+V0csE1@>;o#s(A+-ag`G@SNCR$@Z z$u-mHf{Aeg*X=%_yu+B@f`8I@^3XSzEx>j zoB1v^?sZ>TU%h1;{+XHGiERqj^v5(%{x8 ztQx4-L_ja&U6)(<1s9S(3pM^SVgYf!AgTISiqd1V&d;FR1M(aI4gnF|*5|XHyJ&z7 z!Y?2!sfqaK1S0tJAGp&D%D0_U4!(W3|H-v3wAl?N@(1A24iILRq?Re+g0O3lIMO#TT1Qxlc?AJbx3jH2~S^e&}PG2iU#d=b$FO5MbfAyNxY- zwm0CRn7`~EkA!_mU_OUFzSeJn*7q-$*J_%tC$lg8A8?mQNB}PLm+{RPOO107xBHhd zn7zIm*E3B?|28=2hrSB<^%i&qQd4jX-{)0TT?QrKO;M2Bblr5gefZ*qx?2Wv1Ey^) zvM%fjxJ_=Id3IC%1qFk&?$sgfwqtd`CbkPws(X2*0&i!-2Fd6@KfOztDP{np&fpF zX}tm&5Fk3N-9J!zDBm)IKy)0tf8g{`elfvAXmf*)Up5+qLrk>*biz%Q>BJYI4K0fInm7F?bhQ9HuK) z0}8(%ke3=a(kr2yn}KI0skGZgLUd6sqZ~<73zM0$@W!tm>BkD+x>X_en$RV&6-@Ms zUDP@kQR2&BY9`)m)oA2SE#L<6Qdh5ZSOlx9I*H8x9|3GXm}&H`Yq@ro1xIaXG7YB( zR{SYpUGEkVM^+f~$emobaX#4~1 z8#{lR(v)3$P5ynn%9;#iV{Hwu3`W3Orw|e?kxz7t^re)J(P zm5_Ow9~>5h764uxLU0^EPXxTd)}>SCHT={)E{01sJ;{Kmwmq$f9Jt_$PJ)Wfj?;xj zQyIkmPSGo?zg~F@1bbecc=!0m?3@ZNVv(P6z04~(QCAGQb0NK8tf=OoLElq|1Rb2^ zK4f{U%0{dmY{RXZWjRJx3za0oT&tfX6q1FHQB2SV0C2L=wob&i#}fW2LOm%|_)Sk= z=e|TgVqK3VSjW$J>9CDmX|G{2a$qfUE7d+lNN&NtwNCX*NXK)3R>#W{ZIj&E(15f+ zEGS(-+W*-i&MuFAR81{AAOVV%Q4~=MPs62E>8hR*UDUr>0v*u?wU$7+!&a4gHiqU>SJjAARXmun1w?A>piQQlWt(20LbGc#Pci z0z%HR90Ig<v-1fSVI;UDfrSyHePE7`2g+m$z7j zDnw(-QL5L_?^FG9FSi&bdOKz?6e-O-(Au=-RDpQO69K}%_T(|3?Z^o_GFNzbsT0gg zNFd@wcPLIZ=la!K;WbYyx0L#SCT36x(Ptx!Y2i~|4o62otUkP);gwVe?Ug+@y91Z> zVbEMq0{t(dDf>xZRWzi;X%epqFF0|3`8Ln{!#z*wc3)90ChKw@{Tr6lm7JejWA+Eh zA_}AKt+epv_5L=JdtR^O^bSqFcqG{ zt$aY%mB25YRL62e{Wh}25MX16?h3r8q7igT%V`b9WQ~u;3Bg7ax%UxMl+JBwcxGUJ zjWFb+3%iH6w`3UG-&c`0*T0d#!poS(LhytStg7MJ-??g^k;=au3{erY~#u1c>Jn%vrh8)=WH%z_G} zE%n_I#BcNh>x;S%vrus`Iwjsl6H%^<`qqSZ`AIIXA0wPqIiL9V)}skF37P_G`6EK$ zZid%lN+l05Aoc8ar_K%LG61e>xZ}-cY(rzYj;%xnDIA4W)9(@V@WUe4Da1*@-6_v+ zgz5|xNy`+cyOJRybDQ5J)e2iOLEKY~Ql)%4H*ERj8ZLqpcAu(SQD2*=t1XR+o(hXEi9jhXPiGUFqqa0| z{M9Mhc6cLo8vHHDhQ+9~vz9XThONx-(E`z-3rG^9q?q|fIlYh?j0|*RJ*|kt5DON2 zar@?=4>u$|u})-(>o?P|tv5jwNRi5hU5NhpjZnXljzwka(FxnsZ?*j9`_IJOaX1>? zBnNA2_L)&uRsY@~TEI84%K$BkZ82)JjS}s(;c1ZJbQ3k=`e!2OP(u9o-C=DoH^vJe zLOL#mJ1PuCXqVJ0a<$&No*Ap%DrDkFGYQI^WUY8pLTM;B;;`t916pEXr;@cAs${v!k@`SYEQjuSaDTlU#fq z4-YN%$+9x^H9+Rt76nAEd!pvTB9+8&P5L@3CLy_q_vH`Gb}`BJjTtr5DafBX#r20t)XahNCKNkfzKP93%E^05w{L;=#>PS}8y^KSr{MmKM|X2@^x zu|=Dg*y^~Ehb7x$*IKYt>Ynv`VTsyq&D1fGO!;^)r3wzdSDkVYlE!-+4GH*O?m>!M zc_`V0`%i4@QR0@*!a!B#`R%HyGi-!#b2E-;+0N^o{t@?CFMXtY^)a%-X*Zr1Vzgz? zUpq75%itHDlX~iu@bg`=AeqJHU{)5j$^t#VhR_-8rmi+hvP}jPRoV6X&*7yz+7Hmy z?~pTEc9+mo(cEe$*lg(Jv@IG#Q9ZK%+VECM@_@!ws&MSpSe&U&SlH^Gd+DOWNv(SO zL7VXAG%YZQzneP>5>u;OTJ^sBVV(MOYvH!;%8SpggnS?#mww^W!P*UQ+)_IpQ;R2U z7t7RiBj1G}#?X1v58(|g7Z@jt`HZk+hDC_T?j2)0vdL*(#(Yo1CGE$g$2xnWM~-3_ zw}2&Lk`*|Kk*{f!`8m1x;K018?!X6=-FKJTt9bqWjSwcyTTOKRp^lD^lfiR3$+9qk z?ont)Gcaz?Wtc(q0sd{WtC z&pGG58|~?`46hAS;`_<#r~oBpKFD1+)RVS^`}-j3c1)ktHMT5e_rs9zbsDnfp_?Z>m~$gu_~E<5F18FC1= z%svkf+!V6NKYBeq5zN*pNTga8yW^e@aHPhUBFo|+&O)fiY*FEN;u^+U8Tfi~LjlVk zhst$K`RT4#Rn>k-6wL{TEI*3-)URU%Y~F)z!zysBBaXaOtQ(8>(;lD-w?EtYJ4pq6 zXz{kywbxHXoYdBksp6@}#E+0~gpAqqV>9ujO2On&Z1CS-%ZC0-CsR-Q+5HL+07d_Tw`Vn_yD)E%LHpM|EOUu}- zEUkPRL?j8NCn_S99fPOd8&)?pjO%Ih2o{QN_PeN$f*)5<8(XoB z_pR(6bPg(9L?MJH%y0Z*Mt#;bBXrocu0sDa}j z=31&5s_L>FV&xet+Fjmv&HEo6C7IucB;i={cjNFL9~*uQ^b0(oPxcmW!`yiwN7?g9 zju04f;^$#)Vmf1$q+^1L@&&*DE~p+c553-uZky*h<~9kUC5PlB)A8vPwvhRT;CBoP z`oL&QI&+c?*^+Zc6oeqTd;r|(S19V@z~7B_)#G*?;0n>KrxjH!r}US(!pX7^(h)uS znwE)fMC_GeaSm*IygBjh$*3x>@9#ZStr>FS!tJaVk7A@Ycwc2fSq)!juY!j14RpUf zP}&>re{eH>oDlTveC@+TmDysT%rMvACspbeFO}{quSGPy@^L%QkOLF4%0Cjxs~w}8 zT{uF|bFDWkvwPDq_r$yi>^x=y(Y)+<8Y=Fq=!}XDLoW)>iNKF@szrWaCku$7CE;h% zPRnpT30q5e>9uL*Z0}*H7;&~{KL6Ui^&^fPDvDK$LtR(V?;%XAk}i$=r)lWDKtnz{ zIu(QB9BkOV#`yaa3ld0qC%>3GmmQT-GwrG>Kfp?%OG>qTYRGs>f}%e(#`E9w#g*}( zFzhOXJK8fmk%b)4E>F>N)>7T-?s)fR`>>>SCno=nQh28KJ>+XY!i`I>_PquIcJ4kg zU@iRA<@ya*6?v%P`QtzZPD}L(vnx9QwL)e$jwjSjLN|YWb_9d~o*{S-rc^tVQ{uir7P}`}lsh z2cN;0MT~qLbIK$3$?Mo@moRE$io0=4-t-_=N^9?1vVf;;?a&6JN88F-gHeM%@t5;V zF6!Qgimcb~f5;?u!li$yUUl$K@SdFE?yq&eq1QzXISToupj=RuHj-hT-GZKM>sCct z6OW@aHu=9KHZ*>yyXkSV?iJH!`VZ@Y44i_}*SMgjrSx92{Ro%W#Y~5*oExbF5g=DT zXgefh9gJoERR9!AFAjVV-fg}Fkhh{7Yy|!}aKIUxPpAdDh{>Lihs=VFN;-6N#IBfi zFiz>}3l?QeCaA#A7fB^}u%7j^c)kI80747>K`HX@M#N~wd-}t)WX6G?AiuEL$-T*M zFz%}CREX6RV$+2TQ`+SB5Ro-a^IFa2VaR9UJH#F;mdga4@PF5&;GREW$;ajg&h*B7 z6POZ|-{S>@#z--kmzITxulrLypwHz9|iCWzQ$_|=Z znYrEw(_B!4d8zEMrUD(+Vz@e}uGxiLrzSImFnk33T*|A^#Ri?3P(ei6N@|g@0kQu5 zx(`?Xp&M5}x+KO+S*5xzwiXTQl^iO=ae~U(=bSbAzE&BRskV@7|3KeA;V?uJeX4vA z2xBQ#d}@3Dyi+VM-y9B~d^?CijISJP!AnGLS_-KwWZfsQejj7T=X0j|7R>P!6}lrl zG&Kr5eG}v4JJVR=Vuurx%dv%b9ur-(ZB#RT0&!6YF;)L52=5VSRl!Vbc<_){z^Fa8 zjTB=PwFr+=Af=e zE};|tJG6928tyL%3eSbk+*~vRtZ|wgMRRKag)U-qF&zAWNTvsAsU5ZPnShi5wa zOnqEq4NbRU$>VdE@92zqj>uiv#ct8b6?{p#k+E!>&fy!YK5_2}-}zD7~b}bOBkq!Fnnw-TF+F z70h_2dr@U_*La+!#v+6@86V(t#IFZV9!#Q>)uc%&tBGwQfBVFczLXBa1P-fd&6#zq0C+Qfi_2Wl7c znL+?9GL$wSqD2w-3y7O9B|B4>WKmj7EiTv$i@j*rp}b?IRh6U@@!4sW(3kJMUvSJ9 zIi<8`>u6~A*Yh09!}n3DTQh=$rkVV~_skExa>zGYlibgooTwYsbT==Mm*X&f-^HDj zQ1fViW9F0zi{TcyY;Ww7Xn4RHq`cdAGvl<`^Oe79gQh>fv+ApS(QHYCY6D$h)Z-lVGDcHkhQnj&QUxr6<0{ zD=cu$mC_=f8N0sW&Yzvsvy_eVt&rE?c^uf!+SeXY{@_U~Sn2PuQM7;;5-m$pILzQ4 zsjjD04HHp4p>u$9PE8+$j{HQH`%((m0^*TuZOu(`*G!`0PvdR133D9`diI#Om#+R|Cltm+3U5kY6-nQ4ri+ur8a>J52 zuhAe+GITKtph+@w=!l{Ue50TvAYV)B7MPL;jK+a({&kvD_lA^`X5+sEs{eT994fpP zkjG`p$IB+dc>-Zd=*K(VIaspn#mNw-$E>twnB%VGY{LzO{KF}er1jR6F&;jfcE3}G zS>LzEGZ9?*J7tQxF$TI~SpQiCA>elD*{y8jiz|&T?2BxCC`rLK+fBonV#^A=r|0)+ zas+(e-7n{U@V0pSpMmmBRd*p~#pIRPG4|sm8E>m7fB+W|ae=7awO<-4Bh=;|M(c;s zZXYwjBb*!*p$)pl7Tcpcrks1aegm1O44jIao#c1LH8bfmP}iC%>t>?(o`sj47Y`|> zA3hjqrUU0j9fe4b_8RgbnepWnVx_YSVp|H%JmG3(j2+w`Lnx3wk686Z9kN=mIm-hI z8V@wEC;%v*l}Y&vw@J~;?-msuAtj$={h!F?EVc_ewleRI+PGP=CO~)l9k3PHABF2KT~bf5y>s`PJ#UT(>n1%8&vtZ zS7y~wM^eHNyFoNReX;w;5wAqq)LFU76%fXpWhHtBP5Fp`J7lI+;Wig@x|y~1$JD3N zmdG*N`KzHMdwP%}glRO-V14|U?vKKZT~d)^M73@w=g7t1^&%z)cYA~O22x8C%b5!& z@$P7IX`SZMXFW2naxIw9yTj6QhWUr%0vs#P_BKv-ta+@fhbG+~Y8MVse%rNdXP~^> zO13ui{x+(_2|M*sq`OKbIPGT?)TCuA@35c+@sq|Dua2WvuE}UpnaXFOSLQ8ETckek zEOWL5ufI4U@*=0ksK?vn$lQ$PUd7UO4bu3B$s<+uX;}swRFYulRp0$ERu~Zq_PcVX z`;98sgygIHLnAJYF1=7j?3{7#2_UD%hMXLf{!4c%VEtrfQiyab%{f@!vwDFQ%z{2e z*0X5QXPJ>VjK0475jlkrV!V9qFh3TNs;*2U#1y1)@ojufzf-IB?+f0sO16)g((&=Y zGQA+NPqep&xkTGoO4FDNNlCuKRGiJyixqvz-f230p39P^Z+)07+Ru_i$)XB z_%Yk!V$qvW>Ers&O>5)|(fWsie~UM*t@?9ub)tN#xt--I;rSZfzG{miky0$emMy+$ zJ$tJW&3jcB+gMDg!=BGG9iXZT&Hs?7ur-gjzAQ0(fQmSg_$eipx4-VR{_EmBs-O+k zZtzN5X+pJG%Q=aOB9#!iQ{25XL8^353cS4O7e0kjXmWISGC4JM;Uh=&@u#>LvYV-#k`Hob z)RUF4O2y84pg!q9@U%8bTTNg9()9fKMOdcWwLG921o^#~iWLDU-96rfKX!=-Z81&MO$}5TxDebCWGv zAqeLDT9OFCkJ zsj$LT3lxodM0h#%?BVjXHZi{Ds<7(Y)<*Alf;}lnbeV&{5GzMuLYC2px_rBsX;kO* zSa;O!#2!B*0cFaw=9sCAS|fsJSp?RyX7Qg`b6Sz{q#Iy~1O(r6h{hg0qqiV)MxHFH zRK2Knb_uq|A+<3#gw4kgR7U|0XjsaIZr-OX1~0W@UOt68kQoN zGaa-&oj|=?R$N+rHkiefg(K_yhncm+X1672AE3-WElC9v$zD+jpUp{r(+&=rD~l0K zIsE$zL!Y;?n*bRTQ;{U0`w|epJG5mzmDGhwEqLANl1FD@W>c5w0h6j@JTNPJMQan@ z;P&%K3b+cnoK?2f<&3N5Y<@Wx-c_49gM0~%sSPLccbC*Z$~ZiurPC;qNSGOhO1(AZ zPA9OaYqc!WJ)1#;mqDK~zQ+HJew)`F)m%%6{mCr0hChHB? zzvQt=51w&BvH8OLGNdzBlB1&D>~h1U`EVD>XRaX7}s% z4m-J_Z^nj4FUUD`%!K+bD}snqFd5M(8V}&VAsKU;rVag-z2Xvy-}?^kDUhSh2>JoG zGB`QgR_~fPA;tl`2kIXYQq@Qu_(7{)pod8zxlZ-HR0-%#O z;Hb ziY#Rh@z>uRGO^u1IU@x&HSyy06BQg=MC$eI_5z90Ih((B3EBr)n2~>zO4hQ6%@l9( ze?;P-q^ntsPkz@&MZ9=DvD54Op$*7IV5MNKcvA9tlt#X|d`RA^FA8Jm#%G9O3Pgp3 zP9jx?g5O@l>KYdzQ#ngLt1#pzj2MSudgT79wpJ0N+srgKb9Zg#*SwmA!^OrGNzxn3 zJYO%~*dpI!Ui$&l&Uv1cVED6$PN`U0UpEtHRIZ&htX(=M!FvYdRj~KhP!q7i5&j~# zoKjBwW*-xjf;NPs>SZ7IUGHF`xQ>zSa?(9Y(Qk+tmjlXrmH<;fJJi>}W1VijT7v#n z<86aCdEGK@;a7?mQ)D7FA9h;&I>lUPw>@c)=rHQ3F=oI6s!V2w#tGyv$I?j4`C!E* z7lXe^9CVWvTPpJub&NR`^7jDVB#hkmeQS6@BE)*qH8{w=g4`^8Sw#vNbo?oksOR~& z@nLkyjBpM4rLlr4*X#mVvwn$Fbx{nP*F8=}4k%>x$(dK`a0vK_g3YQ9h9duOAC$($ z$Wpdbjd;@t-n}s2Zm+l`yG%){x#=S@BstJe94ynxH?=*w9(^eNOO}9!mlttYHsaRl zUIz_|;;~MfPO3Q_*+Js4kl4(o$o@oa6{?h7$1Wx?j1r{PgO>OK1Mb8-qs*Q$^8&K? zp0KV+-gy6}l51(m^Kxvu=ykr+3sTAbH;Jc2q#DBjXYs+3-C=VTWx*2VoIy7(Zw))d zYjU(UgazuI&?RFAym$bipAuGYCb|J5mIP5qPKb!)4C_)f6R(g|OZq7n3*R@Fcj;GI z*!W<=i${%RYG(_GZfy=#b7Mm09FMpbM1j)qpHbJF8nn#%k{|N-p9OFt4Jl=-3K(3y^gZThdKc`dFc%`I-l=rXWw^?_y&MhPa?ZsY081ZhOtJg z__)xkY|y~|?o z&m^htHN$_K?U_nmh%~D!JySUmOb?i@$EI155halH3FClo#yn~+IOeVNkd@+Ig-2O# zR)O&}ey%tJ2Dm$3d>dB9(YQi#^B3-u`I3V2Bm7uX<$o-SJ?bOuTwOIh?crk!35?LU zc!jxrbANx4jb{JY;b*{dlkoG@JbJ2!DNq-!PKiqIz9lCOxlOhf z4g)Cl1fQ(L!#BT4bq`P;_YpO3N;r8B2bLYI?gs+5{$r8zCcJK>N(Mr=QJ*xwm&-m& z)d~gs$UT*y{{~06_@W!N&#!a@G&scN&Q0_;zFviKD}%7aSPe*>OP1OzkM^(s!tCN# zR|2wI4A=0oEZfIyigNr~n-UiaqrME_fuY0f5ZvZBh^!6Ur9P|5OlF~U0{4xB)h zpkVQe7^sQOKbw}s%q>5_m5;+w48G-JZ9gVVe=nxayiC6*dI zm*+;HFw)N&$y3BhmNSQ=Q*1}NYJz$Hw9)U0UD)68oGC}_^bgaSkFFrS6T5|XpP7&a zbj3@`(f5COW}C(3zE4N4%6pe%)yNJAbukFYux~T{!hKPtQoQt{DAJwi zjx|5(c!M3RdhsUqti9@Y$n9Kkl7vp^K2WjI1NQ}fdTA{hrE7&GJ2t?bLL9=KKKlu% z=}*ZxDloZ(r3@*swbeC2+bSJKJ_{0&Jb5?hQFdBA5zTe!CGw148cmlCGwTL2XPwf+ zEM^NCFE3aTh{xCAl$Iy|DxZYBV;QajsvVLN1}}}+8xkbYS8b;=e3A9$0lcSV4~1yU zsF6amaVE5)_F^&${$c!Z?$3$KqA$N?kO$aJu}ug?sY7%}A;DaeaC>asDx;Moq`BCm z#7y=#93vTHg$`Vr!?@<3quBW85e#7`4cE=PBZklqCKMUC4cf!j@AlNKsCbEhGSHRO zZ~$}brovj>KM;P5ceL`xf>^+^_1d#CuBPSjSV)qHU;J%4=lTvaefq9&v>s_LrPvGx z_7+!{;fyo~8JTMOmoeRGnf`!NVB*a4uN#JSfmHQ5LOJtGmAeXd0uiR*2w5L}4gXOy zzn|8Bp$UaHu``;0>BMHI`UxRm96X1I>w2)X=S!1d(A8CvA|*SG#{#m8Y`)H_vxq>g zU=wBj6jMX5VO`ua1$LCG5hn~0{u_1hCh?bs6|GRkK4*>*<6{T-EPGcXd_vCr5Q@5N zX8?)S4}VUV91UnU8y$k5NXN5msiAbqW4@PlOtW{iA%wohmxdWW7j_FkoZ4J@JAu{Z zl)O71%`Zby+Mxx?U_(K=57$WIICfLr7W69**)_(;-!T*Xx+#B?E?v6x46BH#q--=(FIZGQH4>O9(6FF$I~1IhK$$yQydokn)iLZ zwbGR5)K4LF#L_B%=cv#FeBAaos0W-r^lIO2HxfwOIm&t*^h-J1IbToi5ldFt?V|*W z4=}5z6_k{QRonVHeUx+^S{Bu85P)Dx0YWCei4d-EZhkl=cXH zh`h3)#|ELbvYZ%+ukUzK&=zHkoEViJuSK^K-D1S9%+|ft1mhntk-I1h?5fm z!_4%=h(^wwCDdpFW;3pI#f`iuLh>FgGNg;sVT5*;?S0G==BBF3^uUw`ftuaN0Rr#U z;ciwcL*F_X1VgK9d~Ni(7}0;qXqbpW%~ptEKG|lu7DaM z|CH=e3g{Ch!SxHz4hu`JfdPR)!vymehvDIoGo;}<-a-941W5fj!H^`-9Lq@f z-#ZNC|CXHn{(ObF7Ip{io1KjZ>+S~~yHsStfk1#h_E-9=_;iLzYQW_O3kDXL_x_m> zAo&;f_iZdF*o;L3$}%Bs5OD3Nyp0L+$B@R{j|Btv;ntrQ#Fqnc8oFKV>r^f@7Tw?! zLF{vfMT9$V+JK2>dvzNsP-DRlfOq?2HRj8^!Jy@;zu9}$g`6hNS|mQ zQ2fUBs!oRU?#7G*Ceq^Nx8uv)g5HCA2nC5#at4>{tpokJ3_^e}VP zd~oczT^RVa)uDoTZ|vqEl9B2$BV#DTz&gDxP+Y60pZTZ-@|BRRZ{#{)#9yoXlrb~W z?0fEg_yJkfbOipv#-G8_%q@X?)!zv^1pLe#Qj57%WgZk}8^f-NGte<0QK0>WmHYZY zN~l3j;eSDWYW2-e0pPD|FKgZ#5+Fn1JD?67b`W?VPCbpBK+e zWF3FcK?467FBPeAGOu&am&{7`h@Z8<^qBSi5RbL|`#`3vkFJ>7t>bt|w(Wesrhyz* z#ie;!xwZJO>g=z_&CZBxNCSw%LXZHm-98Y**cKRJWgR#a;Cf3u*kC#5x8G;jLI58b z$Sg4L)u{w5oZl5AxPKC$>Gc3kC*)NN@Q~N<_=!58D> z7sHQ@waE)E|09K=uMiN|kk*%vAFavNT&JEG_b5x?pkMlBkoPg5i-Ej79UEUu<-fu7 zV}e-MPqY?+0Duqzp%m%vH!n_~BFd4F>Rrgj`O-jZ@3x{C1lr?Sd@B;D|L3rqfEmit zSpwrOHN3-LTG- zvX0)@HMBZZaB5z2GSZldMwLf5iq+=XZVrs_rLGc%SiA*QojZuTMD#fojh+W}voemQS+~Uo<->%eLVuK`oZNiV z17$QhwB0lMBez6hWs;+tV8!>-!y!=39RdHf9#u1!kjV7~ri6AE*p~Ttockoj1S7x| zHGhQ9%WW16p%zE%>YQRuEn?PKg93<^$yZ}Z7F2g!r`goA8-zGzi)1Ob!K#blVpN-S zrj(dV$$n{%afhh2;A7~~A<(}TIXo0l@2m3oNICwKHB5FZ`#)@*Q+t@fqDEtzZEQ5Q zZJUj4TVJfkwi-K)ZQE{a+sWzP=jL3T|1cLb@2qF7>bC*%%RBWNUWp_JtRkDRMden@ zD!wW0FyW91B2ch#(^F@Cr?vZxfx}aS-*cCQB8qL?Jbj6e+l+$3SwPfTRVz|k-uhp# zc0V;9aB1$_M=_613Em|a)eyJyD=E0RAcXSVQZ{ugoBLRm1`iC^PK=c#LOe}HkbkZM zGWSy?9{$P zZjm#sToQFr^*m*T&DeqAnhwO4bTr;?glv}ZFrh~fM$@rk-)2<0jhb*TJU zn+Zg(5cayzI8`qf431meE8Uwr)j|9NVou1TqM*%&ZwI~w<+~ZCTvAa_?DDNt;y+l7 z_$YYlh`Y1?27Ba<*jJiVVU1P^1O3M^FgBOu~OFjB$*#ImOZET4Zxu z#+_$a2%uyl;Q+XJt|?h>uc)z<<>v`1D}Nx;3srVV=NrbY{nKxpx6s~ZRZI+Qe!Coa zx^}w(5XZ zBJAl+Vxo=D14G(E;QOQLc(fbYX|iJ^i%X9cgyleyve`k2L=A z44Z{xIvmIXde*-hX~2@h)Qvv+9EqTe+L&@#x@zW1K`TDdsjb*Ja_q|%5IM->0api0 z2*V0N845@xtp57*dt}k0Ib`*LqtJqZ!amM)-41*WE%6%A-2J(6&?Tk}6)ky!+SiqN zU2k~OKekpLLwpyj1)s*LwW7L1iD>+y3mck*n>t_|u$Zy+m$UA>iuynX)pnmpYKoFS zwlm*Q3Yw~_4_1KZMi3TNL1E>-0g@(L%~V<86aj%E0lzU`3FS4QP+CeY2 z_n|;Y_@${+5w<|VeGNtS@PC_xv1LL___&MC^rH>DkRZF z3q1`|#H*1KZSJpbt!vdhhq~Twp^L6CvIFeA z3k8Raw@SF&n4{kfV@@8i;-fe1HoOEyvfP(gt)#kYQ+-U5+AHt)e<6Q{Y*VIR?J(j0 z`msIP#FxA$^x1l2mb;#~yn}(mAxB274wCUMc&Z#Fry;i9ZDLi$S90^_V;B(~~T9rW^@~fnx{A!_P9i;eR0?WA)lp!D51ztAb z^4X70fe00+2sxK=S@xCw#?I@k0P-EWp_L$@CWDiE3hbZ$%fao4uV2QC+0x!!+HhCj zXTHz|@VKNV05IFbg0J7VbNq8OAb9Psxkr?+uLC*SY+eXpFLCVoq+URfH=DyNNokGS z_7LG`vcnEhv@Nv5y4#0&1-)A1V+7W`a%~d@t}cybShNo%KN(6>+z7p*0&|t2wQg0K zwRoALRM$qt2XmBf9u2^j`(8^_8|0qBt4D{C|L&^T0K3+EkHMQk$hi(eetNf~gOj;WTmqQ$>; zjKILH;bDJIJ<|2Hq|hvL8;!271`Z?m(S!IB85uIBvh_0kmv^ccjwZ@B@B~cI?!MV& zsIt}K11<`b=!N{KrtOq`3*Dn;nM<_Y+!=1yQCqrsYF(J$nrRGau}V$WM1mnrHC^z1 z*t<3x-kt?#-O6f^_kRd;)F1q1@p(-*&SE!xJc(w1-5>dBWOimP`XF|-byLI4AVqG0 zg6vM=43j+7=@3hZGw-JT^m-~)mHiw}@6z}44UmDqBr*~pe}y70d~zW;x%B;d_nrd+DT}-u~SkF;@ZAu^Z&ejGN`umwL7G zEl=*qBSs(7PcQV3eqp7p|jjG+*xlsPR=NV z!H{V2FU2HBIIG3ofd5Y3t8pLF&My&KP^F#dq}{Gn8OdvLpY+1FlF;07!3+(7I|R5{ zHr6}5N3TOj+9;Tr>jn5J~W8j^JsFTB@Nu>TA2b~m_cn?Jrm^#g*Fr#80<$IQxDFeugAn<4Po%8i+y5>!fK4_0pG*Fo@3sC#mg{nFQpMxL0_Sj@7nM6fqIyy1DBFstdz_C-&jGh$0 zx3v$O$5BlxFT$2w-CL+{Ap8a7YIlGWBg5=?zma)eM3!~OiebpSHo~>o=YdHLCPM9( z&}GuU=DkaOTkV@Y<0e0p?i3L31<(Z}1+arBt<%`o<9nT-%?#9_1u}bj_5E@NfsJy3 zZovuPXB!x0*R0D@ZQSJ;>108>^lgncg1K&n?q%Eef$GhF+F+s|o#j?{#}mU$4@z55 zqU&pCM?)8bwh9x&P4`atJHds*BnD|F9CpRX;wng`KC%tTs#{dsrQ<9HK+gS3-$lNP z-Pc{H>1P+h?;)(%9VH{xC(@5tDftS9ADAgd=I@*{czg`S`u50ULmM@C4CYxN4Pgr@ zpHxv*ZB+sbsL6EPIoX~ktrNuEF6Ck-D~iVhBKv;>HTNOsTPFD;*=0%vSb~}S>g*NL z1djVp34JYW2*S9;ZVT zThAWL3iHUoO=iNIJtQvjjOk{%Z@<_l%c(o)P5*%d1w*O1Z9wU+=5jyxi`Qvui$vLZ z&0WM&V>@OhS#XkSN+PLM=w~bI9tNs~A@`)tCYmQpkKQy~Zi74k^a${=bi&Y#j-~I= z;~PVcCKlR0UV0K4&Pw+bRX;yPN{ByESyAdoWH-hQG`)dJxCrG|;#`{gB6f$N-zb-Z zEvHI>N>G8m)kiaq4+}mfhE)8_^}t4OlhoH5)s#r>j-^6<7~*kb-JdIGs{vcG38r9$&6TD} zJoIVfz^o(9!ewewbd%zS+{GY`75UcyDdB6#c&@9u10k*`u=XZ75G$F&ms4_;<4$c6 z6HzrXq1iZxd$PgCUfOCY&l>Y-XGVQBvbZBPEt5U^d251`lvg1jG(k;j zMK}kw#XvrGj8P`x7m%63&HTwaj&dq}m)%^4paG%Lhqvx+Fkg2BfInX4K3T9O-S9X(G@#09~m&Jp{@?XqYv_k*3l|x=@ z&g=WMdH>U8$U3a(p6v`_jif`J%r4p{p^I5SJhmg%Gv-Idu4N(&jfFG}>NBZOkM~UX zG;^H#)dLMo(U@g74~z}%oJtq(+EMqdr%LI}Acx5{5NIss?%3Z@B= ze5>v_kgFW9VS1m$@4@l1)HYq5n9DxlIC=IjI4yw)F?~5Gu06ZKF)Gs)8Gy#pv@Y@V zynDsU`D-LO%$CwmXsG*SB`OnpNtY@O%V3|J!D&9&(uIl(t1#`h$}t2#A^gxZ1$cZC1`R}EJ% zIK9p^*<9%`E}e9TyrB*F_VD)iW{`STit-l*MLs4at3AnK(6^XcEC3r{5#9nlMCC>h z=#-a?;v8tRx|aEq`^Q-wnR>$KiB}u-+5rNQwxb*eU zQC=Bllw{=sU9Zx9t+cK3E}U3j-m4PBv>oZQP7UR9tyPi~U(agldJcOE&7ql&j=jWyV<)d=!G*(^fp^RQp;4UPv=Gu5@ zXO#b)-atTnM!jb|tF0^#ROdDzq5Raei2DZ9&`x51qZZXOIF`6md~~)D_?-}UA=7cb z($w);N#4YY6S;)tVUwOI=`m?z9{A=J|Nf$>d+fonpvQ&3U+s(bPxQHCMB^By_ zG-Xoc16z+*xQFUxkKx=)u|bH*w43{gs^kI3cUtDK7LuF_oS1Z=1^vt|Izs>wf6xizw#E;EjGef`G?)WsO@^WpV z+#6rxFWI}uOrZJ34kXwY_`Gpe$xz?B8epPAU&?)+r=v!lwv5%y_Y>-@=rA1i|1ra& zI|ZLoI9HaoaOX_x270p1_NBjPaucrpTK|eq#J>GFY)KlJZ~-mHMU~67L9M`OA1%lB zE}1mS{^@TpqG)whggPA_ypJd-h5VL_|P+Zi zBk{O(3JrTKA2(OGY`NOKfs2cXOdvnEYBZ8|HkAn#&~Jj!m@c2JCn=Ax{r=*#OzajK zGTJ;;?R`$l2c+vy9n>dJdNCjJI9up$oe%YguM1NYtO>aL6zef+dsy?-`KHc(v7-3d zHUHeBWm5vLA!ZMhxI7KzEH52Xz~5Beqt0YxPR zZoNDV>*7*VYBeuY6~Hh?k2DF>kA57N+O?I5c^&eH9k6_LR;-ZtsA;#kgt%H%crqQ- zmi0Vy`GX|8SjYxl)Yd@XDigS{NHppD7yCB;vZ4S(;KqPd28SZp22HgLb>EcvtU8+0 z^}@$oeqOu;b?O&*u*4YTfo!#F`MbIzgP6_bU`m#$`j)hA0t*)(9$Lml5JETMIQ2Y@ zQ|8{K7LWvmAu#^R{nLX5%W4+vJFQOFX=sJ-O@*5yh4ZIr(W)|a{aa?T-`vu9Y%F9% z#o(=X;<;%8j6hJ@ND;a#wXm@LH>zUifw283qMcT5_&@G3%V#8+$#{+T*&ljB_TSNl z<=z)4oKBW>vw-OiGY=B{zj@hvDqT_|qo%Jf7cJcd zG8DQOw8W6W$atvTs@pJ(=B%PsRs*F2%u$~bkHT21`89s}Y(p6y%i zB*6KfTq7+aCIp+v;)!Wyh1~ezBbFBFX!=B+s2BCw#CXwe`M7J(ryVB=$yP(oHiDxFAnp*JEtS;Ea^Wn6x=y8jJOw1RL@9~Ga8~^n#icjVjhJZ%MhpImfv7Wr~;<;YUt{D;n%jj*}Wcy%_ z-hUkvCr?Fn*@9zl_ATT)!y6=34HMs-jE23W0n*^HHWEzpugr`yze#Cs5p97T?12ki z{czDIk6#iLyM9E`{0c9Dy2nQdN2`?&>6co?e#sm~QE7}vn_$ll&pc?2S8pD zPfC4;@h>lABZmQ^%hY+72T$RjJQabC{ipW*^_9|SkbFj(2P6=D*D{Uu4UQh^K?l@N zG`V(chZDGbbw*$l2Y)We6bc9n_O7zdesG+vJGXwfVLmCr3f$PkyMYn#QD88v=afw{O{r(tXwX8uG2lF+-zB68WAuZR~y$(0K$U zIft23Ekt>%{ZM2l7?x9E-5k145q_OH_=tX2_ZIR?7|C@@txH`Yocgyv&+*xIFY!}q z`N~O_OXALWSS8*F1(&bg_X8X5p3Zl%1j>ndizV6fh2>qX2v-K% zR3YakW8&J{QP(`XBBnl`V0=2bA?!b2P?x?QBQM|=iM%YYs)KKV3U`r|WVS4~Ue8=4yb*jllR_~>H?0%oh3h-=b;Na$?ZuXWt#~Szx2s3s-2l z)aFCR!0i{ff6xURT&hYs7%CVi+kXp@XiRJ@|Cd!@Wo1u9oyUOSWc@F!@B{eADp;?x z2THob!q~MLh9VWU<64HMgNCN%VU1Ep|3bqvr#bXG;TE>gV+XW!=9d#UKJ$)^| zb~P?%bk?+A9d%urR~Z`4EbYYyu-*$D6oSUG6 z82*e?DCrlY`d7a{?(>}k2qVL@z|QCWCnik5`vpRPu#)@~N+B|sCHx8z-j07t0e4M% zGYNmKc9rt^^07ofJ-|A-cGUCp2g+!OW zpbn*A;m(A)zi5j` zkJ|oMLo4deCtZC=JGd|riX;XZT#qhD3&%ptl?IqEq4~2tIq;nNTpR`>V4lQc;5-k- z3t|Y~?YZ;XD#W|v-(CUD+jEs036SbAe)A{JyYy$6BDg4F6^uRHS*KMdU=-*}31+#&jQ<5xo%i-vcYHvlmRavJ<#Q$tnqSGY?(=0HOSeI^2a<7Phq zGc~~Jv%#0Yfj{ymG8N4A>IXO+s82W`Xy`xvNq9{W+b@v!t>@^T17eFz_!ImV+-ItL zr<*&>tEKI)4p{e#r!y_Ev&DggcZnFbE3Z(HhnD}?m~UIM$lRdETS1D<=*}!pcykwp zp=Hqi^uwn?M|y62GF^#ACCd+9y`lDMAqjBhn*5o@ef_hnUH^*IW5QYAvH!8lwQv97 zrs~F|JJCBTQFPJ*Qrz;-{VsWRr^T8J(||6gGUK`)9~KGFw8oK}*r_=xgDIxkzsAbb zRxabbKz$YQ-S4aX>~pP6A5c5KrvLe=QSuyjpjlt)GD$1m2#s&vBBiG`p#KW>b_7WN zopT+AsPN*~+B8LSr&NkN?xti2S2N|*kbGw89=Q97RhHX`mX24HBMw?&>Q%g9V&WlC zy}S{oLW>#;(Hl=B{O)X5+j({-QFE>WK2b5DSEp=bHo{3K&sv?O#H&ogH<}%ZDQxDg z^NhJkUg2a0lb`svv5(CXuE#t)Z3~cBvwpBFPUo9HPSPG9bs$F+Zw2!^!m z{n4S%`u$xbsvt|O=xlyFFGA)-&;KP|98Zl{*w|M06du%TOzpWvUYGjNVg=Z#j`w1( zF4(Nk-mM93{xHoMAI!ebZKkTDn@dfS5oNG?yumdXU;N?Cim0_r#!vUihRoXEqFbXQ zQMWxC5b~GETDA90bs<>R31a1?`sDujZsVALZX4YB?pF8!duFhy7o}M78Jus~(sTAK z%jlcgrVGHL@m+*H34B^6AM?d5GhA?~dy^KHGmC+BpHS{J~FKbnCoF|2K2 zoTt#*gmtVWT@#tICpn-~gR)k(YDSbB0ZU2?%v$J>i6AID6+c_`r8WjD!x9@kq4U7+ z`~ysBeWyIruYEk>|G1cXC#;6O>^sCmgW@%xfP6hJ^ia~FiVP2)`K=@tO0Xt*PwZ*_ zd~WIQER@qsEo7?znGUFY?X-iT&)v&vJ43QiDG_pe9W16Vsky=eGwc>JOLxv+l&S5v zJ0=A#gkju}-ybI*qpUl+Yhk_F#v+Z*AA?AX_tj>71VOg~@REDiQpcF{)~VkJCoRJG z`@@O8(aY?bPS zNouiobkiDhI|8P|9#UYFoA%4&H)0G_1d61}cMgKI8Ft)5e?C^4W@H$SZ%LZ@C=YS| zP;KoR`xFfR16DG|_YrFObitmZ%^>A4bXBosulHH)UN&pjWuz_FAlo8ic{0Ym{yVcu zk4w8XoxL|K_aTO&gIne{Y+pfsCQ%GXfK_DZ!}(#5sRT$c66Db}@_Z$*y~IdxU&sc5 zlt0+$w)Dl7{3O{&PcO)7@()!XzS9Wj+Uks6W>r3AgdiOc^$4iEfYQWzkUB^|o^CwbP6JELx?;QS(9pwb#| ztMS{7Iz5n~rVz<{ADu6=eZxlb^K86KgD5sQ-xflC)f{DtG|AbtX0Rl5xKO>o=&F)9 zxn5&5PAg{6yfKnIa%s`f)sa}^>Mmx&%CyGkeTs%ax>-AwcYEEIMRS^Us6rfZZ3WI* z@xZOCg;AUIFD_K1#WJTwRt=U;E`GO-K&F{NHXcC!lq+~|g#U3YM%*Bxh^jZwa(}PQ zB^q&p&mMdG&&-1^@(8~TnM5Fm6kg=Rp~T>s(F!jBTbUC?CWdSV zaQ>553xDfeT&-4S?;x2&16CJxU8<+JUbO z;yWYMC!(LHj;9M^>PJNV$63BfXsBf~Y6BbnMMfE?@w~?LhDg+}e><`9aa8cR%T+Rz zr|f3fWmw8vtvA%y4dgiRehR@K%KxbNgg8l$Mf5crx9;>|(@2XFSL4MO1BucddF?z( z^QuIo7g%=hOOS}d9+olnyM(e6lBxwMZ~r zg2$M7MTwg`N0anL*LIuel;Igu#YH`Rwshf*zhE+Mr&h{jnJCJ9{Z1tuOah;;J}Z`BwU)Xo zUx^Q!bMQsvCOPiy@aZX4t@*K*pHP3rHSfWmSsLC6jjl|kPD1+5Nz%3>3NTD>fWdWt zj?-`b?$l6QMHelF)}k8!0SDm7j9`d_)|^bi#Sb~@?0S7^4^;e2$R8KU?J5w1Wto7^ zFb!4pZ%cnS)$4S9U&!S7as!!m&XL4ST-vpCNbU=OBOLYqg0jbns|cRLK2{vhw6mmH zDJ%p8>8jSt7;7qF?mquTu>y$Cf<_F37u1I1GpOP{Js+2ZxeViYuokJ-@zJ`|9V(Pm zYX>k64s%4VX(sP@Y)j}LH)3yWuV|U_QLw0BDpBO^7D02 z6;GV6I6hM=-Gs0jm}=p$j-VCxI5-z2Q6~^x=rq6SFaV#-sMY2)x)i-UG3pXqZLmQ{ zO7ZmlnLWGTw|H$7-8nsLc34blp`ac?dfW5QSn#hDJzrS`um14d!Sxs9K-9gGjZgd- zYmJ!Rda?L0rv)JmQ>6Lf-_m#{`bIE&FY9D~pUED+nQ+Pv{F6F>W-BO_QRn`EEhpS=Ft?O+P|NzT1h zKL|$V1*87t0G2>%Y<4vH$%Tfq%Trd=a1COJg%oi7z~Vhen=5??Pa4beJL!6jm{`~kz(-yiAQt0hd)NuH@MHC5P zA+65Ry*_YeZdCI6gt)-#R~Bc}^04kg2|pYP_}NFnxB|BJ@&k+PTheJPg0HrZk!42&ym9?DYTj?IDRbRNh zmh~@<=02p6+yxu7g|&C3=jV{yD)ac+UO%Vun7X402nJevc)@mvw#r{kv>LOVh_^(- z*xYC?_S(3r|4!<4g8#-0do3hqOr2f zBGm_aJXlWX-toN*YAX2Gt2FtW>4q6NyCO?EK!BwgY76OvG@TUYgtdpmn8*dT%0sH2 zl}}_Ak<{6c&}Z(MV$yrYn6KpVho8tJqzp(780s(!efRn&4ra4As}ocd7eO<<>Bi|A z3LhO|Jc+8c2(I#qpY>@;`=OfXfp%`STb^nH{lVWQ+A9d(ve^}9 z-~cU@ykzx915Q;T`g^0dY)N?vrzTn%7XLmi7~OuxVkbdV1Ewd>^fay*sdG}h3!?a) zal>ua)0I=R?A1d%7pKuSIuC25-U%BeLUX#!gjBI8kFLFhT@yaSxcIFl#ITxYvb5D9 z?K5xr%Eip~nfaPQm~%pOv$q3ZUCG=Qa6h|)5b=W!3n@OM;iakSvB3HKQj**ud$D;X zxI`JTXghLhD#_n1V%RmZla`zC(Q9=7offu@Z*bCLnKYg2pALa|t%VVSRKwb|;n6Pd z`1MKD)4BTC9mN*=x54;OzkN#AXbLaC;cPwEMgg~<3X%rj_rQ3*Ed}475v&%C0DHf@ z++Jl_Li1D7Tl)lp-_l|5V6dAu zi^L{)@?93ouYV{vzPn?X7osg;6Vu^AP|xegPBn>AhbHn=UIc0?*$R_#+pmRgyGeSn_rUY)T;yGVzo5 z%e`)1b#Dq=(^n@jQ!tFS;buGBn?(>cW!^pZ+a)FnvVb5(;@CCxC0s=YqSc*o-a>~? zg#LTgrf2)2dXr?hKROk~v7mJLi>_}jd-2Oo7n z-TnE8qQHBgr}aOanTKHmzjN2S*)6-gY|xez!JT*Z1DMPDkuGER_~@bZBYJNbKBOtI179cYW4ic4dM`RcOC?ZPf0gQ$}eJf*Yu} zV29hEQcMpi#OUV$YqOD)zBkDSRf_^KhI4--_+ccf{haq5@bH+mxh*yMzsf&k{3>~ZtU)exer zA-UQAO3oKV)WdC@O(H7cy!8wF>GJp3yKx*5CsZp?9%8jUTtL?-n?<=se zKX96Nx6}N0d8OCnb3sE_I4@U+i7}Y!QQX(>Y5A>9fW=OYYyDX3`p_AXv3&^Y)OF&W zWBL602|}SWed-M@85chCGa)u~b@=ezIG3Q^qikc3R9PEaT^hUy)*Bgjvj|Uj?}g1zMPqU%(!qN%*k1oIO02Lp|ExDB zZv{i&cWv(kpCVI0iNL-n8m_ zq_e5@fSjW>m?h$;EaA#X`(SrK)F7j;U{p>CL!-wYMUJciI)l{-k=!rNjfPM;J`L_u#HurX&?W4$R;RWO)cPRY0 zKmAyaryzY9MGDJp#PHprTjs_Eqjcrdz@b;hYLmNRceSJ-U1jZL7@-X$(pQ7}!` zDiw|nD^DX)$0(b&!iCE&SlU64l_w2eCCL|U?EOKH2gsrttfiZH_>P%BXh{?vH#ZY= z0QEcUAO*idgO0ZB(O~|GP5GeQyDo(*K60m<^=p(grBa(-lcW%3{0tDB18x&dvWb7^ zGh(8VkboJ#W-xd}8fO6Kx3eJWLBVOh*ZEd-P`G1%esINO%mou2)SHic1{wup`_44r2W!`FfmKv=0&? z6qogmJs3}ej_9tPHTFcK=*jVV8?P>L$bfzA^F<8>@()}{rQs{9@0lgpaV5h9BAs#;vhlG$J_X^fC7H<2@KfT8A`^9o4{@GFjA52H|0KP3( zCq6Gmj;5OH;33{8Jev(IE&plT4~p1EOM-Vcf5C{z>ludi5FUlmGIug7M#WE04;VKw zzvRa5U=f-if#>Xz&8L}od8Tkw#Wt)CBY!%S7vT)nzq>ayD%1IHoiV+c0uM&Bfc1OlHdmM3uocF_UGddbDNnY5ra z6Rt1gpF=OhNZ>MG@98cRLpg@~4=4}x-9wH*xftgh)I7&>D3a#Pt%OztP-B=KI*)~| zENc<5zBx?3kySuLLWe8E<)uj&;FePrgvHEr1J6M1tQELaNN|mpS2CL1`FeW-CzPKwfJxjxN7q1r4JmjTcWB*B?z%Kj;b}#e>Syk?*_({jUr1S# zf0Oe9oz7$V5^JZm+uE7yz~z#rKyChws36HEBTCA8SzNk?tK<2 z6H&a`=uacP@e4iXt)vU`VXktdKoI+h2;yYZ=4;PA*AoHZtmnrd@GFp*i97Ic^Zjii zXSk7k`pV6i8}C<4$LM5@VO~3*&ftT=T>DVdHmZ%TpiN%b(TRk}D?U?_1Ev_Okb{!Z z3&%-&Ln6HvI3I0nO`xVU0fYXU4K_%_;(NSHynyoMvevXja&WukN5#X8`Z*T298MFU_UF?D2=#b>qLWcDr}Ta zynBl}J&RMT@CO+`;`@YI>`&fxFe6W>_Km1jzQY%sy5kqoc!KGbAM?$i*<9fv!*PW~ za!;k`Yku%BKdidj1?Q>AQ2FR%w;eK8nuu9amalMNFpSuggs+ zpWBS6Rrx3Vg@sG(hBZ9rUE54A$T_`VJ1o(Xs8nqNBdkQvB6c=`Z)o4C$8D%q<`MDceI>5~IsVPVOT*`T(u<#8)#yf)(cq4M; zME+TB>N#@#osd(xx1b{H#{?Xn9m^ay*qc@O?oq7Gfa#fR&O}}x@!B)Ze~!K(%?Q$# zpTREuvt+BFD0P=-8s=UqW`fI5D0cYvS2ZzCLhCGBPQ{B-Ue^4~4{2>d9c*#Qet0VB z>|Y?-zYhqtI7NTHk&PTj8+{#o#5ohWmozmD^gHG6n%t@Hmu;lh2~p7B5Gzf5&Kr!G z=Q?l~0P=1LD{gaCRXKL6N!Nz_rTgpBMzPu@YYNck#UWctCJpx!49Vg{yDcH*CA+3U z`iTk2A*L+eS6{WrW$3L>aa!S#L7Y0aLCck@TqYLd)o&dcf{N~KvuEcYw9cecQ%X`w zaJ^Md^mGrUh&ZpkMOW{0pj#O#Xu{CpP7DlhfJ!p#4e9$>Hd~p>vG&l0+g4^Q3>Ieua|24ihvW@ZVj!?4wY-p0`x$HP!p>H_rCN=Tgiyw~ zNQs$2OThcn?@HkMcZRrmlG@7-3Nd1fkOq|_n2r~FJACi*i^uGVa~@v7`0zKA56~<$ zfF*dHW4X*pm)k84-%>=gPGb3^iKLIt8#P&-Bfq@Wpm+d0cg7FHWy)E&xv&`Tx@|AV zo3}F?nMO;K$rHDyuFHC{Mt~@ zJY5f3u8xr^Z&S7@4@F}rt>!wY%P@UcBWQdvU|LMTFQ!mW z?T|L5bMl5;N@~Wi^u@`r9ml?}8tm=u-kWk@su)m3Qa1sa5BVKqfP7&4!(lNR|a{+uvT z;A#f9z{yd7u4vwps66p<0Bq3&dcX$76Ulbh8qr`vuAB?4*3vqLB(Z0sitwKSrcaA- zHDnoM3e8&-VSq1DXMxvcHbyLi<5?{BMfAI`V04#>UcmV->BN4?-=o=skAhypRi4)X zdnVxz76-;ECLdAWOHa?7$a@a=zcIJhB&+MLlDt%Uk8*HBDTiDmKwVS(eF5z>FAoPk ztYlgptUm$G;M?|JX3uoyAEL$&DP%mZuO1mc?osce0uYqWdPjzttSE&H@LYX5lhkbz z7VkDX-m58lHwq%JW0dR~&a9|ebVD5z3{>xB4rAAhurUT@!*&MQuaG5*z%Ml|U=(Yt zn=^+?=x4NGY$NxFbO>7c9CS|w_L#X7@tZFctENy{Cr{uQ%+D$K$1L~q1)Ufnq=(hVH82w#Glg*BtnFfY;N#x zC}%#>Zko4)3y2~ONz#+ZNZzNXnG7`8K`$575$+>+(7>Y{vJ2F z(T)L6o*)1h=pVs(_s!m!0{Ln3-2e!u`}PvBcV>0~3Pq%|+56e`W%>LB7ZwI>01iqF zvSh#mgK(64EcwmmZ5d}3&j*uu_#pqrGS+3gUX2{r(4^BYpg|NKjNQorT1#xqFRz<{m=cYhf%=pGF0YdjQ>Iga!e%TYx7 zt{FBeD<3#Y4hz_3?+%UD5JWLJIJG*64mxLZA_uuX5RxxKTsdn_1KrSJ-*FE?;lNs|s&u}ZF;AM5SD=l zkMIZ<RE*@{yx zGL|;4AB|*a&6h_^&iYZY9bS%knRe#}FQ+TzBT(e~*JX}43xqOs@v}p(|P0&r4Qw(RX!-abIw%&hyExPG++j=Il2XF$EoDnZDx-=IE+#9zp?$5Q<8UN?!)8eUp@pF7Z>!FN)q+FLF7I)a#j z#7BiLZzOq;f;1^trO6G2Nk-sYwmdD7o}$<(f@vAeQfT?-bm2f-(jcFEgPnhdZ14lk zfDeuCb#Q@MiM*cW6Kac%9K9dhc#oQsqwP6rG8kkSt8Gxuhcmxb^Fi}_(aC557OOPBiMs-s{^_I&1irZzNq8 z^$Z%F+BFrpZt>~^O~k!>qAf^q)vbsJTXAZhcu{^0juze^co_?VJGS}B{{rh)qe=ELC zg)%5wTIZ>-7kU*HX3-QPS;!Sci{o@{^XFiEHZye{LDM{C}GvSd$P z>SM9g+**l|&rdnR2G&a}J5A{;e>A3LlTYd#-1pFtO`9)9&()T*$DzQVma^c;8zdXa zCFpQ`NET;p3*C}dzSJ^Dt*FcLT*xLau~8mU@^NGVUGRl^{Y<2d84BLl9oE-UuCw)3 z9V;JoqYQt?JsdTzZZA0C-uLKE7&j?s`8iM9tj4HWAhnmJ6dIB8P<&a7IbP+}$frP$ zq#2F@hI)E?{Ze(wv${RW)f8v8*)Smw{5f)oPg*=~{_BbaDN;om$pYp_d}+~on{~pr ztq}l9Z|#!E&=yVBuzI{IpwRT!DMtKo>@Y>#qN{&o9iLakr>C|%zL68=L~{0@&%`$h zeY3LGWYR`2BH;CsfAGrxiE)Pe7&a{b5Lb+7#XvVm`;v z#1wzKd3s_-Bn(YU*l2QVVG0ZD=1yE&GiS}F)7$I|vcfw>U9CD?n5bJN3+~XRFAz)E zPP=wDN70SOkrp_~5|oi3`c&uv`;?`(j`cb#p?18TK78_`oY;&TW0W({-#2Pwr$% z`2xE;k?NwwRq&#{TdcV5S^9R@z=p>>t%xJ8H&HxUgHp1$vlyk^(A+v8#ew30CwG5B z^hhVntZFI8+DB1VlAbZd54=?DNw-~_J}MYDQmmu3&G5CUMf9%YBkNE{y!?~-?P43| zs@}~mD$bTh-1odF+*JJl#^ZSZ$b~Md*!j=oxK1PC)z7Xe`wG?Yg~#8WEyRIFP8-JI zBnzljF*#~V#UEQ$KcAg8=CNje$8>+mW1ss3GWJgEA8jkn&$)*~Y@z-k_QsM^styzQ z&;a9xiuo*eymsQe9HCn_R77ASxm0UJicZWDY8)Egcj2gE*kJtbKw(~dTgJaGeaErx zQ!nO<&H>Bf%(yx4JR>0xRr>m7-zlsOC3cKp2kPe(3ZH(zU(QdVz1d3oI;?-d`vtLI zO2VSYka0}_k!Y5P*;8$j_N-!7mfs}JnWalbyNA=^XDhofC9fwLnPv}EgdJTwls z=oJP1aZC2`4l(od%o=zKq{$QC#ip|RZPl=Bv>41Buf!;pRY8S9RBfpYNqm-F^DPb} zek#)YGFR}AL>Q?9N?5uc^`?K-Mtr02;r?9LxZpFT*842XuO(XU9_;`>C&7A}^=Dy*ow_p221&C(mGE!`amXlL!1(oKO0ie9jn+n9PGO}JJxTm4Hg zxICf6wxkoq({VBK5$&ZC533MXSptMUZld>F2m_F2{x-P2ve!To()EAvVYJWbmheEo zUsv%{n>z<@z3jN)-Ub7w!LJ#s6fUJ7>|}>2l1`Jp&A)5Sz9-^yzim@5N_IJryAKi2 zn$({ealaH!!*-A%5i40_6pvLGJW46<|C)Z&)|d6;?dXe6XAq@RZnfZr8wtYHXd_g{1C3$ zn5Y+HJ$tpdY6j5(XYcQ>FP-p6pjKmeW{f2JT=XTDiEZT;Z?u1#Ak=}B8_w^}`{QYc zF)`heQ3brYj6xI8sHaBXupWP{Yd3)%e+w$Wnm8=LfISG*ZGPIefmjsze&oIBAQUSC-NUd zlAWEa71%1vzSn;)XL@{d)tt5&g9*VN6cJ6K*@}d+Ra5O7iGJ_gdOFyiEy^~!tVUyt zIvPjXE0WA)mFytKPUB1328_gJomiAkw(G4?W!nulQtd(gWYEa$v+(TngEY+|D>m%# zeEdP(j9z;xQEyh_C$+H^e3iX#Cp+eHkg^+-k|R+!@fUxen|XWaj!BmFBd`2&P|}}Z z{17k*+q0e2kJ)w63iiLap7N8pi_J#-2b z3tPz&9VBp`n@IvHw_o^r-mkVG5a7EgRyeeq?yBQ|A(OSek+UydR{3zX!{zPx(Fnog zm3PaN{0*2fw2?)L6Nfx+v}_-J#I8E!nAPpi7d(GkZ@*9d(zU46Bx$MJ!}1GmdqeHd zIP$ZuK(7l$hP!;Ku;SfuF8_j-u?HVgo65*Pk9R7JW~G8^YO|9Ad&?7}FIq>ql|^b{;d!D5ysy7x|L;NxMbF(?_n;T`>y zfvkVh_T>xVkB;OsN%zIQFLO2A@vKrYcZWllLpuBGhn;oT<%_3RJ z)h`JG8+8l6-WjJaz#1q+;xI6>5@wjn*v#W;29ubtK~g9LYmcs(nuh$uSH}8_GA=r4 zCn*nV9^UOPRIsOl@LvZtiWdZUGdu0OiD!T5?*xlD(^+()mTI>ze&1j&(a2G(|F(KR z^C>=Rf7$3e2ebNzuv0#xrz!S)Mf=;8A)_d?0JSjQ7?EXi_SCkocQk#XCzmgG*zkN zv|lk?F}#%^q5Kjumn*qzBgF?|B{bu!piIZ#ouQsVVPsSHSEBBv%-Si=6O(_nd9|8( zf5_FjgQA9q#Nj4ifryf@D+(0uMuSK@P-MPXlQm6mb!J6>&3cY;n``kno2Zbrc7~

    c#9FF-5u`vKT9K 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 z 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 fJ$ZW4X_c?{z>s!gAEWT%J6Lv@x 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

    S|CS7+*_FRpJfeROb#P8jQmZ; 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

    w75-g;BYd#wxAyM|Q9voe;4ya~H=Oj}>` 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 z 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 RMpkwXRzwO45jj!B{{;b#Eq(w1 delta 219699 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

    F_2@1J^ahe2Ce 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

    -_TO%5z~gD0 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≧pLX@WIqF{Hu&Jb?b>x&bc-^ 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 delta 245938 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 w9yaX?5x7swv<201YFHU4p@n@Q +#include +#include +#include +#include + +using namespace std; +// output file as global variable +ofstream ofile; + +// Main function begins here +int main(int argc, char* argv[]) +{ + int n; + char *outfilename; + + cin >> n; + double MCint = 0.; double MCintsqr2=0.; + // Initialize the seed and call the Mersienne algo + std::random_device rd; + std::mt19937_64 gen(rd()); + // Set up the uniform distribution for x \in [[0, 1] + std::uniform_real_distribution RandomNumberGenerator(0.0,1.0); + // Compute the variance and the mean value of the uniform distribution + // Compute also the specific values x for each cycle in order to be able to + // the covariance and the correlation function + // Read in output file, abort if there are too few command-line arguments + if( argc <= 2 ){ + cout << "Bad Usage: " << argv[0] << + " read also output file and number of cycles on same line" << endl; + exit(1); + } + else{ + outfilename=argv[1]; + } + ofile.open(outfilename); + // Get the number of Monte-Carlo samples + n = atoi(argv[2]); + double *X; + X = new double[n]; + for (int i = 0; i < n; i++){ + double x = RandomNumberGenerator(gen); + X[i] = x; + MCint += x; + MCintsqr2 += x*x; + } + double Mean = MCint/((double) n ); + MCintsqr2 = MCintsqr2/((double) n ); + double STDev = sqrt(MCintsqr2-Mean*Mean); + double Variance = MCintsqr2-Mean*Mean; +// Write mean value and standard deviation + cout << " Standard deviation= " << STDev << " Integral = " << Mean << endl; + + // Now we compute the autocorrelation function + double *autocor; autocor = new double[n]; + for (int j = 0; j < n; j++){ + double sum = 0.0; + for (int k = 0; k < (n-j); k++){ + sum += (X[k]-Mean)*(X[k+j]-Mean); + } + autocor[j] = sum/Variance/((double) n ); + ofile << setiosflags(ios::showpoint | ios::uppercase); + ofile << setw(15) << setprecision(8) << j; + ofile << setw(15) << setprecision(8) << autocor[j] << endl; + } + ofile.close(); // close output file + return 0; +} // end of main program + + + + + diff --git a/doc/src/week9/programs/mc.py b/doc/src/week9/programs/mc.py new file mode 100644 index 00000000..225b348e --- /dev/null +++ b/doc/src/week9/programs/mc.py @@ -0,0 +1,54 @@ +from matplotlib import pyplot as plt +from math import exp, acos, log10 +import numpy as np +from sympy import Symbol, integrate, exp, oo +import random + + +# function for the trapezoidal rule +def TrapezoidalRule(a,b,f,n): + h = (b-a)/float(n) + s = 0 + x = a + for i in range(1,n,1): + x = x+h + s = s+ f(x) + s = 0.5*(f(a)+f(b))+s + return h*s +# function to perform the Monte Carlo calculations +def MonteCarloIntegration(f,n): + sum = 0 +# Define the seed for the rng + random.seed() + for i in range (1, n, 1): + x = random.random() + sum = sum +f(x) + return sum/n + +# function to compute +def function(x): + return 4/(1+x*x) + +# Integration limits for the Trapezoidal rule +a = 0.0; b = 1.0 +# define x as a symbol to be used by sympy +x = Symbol('x') +# find result from sympy +#exact = integrate(function(x), (x, a, b)) +exact = acos(-1.0) +# set up the arrays for plotting the relative error +log10n = np.zeros(6); Trapez = np.zeros(6); MCint = np.zeros(6); +# find the relative error as function of integration points +for i in range(1, 6): + npts = 10**(i+1) + log10n[i] = log10(npts) + Trapez[i] = log10(abs((TrapezoidalRule(a,b,function,npts)-exact)/exact)) + MCint[i] = log10(abs((MonteCarloIntegration(function,npts)-exact)/exact)) +plt.plot(log10n, Trapez ,'b-',log10n, MCint,'g-') +plt.axis([1,6,-14.0, 0.0]) +plt.xlabel('$\log_{10}(n)$') +plt.ylabel('Relative error') +plt.title('Relative errors for Monte Carlo integration and Trapezoidal rule') +plt.legend(['Trapezoidal rule', 'Brute force Monte Carlo integration'], loc='best') +plt.savefig('mcintegration.pdf') +plt.show() diff --git a/doc/src/week9/programs/plot.py b/doc/src/week9/programs/plot.py new file mode 100644 index 00000000..ecea29a7 --- /dev/null +++ b/doc/src/week9/programs/plot.py @@ -0,0 +1,16 @@ +import numpy as np +from matplotlib import pyplot as plt +# Load in data file +data = np.loadtxt("autocor.dat") +data1 = np.loadtxt("automersenne.dat") +# Make arrays containing x-axis and binding energies as function of A +x = data[:,0] +corr = data[:,1] +corr2 = data1[:,1] +plt.plot(x, corr ,'ro', x, corr2, 'b') +plt.axis([0,1000,-0.2, 1.1]) +plt.xlabel(r'$d$') +plt.ylabel(r'$C_d$') +plt.title(r'autocorrelation function for RNG') +plt.savefig('autocorr.pdf') +plt.show() diff --git a/doc/src/week9/programs/uniformhisto.py b/doc/src/week9/programs/uniformhisto.py new file mode 100644 index 00000000..87eb902e --- /dev/null +++ b/doc/src/week9/programs/uniformhisto.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +import numpy as np +import matplotlib.mlab as mlab +import matplotlib.pyplot as plt +import random + +# initialize the rng with a seed +random.seed() +counts = 10000 +values = np.zeros(counts) +for i in range (1, counts, 1): + values[i] = random.random() + +# the histogram of the data +n, bins, patches = plt.hist(values, 10, facecolor='green') + +plt.xlabel('$x$') +plt.ylabel('Number of counts') +plt.title(r'Test of uniform distribution') +plt.axis([0, 1, 0, 1100]) +plt.grid(True) + +plt.show()

    xJCK=K>Q5%>?Fn2SG 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%0D)j!dIXH;WC2TO>r^1Nns(c|4+Vdu51rKcN$AgZ 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%0A@(FTK8sF3^6QFFKQ#)RBL?s$%ulFO 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