Professional Documents
Culture Documents
Object-Oriented Scientific Computing: Physics 244 James Bordner
Object-Oriented Scientific Computing: Physics 244 James Bordner
06 May 2008
James Bordner
Outline
1 2 3 4
Example Problem Evolution of Programming Paradigms Object-Oriented Programming Concepts Sample Object-Oriented Scientic Code
James Bordner
Example Problem
Example Problem
James Bordner
Example Problem
Example Problem
James Bordner
Example Problem
Example Problem
James Bordner
1 2 3
James Bordner
Unstructured Programming
The Dark Ages
Denition: Program (unstructured programming) A program is a single contiguous block of instructions acting on data. Contol ow is handled using goto statements.
James Bordner
Unstructured Programming
The Dark Ages
Denition: Program (unstructured programming) A program is a single contiguous block of instructions acting on data. Contol ow is handled using goto statements. Used in early computer programs before ~1970 Common with early languages: Fortran, COBOL, BASIC
James Bordner
Unstructured Programming
Pseudocode for sparse linear solver example
main
if (matrix type = 1) goto 100 if (solver type = 1) goto 10 [ Code to apply solver S1 to A1 x = b ] goto 10000 10 if (solver type = 2) goto 20 [ Code to apply solver S2 to A1 x = b ] goto 10000 20 if (solver type = 3) goto 30 . . . 100 if (matrix type = 2) goto 200 . . .
James Bordner Object-Oriented Scientic Computing
Unstructured Programming
Advantages and Disadvantages
James Bordner
Unstructured Programming
Advantages and Disadvantages
Suitable for very small, simple programs goto statements easily lead to inpenetrable spaghetti code Very dicult to write and understand large programs Virtually impossible to make large-scale modications
James Bordner
Structured Programming
The Age of Enlightenment
Denition: Program (structured programming) A program is a sequence of instructions acting on data, where instructions are organized into manageably-sized sections (functions or subroutines).
James Bordner
Structured Programming
The Age of Enlightenment
Denition: Program (structured programming) A program is a sequence of instructions acting on data, where instructions are organized into manageably-sized sections (functions or subroutines). Go To Statement Considered Harmful (Dijkstra, 1967) Higher-level control ow: if-then-else, for, while, etc. Programs usually designed using a top-down approach Supported by Pascal, C, Fortran 77
James Bordner
Structured Programming
Pseudocode for sparse linear solver example
main
sol1_mat1
1,* , 1,*
sol1_mat2
1,* , 2,*
solm _matn
m,* , n,*
if (solver type = 1 and matrix type = 1) then call sol1 mat1 (1,1 , 1,2 , . . . , 1,1 , 1,2 , . . . , x, . . . , b, . . . ) else if (solver type = 1 and matrix type = 2) then call sol1 mat2 (1,1 , 1,2 , . . . , 2,1 , 2,2 , . . . , x, . . . , b, . . . ) . . . else if (solver type = m and matrix type = n) then call solm matn (m,1 , m,2 , . . . , n,1 , n,2 , . . . , x, . . . , b, . . . )
James Bordner
Structured Programming
Advantages and Disadvantages
James Bordner
Structured Programming
Advantages and Disadvantages
Easier to write and understand large programs Still dicult to make large-scale modications
soli matj() subroutines depend on both solver algorithm details and matrix storage format details
Deep call trees can force choice between global data and long function parameter lists
James Bordner
Object-Oriented Programming
The Postmodern Era
Denition: Program (object-oriented programming) A program is a collection of cooperating objects. Each object can receive messages, process data, and send messages to other objects.
James Bordner
Object-Oriented Programming
The Postmodern Era
Denition: Program (object-oriented programming) A program is a collection of cooperating objects. Each object can receive messages, process data, and send messages to other objects. Roots in the 1960s, but not popular until ~1990s Supported by C++, Fortran 2003 Can use C, Fortran 90/95
James Bordner
Object-Oriented Programming
Pseudocode for sparse linear solver example
main
Solver S = Solver::create(solver type) Matrix A = Matrix::create(matrix type) Vector X,B S.apply (A,X,B)
Solver
create
Vector
create
Matrix
create
Solver1 1,
apply
Solver m m,*
apply
Matrix1 1,
matvec
Matrix n n,*
matvec
James Bordner
Object-Oriented Programming
Advantages and Disadvantages
Suitable for huge programs with complex datatypes Easier to modify large programs Easier to reuse existing components
James Bordner
Object-Oriented Programming
Advantages and Disadvantages
Suitable for huge programs with complex datatypes Easier to modify large programs Easier to reuse existing components Object-oriented languages can be very complicated (C++) Easier to write inecient code
James Bordner
1. 2. 3. 4.
main Solver
create
Vector
create
Matrix
create
1 2 3
Solver1 1,
apply
Solver m m,*
apply
Matrix1 1,
matvec
Matrix n n,*
matvec
James Bordner
1. 2. 3. 4.
Matrix
create
Solver1 1,
apply
Solverm m,*
apply
Matrix1 1,
matvec
Matrixn n,*
matvec
Solver S = Solver::create(solver type) Matrix A = Matrix::create(matrix type) Vector X,B S.apply (A,X,B)
James Bordner
1. 2. 3. 4.
Matrix
create
Modularity refers to grouping together related code and data A class denes how to encapsulate functions (methods) and data
Solver1 1,
apply
Solverm m,*
apply
Matrix1 1,
matvec
Matrixn n,*
matvec
Solver S = Solver::create(solver type) Matrix A = Matrix::create(matrix type) Vector X,B S.apply (A,X,B)
James Bordner
1. 2. 3. 4.
Matrix
create
Modularity refers to grouping together related code and data A class denes how to encapsulate functions (methods) and data An object is a particular instance of a class
Solver1 1,
apply
Solverm m,*
apply
Matrix1 1,
matvec
Matrixn n,*
matvec
Solver S = Solver::create(solver type) Matrix A = Matrix::create(matrix type) Vector X,B S.apply (A,X,B)
James Bordner
1. 2. 3. 4.
Matrix
create
Modularity refers to grouping together related code and data A class denes how to encapsulate functions (methods) and data An object is a particular instance of a class C++: class; Fortran: type, module; (C: struct)
Solver1 1,
apply
Solverm m,*
apply
Matrix1 1,
matvec
Matrixn n,*
matvec
Solver S = Solver::create(solver type) Matrix A = Matrix::create(matrix type) Vector X,B S.apply (A,X,B)
James Bordner
1. 2. 3. 4.
Matrix
create
Encapsulation refers to hiding implementation details from the rest of the program
Solver1 1,
apply
Solverm m,*
apply
Matrix1 1,
matvec
Matrixn n,*
matvec
Solver S = Solver::create(solver type) Matrix A = Matrix::create(matrix type) Vector X,B S.apply (A,X,B)
James Bordner
1. 2. 3. 4.
Matrix
create
Encapsulation refers to hiding implementation details from the rest of the program Public functions (and data) are accessible by the entire program
Solver1 1,
apply
Solverm m,*
apply
Matrix1 1,
matvec
Matrixn n,*
matvec
Solver S = Solver::create(solver type) Matrix A = Matrix::create(matrix type) Vector X,B S.apply (A,X,B)
James Bordner
1. 2. 3. 4.
Matrix
create
Encapsulation refers to hiding implementation details from the rest of the program Public functions (and data) are accessible by the entire program Private data (and functions) are accessible only from within the class itself
Solver1 1,
apply
Solverm m,*
apply
Matrix1 1,
matvec
Matrixn n,*
matvec
Solver S = Solver::create(solver type) Matrix A = Matrix::create(matrix type) Vector X,B S.apply (A,X,B)
James Bordner
1. 2. 3. 4.
Matrix
create
Encapsulation refers to hiding implementation details from the rest of the program Public functions (and data) are accessible by the entire program Private data (and functions) are accessible only from within the class itself C++, Fortran: private, public
Solver1 1,
apply
Solverm m,*
apply
Matrix1 1,
matvec
Matrixn n,*
matvec
Solver S = Solver::create(solver type) Matrix A = Matrix::create(matrix type) Vector X,B S.apply (A,X,B)
James Bordner
1. 2. 3. 4.
Matrix
create
Solver1 1,
apply
Solverm m,*
apply
Matrix1 1,
matvec
Matrixn n,*
matvec
Solver S = Solver::create(solver type) Matrix A = Matrix::create(matrix type) Vector X,B S.apply (A,X,B)
James Bordner
1. 2. 3. 4.
Matrix
create
Inheritence refers to deriving a class in terms of another existing class Derived class inherits existing code and data from base class (code reuse)
Solver1 1,
apply
Solverm m,*
apply
Matrix1 1,
matvec
Matrixn n,*
matvec
Solver S = Solver::create(solver type) Matrix A = Matrix::create(matrix type) Vector X,B S.apply (A,X,B)
James Bordner
1. 2. 3. 4.
Matrix
create
Inheritence refers to deriving a class in terms of another existing class Derived class inherits existing code and data from base class (code reuse) A derived class can add or override member functions and data
Solver1 1,
apply
Solverm m,*
apply
Matrix1 1,
matvec
Matrixn n,*
matvec
Solver S = Solver::create(solver type) Matrix A = Matrix::create(matrix type) Vector X,B S.apply (A,X,B)
James Bordner
1. 2. 3. 4.
Matrix
create
Inheritence refers to deriving a class in terms of another existing class Derived class inherits existing code and data from base class (code reuse) A derived class can add or override member functions and data Models a kind-of relationship
Solver1 1,
apply
Solverm m,*
apply
Matrix1 1,
matvec
Matrixn n,*
matvec
Solver S = Solver::create(solver type) Matrix A = Matrix::create(matrix type) Vector X,B S.apply (A,X,B)
James Bordner
1. 2. 3. 4.
Matrix
create
Inheritence refers to deriving a class in terms of another existing class Derived class inherits existing code and data from base class (code reuse) A derived class can add or override member functions and data Models a kind-of relationship C++: class Derived: public Base; Fortran: type, extends(Base) :: Derived
Object-Oriented Scientic Computing
Solver1 1,
apply
Solverm m,*
apply
Matrix1 1,
matvec
Matrixn n,*
matvec
Solver S = Solver::create(solver type) Matrix A = Matrix::create(matrix type) Vector X,B S.apply (A,X,B)
James Bordner
1. 2. 3. 4.
Matrix
create
Polymorphism allows a derived class object to be treated like a base class object.
Solver1 1,
apply
Solverm m,*
apply
Matrix1 1,
matvec
Matrixn n,*
matvec
Solver S = Solver::create(solver type) Matrix A = Matrix::create(matrix type) Vector X,B S.apply (A,X,B)
James Bordner
1. 2. 3. 4.
Matrix
create
Polymorphism allows a derived class object to be treated like a base class object. Object S is declared as Solver, but will contain some Solveri object.
Solver1 1,
apply
Solverm m,*
apply
Matrix1 1,
matvec
Matrixn n,*
matvec
Solver S = Solver::create(solver type) Matrix A = Matrix::create(matrix type) Vector X,B S.apply (A,X,B)
James Bordner
1. 2. 3. 4.
Matrix
create
Polymorphism allows a derived class object to be treated like a base class object. Object S is declared as Solver, but will contain some Solveri object. Appropriate Solveri::apply() function called (dynamic binding).
Solver1 1,
apply
Solverm m,*
apply
Matrix1 1,
matvec
Matrixn n,*
matvec
Solver S = Solver::create(solver type) Matrix A = Matrix::create(matrix type) Vector X,B S.apply (A,X,B)
James Bordner
1. 2. 3. 4.
Matrix
create
Polymorphism allows a derived class object to be treated like a base class object. Object S is declared as Solver, but will contain some Solveri object. Appropriate Solveri::apply() function called (dynamic binding). C++: virtual; Fortran: class
Solver1 1,
apply
Solverm m,*
apply
Matrix1 1,
matvec
Matrixn n,*
matvec
Solver S = Solver::create(solver type) Matrix A = Matrix::create(matrix type) Vector X,B S.apply (A,X,B)
James Bordner
1 2 3 4 5
James Bordner
MGMPI
Overview
MPI-based parallel linear solver library James Bordner @ National Center for Supercomputing Applications Elliptic problems discretized on 3D distributed Cartesian meshes Linear solvers:
Krylov solvers: CG, BiCG, BiCG-STAB, CGNR, CGNE Multigrid: V,W,F-cycles
http://nordlys.ucsd.edu/~bordner/mgmpi/
James Bordner Object-Oriented Scientic Computing
MGMPI
Computational Domain
James Bordner
MGMPI
Computational Domain
James Bordner
MGMPI
Sample code: SolverCG::apply() while (!isConverged()) { A.matvec (W,P); Scalar pw; dot (pw,P,W); Scalar alpha = r2 / pw; yaxpy (X,alpha,P,X); yaxpy (R,-alpha,W,R); Scalar r2prev = r2; dot (r2,R,R); Scalar beta = r2 / r2prev; xaxpy (P,beta,P,R); currResid = sqrt(r2); ++ iter; }
James Bordner
MGMPI
Sample code: SolverCG::apply() while (!isConverged()) { A.matvec (W,P); Scalar pw; dot (pw,P,W); Scalar alpha = r2 / pw; yaxpy (X,alpha,P,X); yaxpy (R,-alpha,W,R); Scalar r2prev = r2; dot (r2,R,R); Scalar beta = r2 / r2prev; xaxpy (P,beta,P,R); currResid = sqrt(r2); ++ iter; }
James Bordner
MGMPI
Sample code: SolverCG::apply() while (!isConverged()) { A.matvec (W,P); Scalar pw; dot (pw,P,W); Scalar alpha = r2 / pw; yaxpy (X,alpha,P,X); yaxpy (R,-alpha,W,R); Scalar r2prev = r2; dot (r2,R,R); Scalar beta = r2 / r2prev; xaxpy (P,beta,P,R); currResid = sqrt(r2); ++ iter; }
Matrix object operations Could write W = A*P;. . . . . . but executed as t A P; W t Vector object operations Could write X = alpha*P + X;. . . . . . but executed as t1 P; t2 t1 + X ; X t2
James Bordner
MGMPI
Sample code: SolverCG::apply() while (!isConverged()) { A.matvec (W,P); Scalar pw; dot (pw,P,W); Scalar alpha = r2 / pw; yaxpy (X,alpha,P,X); yaxpy (R,-alpha,W,R); Scalar r2prev = r2; dot (r2,R,R); Scalar beta = r2 / r2prev; xaxpy (P,beta,P,R); currResid = sqrt(r2); ++ iter; }
Matrix object operations Could write W = A*P;. . . . . . but executed as t A P; W t Vector object operations Could write X = alpha*P + X;. . . . . . but executed as t1 P; t2 t1 + X ; X t2 MPI communication is hidden
James Bordner
MGMPI
Sample code: Matrix7::matvec()
X.refresh ghost(); X.enforce bc(A.bc()); for (int i2=0; i2<X.n(2); i2++) { for (int i1=0; i1<X.n(1); i1++) { for (int i0=0; i0<X.n(0); i0++) { int ia = i0 + A.m(0) * (i1 + A.m(1) * i2); int ix = i0 + X.m(0) * (i1 + X.m(1) * i2); int iy = i0 + Y.m(0) * (i1 + Y.m(1) * i2); Y.val(iy) = A.val(AZM,ia) * X.val(ix-ixd2) + A.val(AYM,ia) * X.val(ix-ixd1) + A.val(AXM,ia) * X.val(ix-ixd0) + A.val(A0,ia) * X.val(ix) + A.val(AXP,ia) * X.val(ix+ixd0) + A.val(AYP,ia) * X.val(ix+ixd1) + A.val(AZP,ia) * X.val(ix+ixd2); } } }
James Bordner
MGMPI
Sample code: Matrix7::matvec()
X.refresh ghost(); X.enforce bc(A.bc()); for (int i2=0; i2<X.n(2); i2++) { for (int i1=0; i1<X.n(1); i1++) { for (int i0=0; i0<X.n(0); i0++) { int ia = i0 + A.m(0) * (i1 + A.m(1) * i2); int ix = i0 + X.m(0) * (i1 + X.m(1) * i2); int iy = i0 + Y.m(0) * (i1 + Y.m(1) * i2); Y.val(iy) = A.val(AZM,ia) * X.val(ix-ixd2) + A.val(AYM,ia) * X.val(ix-ixd1) + A.val(AXM,ia) * X.val(ix-ixd0) + A.val(A0,ia) * X.val(ix) + A.val(AXP,ia) * X.val(ix+ixd0) + A.val(AYP,ia) * X.val(ix+ixd1) + A.val(AZP,ia) * X.val(ix+ixd2); } } }
ixd0, ixd1, and ixd2 are index osets along each coordinate axis
James Bordner
MGMPI
Sample code: Matrix7::matvec()
X.refresh ghost(); X.enforce bc(A.bc()); for (int i2=0; i2<X.n(2); i2++) { for (int i1=0; i1<X.n(1); i1++) { for (int i0=0; i0<X.n(0); i0++) { int ia = i0 + A.m(0) * (i1 + A.m(1) * i2); int ix = i0 + X.m(0) * (i1 + X.m(1) * i2); int iy = i0 + Y.m(0) * (i1 + Y.m(1) * i2); Y.val(iy) = A.val(AZM,ia) * X.val(ix-ixd2) + A.val(AYM,ia) * X.val(ix-ixd1) + A.val(AXM,ia) * X.val(ix-ixd0) + A.val(A0,ia) * X.val(ix) + A.val(AXP,ia) * X.val(ix+ixd0) + A.val(AYP,ia) * X.val(ix+ixd1) + A.val(AZP,ia) * X.val(ix+ixd2); } } }
ixd0, ixd1, and ixd2 are index osets along each coordinate axis y Ax accesses ghost zones of x: values of x that belong to neighboring MPI processes
James Bordner
MGMPI
Sample code: Matrix7::matvec()
X.refresh ghost(); X.enforce bc(A.bc()); for (int i2=0; i2<X.n(2); i2++) { for (int i1=0; i1<X.n(1); i1++) { for (int i0=0; i0<X.n(0); i0++) { int ia = i0 + A.m(0) * (i1 + A.m(1) * i2); int ix = i0 + X.m(0) * (i1 + X.m(1) * i2); int iy = i0 + Y.m(0) * (i1 + Y.m(1) * i2); Y.val(iy) = A.val(AZM,ia) * X.val(ix-ixd2) + A.val(AYM,ia) * X.val(ix-ixd1) + A.val(AXM,ia) * X.val(ix-ixd0) + A.val(A0,ia) * X.val(ix) + A.val(AXP,ia) * X.val(ix+ixd0) + A.val(AYP,ia) * X.val(ix+ixd1) + A.val(AZP,ia) * X.val(ix+ixd2); } } }
ixd0, ixd1, and ixd2 are index osets along each coordinate axis y Ax accesses ghost zones of x: values of x that belong to neighboring MPI processes So these ghost zone values must be obtained from neighboring processors beforehand
James Bordner
MGMPI
Sample code: Vector::refresh ghost()
. . . send oset = is[0] + na3 [0] * (is[1] + na3 [1]*is[2]); recv oset = ir[0] + na3 [0] * (ir[1] + na3 [1]*ir[2]); Scalar *send buer = &v [send oset]; Scalar *recv buer = &v [recv oset]; if (do send && do recv) { MPI Sendrecv (send buer,1,ghost type, to rank,id, recv buer,1,ghost type, from rank,id, comm, &status); } else if (do send) { MPI Send (send buer,1,ghost type, to rank,id,comm); } else if (do recv) { MPI Recv (recv buer,1,ghost type, from rank,id,comm, &status); }
James Bordner
MGMPI
Sample code: Vector::refresh ghost()
. . . send oset = is[0] + na3 [0] * (is[1] + na3 [1]*is[2]); recv oset = ir[0] + na3 [0] * (ir[1] + na3 [1]*ir[2]); Scalar *send buer = &v [send oset]; Scalar *recv buer = &v [recv oset]; if (do send && do recv) { MPI Sendrecv (send buer,1,ghost type, to rank,id, recv buer,1,ghost type, from rank,id, comm, &status); } else if (do send) { MPI Send (send buer,1,ghost type, to rank,id,comm); } else if (do recv) { MPI Recv (recv buer,1,ghost type, from rank,id,comm, &status); }
We assume boolean variables do send and do recv have been initialized appropriately
James Bordner
MGMPI
Sample code: Vector::refresh ghost()
. . . send oset = is[0] + na3 [0] * (is[1] + na3 [1]*is[2]); recv oset = ir[0] + na3 [0] * (ir[1] + na3 [1]*ir[2]); Scalar *send buer = &v [send oset]; Scalar *recv buer = &v [recv oset]; if (do send && do recv) { MPI Sendrecv (send buer,1,ghost type, to rank,id, recv buer,1,ghost type, from rank,id, comm, &status); } else if (do send) { MPI Send (send buer,1,ghost type, to rank,id,comm); } else if (do recv) { MPI Recv (recv buer,1,ghost type, from rank,id,comm, &status); }
We assume boolean variables do send and do recv have been initialized appropriately Corresponding MPI calls are blocking MPI Send, MPI Recv, or MPI Sendrecv
James Bordner
MGMPI
Sample code: Vector::refresh ghost()
. . . send oset = is[0] + na3 [0] * (is[1] + na3 [1]*is[2]); recv oset = ir[0] + na3 [0] * (ir[1] + na3 [1]*ir[2]); Scalar *send buer = &v [send oset]; Scalar *recv buer = &v [recv oset]; if (do send && do recv) { MPI Sendrecv (send buer,1,ghost type, to rank,id, recv buer,1,ghost type, from rank,id, comm, &status); } else if (do send) { MPI Send (send buer,1,ghost type, to rank,id,comm); } else if (do recv) { MPI Recv (recv buer,1,ghost type, from rank,id,comm, &status); }
We assume boolean variables do send and do recv have been initialized appropriately Corresponding MPI calls are blocking MPI Send, MPI Recv, or MPI Sendrecv Data buers are simply pointers to the appropriate array values
James Bordner
MGMPI
Sample code: Vector::refresh ghost()
. . . send oset = is[0] + na3 [0] * (is[1] + na3 [1]*is[2]); recv oset = ir[0] + na3 [0] * (ir[1] + na3 [1]*ir[2]); Scalar *send buer = &v [send oset]; Scalar *recv buer = &v [recv oset]; if (do send && do recv) { MPI Sendrecv (send buer,1,ghost type, to rank,id, recv buer,1,ghost type, from rank,id, comm, &status); } else if (do send) { MPI Send (send buer,1,ghost type, to rank,id,comm); } else if (do recv) { MPI Recv (recv buer,1,ghost type, from rank,id,comm, &status); }
We assume boolean variables do send and do recv have been initialized appropriately Corresponding MPI calls are blocking MPI Send, MPI Recv, or MPI Sendrecv Data buers are simply pointers to the appropriate array values An MPI derived datatype ghost type is used, dened using MPI Type vector or MPI Type contiguous
James Bordner
Conclusions
Conclusions
Object-oriented programming can be used for large-scale parallel scientic computing Particularly useful for creating high-level data types that are natural to work with
e.g. Matrix, Solver, Field, Pde, etc.
Implementation details, including parallelism, can be encapsulated Object-oriented languages tend to be more complicated Eciency can be a concern The Object-Oriented Numerics Page
http://www.oonumerics.org/ reference material, mailing list, free software