Wednesday, November 13, 2013

CUDA-accelerated Sparse matrix assembly and solution using CUSP

1. If you use finite element methods for your numerical PDEs, chances are good that at some point in time you need to generate one or more large matrices.

2. Of course, chances are also good that you don't. It seems especially prevalent today, in the midst of an explosion of development in computing with massively parallel processors, that "global" variants of finite element methods that require large sparse matrices are shunned in favor of discontinuous, "local" formulations that avoid such structures.

3. Whatever your opinion may be on the matrix question, for the purposes of this post, assume that you do need to construct a global matrix - like a global mass, stiffness and damping matrix for a structural dynamics code; or maybe a large advection matrix, or a Laplacian operator -- whatever; a big matrix.

4. For simulations that are small (~<100 thousand elements) AND for simulations where you only have to construct these matrices once, the usual idiom of: looping through elements, integration points and interpolation points work fine - even in MATLAB.

5. On the other hand, if your simulation is a bit larger - though still something that your workstation should be able to handle - say ~10-100 million elements - OR if your simulation involves geometric and/or material non-linearities such that you need to generate these global matrices with great frequency: slow, serial, loop-driven assembly routines will not suffice.

6. Luckily, the nice, smart folks at NVIDIA have provided some nifty tools to make assembly of large sparse matrices with CUDA an only mildly painful experience.

7. In general, for generating your sparse matrix, the basic steps are:


a. generate an unordered list of 3-tuples composed of {row #,column #, value}. These would be generated by your assembly routine and constitute the data for the sparse matrix. There would be one tuple for each loop through the element->integration_point->interpolation_point nest of loops.

b. Provide these vectors to a routine that generates your sparse matrix in a standard format suitable for your PDE solver.

8. In MATLAB, this is easy. You write a routine to generate the tuples (I usually store them as 3 vectors, or a 2D array with 3 columns), then feed them to the built-in function sparse like so:

?
1
2
3
K = sparse(row_vec,col_vec,val_vec,num_rows,num_cols);


and *POOF* you're done!


9. So how to do this with CUDA?


10. Wouldn't it be nice if there was a CUDA library that would do exactly that? (*cough* hint, hint)

Fortunately, one does almost exactly that.

CUSP.

11. If you haven't tried this already, visit the main CUSP page on Google code here and download the source. Since it's a template library, all you need to do is copy the source folder into your cuda include directory and you're done. If you do not have permission to write to the cuda include directory, just put the cusp folder somewhere convenient and add this to the include path for your compilation line.

12. One of the example codes that the CUSP developers provide is a sparse matrix assembly routine (code is here)

13. Health Warning If you're NOT a seasoned C++ user, you may need to pause a bit after viewing the source code to let your headache clear. Maybe a small glass of Talisker will help. If not, try a larger glass. While you're recovering, take heart and have faith that reading the code, studying the code and straining yourself over the online THRUST tutorials and demonstrations will make you a better person and maybe even convince you to like C++ a bit more.

14. Or maybe not. But either way, it's a super-slick little piece of code. I wish I could say I wrote it.

15. You can use the core of this routine to write your own function to generate a properly formatted sparse matrix in device memory.

16. This matrix is then ready to be used along with the BLAS-type functions provided by CUSP for time-dependent problems or, if you have a steady-state problem or otherwise one that requires solving this system of equations, you can use the growing collection of preconditioners and iterative solvers provided with CUSP.

17. Performance for the assembly routine is, of course, implementation dependent and - if you're like me - the actual assembly part using the code provided is much more efficiently implemented than the kernel you write to generate the three input vectors. For constructing and assembling a finite element Laplacian matrix on my GTX-580, the routine required less than 0.01 seconds for a small system with 12 thousand degrees of freedom with 23 thousand elements. For a somewhat larger system with 750K degrees of freedom and 1.5M elements, the routine took about 0.25 second; both times were a small fraction of what was required using my usual MATLAB routines.

18. There are other things that I would say about CUSP, but this post is already too long. In my opinion, it is much easier to use than CUSPARSE on which, I assume, it is based. I love the Matrix Market input/output options. Of course I also love that it doesn't cost money; but that's just me.

19. As for the ease-of-use for the CUSP-provided sparse matrix solver, I can only say that it would be hard to ask for anything much easier to use within a C++ environment.

As a quick example, try the following code along with a sparse, positive definite matrix from Matrix Market.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <cusp/csr_matrix.h>
#include <cusp/monitor.h>
#include <cusp/krylov/gmres.h>
#include <cusp/io/matrix_market.h>
#include <cusp/precond/smoothed_aggregation.h>
 
using namespace std;
 
int main(void)
{
 
//create an empty sparse matrix data tructure
cusp::csr_matrix<int,float,cusp::device_memory> A;
cout << "Reading in the matrix..." << endl;
//import a matrix from matrix market format
cusp::io::read_matrix_market_file(A,"boneS01.mtx");
 
//initialize the solution and right-hand-side vectors
cusp::array1d<float,cusp::device_memory> x(A.num_rows,0);
cusp::array1d<float,cusp::device_memory> b(A.num_rows,1);
 
//set up stoppping critiera
//iteration limit = 5000;
/relative tolerance = 1e-5;
cusp::verbose_monitor<float> monitor(b,5000,1e-5);
int restart = 50;
 
// set preconditioner
cout << "Forming preconditioner..." << endl;
cusp::precond::smoothed_aggregation<int,float,cusp::device_memory> M(A);
 
cout << "Solving the system..." << endl;
clock_t begin=clock();
cusp::krylov::gmres(A,x,b,restart,monitor,M);
clock_t done = clock();
 
cout << "Elapsed time = "
< < (double(done) - double(begin))/double(CLOCKS_PER_SEC)
< < endl;
 
return 0;



20. So that's it. Easy huh? In general, I've found that for the sparse-matrix solving phase even with a decent card like the GTX-580 or the C2070 it is hard to expect stupendous speedups over a good CPU sparse matrix library. Maybe 10x or so (if that) for a big (yet still single-workstation-size) system. Still, I claim that the CUSP library is easily an order of magnitude easier to use than something like the Intel Math Kernel Library; but that also is just my opinion.

21. There are other options; I've given the EM Photonics CULA Sparse a try via their free demonstration, but I cannot even pretend to give a fair comparison. If you know of someone who has, I would be interested in hearing about it.

-- Many thanks (Source :: http://noiceinmyscotchplease.blogspot.co.uk ) Great blog to look into for many a things

Saturday, October 5, 2013


Hello here are some tips for cfd simulation engineers from Stephen Ferguson who currently works with CD-Adapco (STAR-CCM+),
It occurred to me just the other day, that I've now been a simulation engineer for over 20 years (despite my youthful good looks, I really am that old). Back in the early 90s, when I fired up pro-STAR up for the first time, there were no 40 year old simulation engineers (or at least none that had survived to tell the tale).
Most of the advice I got from the previous generation of CFD engineers wasn't very useful: "you don't know how lucky you are being able to see your simulations on the screen, we had to make our models using punch cards and post process with a pen and a dot matrix printer." 
To celebrate 20 years in the game, I thought that I'd write my top seven tips for a long (if not successful) career in engineering simulation:
    Read the complete blog here :
     

    Wednesday, September 11, 2013

    This page contains a replay of highly popular webinar series that introduces you to the world of multicore and manycore computing with Intel® Xeon® processors and Intel® Xeon Phi™ coprocessors. Expert technical teams at Intel discuss development tools, programming models, vectorization, and execution models that will get your development efforts powered up to get the best out of your applications and platforms.

    Topics
    • Introduction by James Reinders
    • Introduction to the Intel® Xeon Phi™ Coprocessor
    • Optimizing and Compilation for Intel® Xeon Phi™ Coprocessor
    • GNU Debugger Intel® Xeon Phi™ Coprocessor
    • Message Passing Interface (MPI) on Intel® Xeon Phi™ Coprocessor
    • Get Ready for Intel® Math Kernel Library on Intel® Xeon Phi™ Coprocessor
    • Performance analysis on Intel® Xeon Phi™ Coprocessor

    Clink here to open the Intel website link

    Saturday, August 17, 2013


    Debugging using gdb - A walkthrough


    To illustrate the debugging process, there are C and Fortran example codes at the end of the tutorial that include both a floating point error and a segmentation fault. These examples are trivial, and are simply intended to show how easy it is to use the debugger. Note that the behaviour of the debugger is the same regardless of the language one is using, so we'll just show the C example in the walk-through that follows.

    first bug: an FPE

    First, to illustrate what happens when the code is run as is:

    gcc bugs.c
    ./a.out
    Floating point exception
    Notice the Floating point exception message, and the fact that it exited. To debug it in gdb
    First compile:

    gcc -Wall -O0 -g bugs.c


    Now start the debugger, specifying the program we want to debug:

    gdb a.out


    At this point, the program will be loaded, but is not running, so start it:

    (gdb) r
    Starting program: /nar_sfs/work/snuser/bugs/a.out
    Program received signal SIGFPE, Arithmetic exception.
    0x00000000004004db in divide (d=0, e=1) at /nar_sfs/work/snuser/bugs/bugs.c:5
    5 printf("%f\n",e/d);


    Note that the debugger will stop at the FPE, and show which function/routine it was in, what values input arguments had, the line number of the source file where the problem occured, and the actual line of the file. In this case this output is sufficient to diagnose the problem: clearly e/d is undefined since the denominator is zero. One can also look at a stack trace, to see what has been called up till this point:

    (gdb) where
    #0 0x00000000004004db in divide (d=0, e=1) at /nar_sfs/work/snuser/bugs/bugs.c:5
    #1 0x00000000004005e7 in main (argc=1, argv=0x7fbfffeab8) at /nar_sfs/work/snuser/bugs/bugs.c:24


    An important caveat concerning the stack trace is that the debugger may display a deep stack (ie. a long list of functions that have been entered), indicating a problem triggered inside a system library. While the system library function is the last function that was executed before the program failed, it is unlikely that there is actually a bug in the system library. One should trace back through the stack to the last call from the program into the library and inspect the arguments that were given to the library function to ensure that they are sensible - typically errors in system libraries occur when the library functions are called with incorrect arguments.


    In addition to the stack trace, one may look at the source code file, centered around a particular line:

    (gdb) l 5
    1 #include <stdio.h>
    2
    3 void divide(float d, float e)
    4 {
    5 printf("%f\n",e/d);
    6 }
    7
    8 void arrayq(float f[], int q)
    9 {
    10 printf("%f\n",f[q]);

    One can inspect the values of different variables in the current level of the stack:

    (gdb) p d
    $1 = 0
    (gdb) p e
    $2 = 1

    Or one can go "up" the stack to look at values in the calling function/routine:

    (gdb) up
    #1 0x00000000004005e7 in main (argc=1, argv=0x7fbfffeab8) at /nar_sfs/work/snuser/bugs/bugs.c:24
    24 divide(a,b);
    (gdb) p a
    $3 = 0
    (gdb) p b
    $4 = 1
    When one is finished, it's easy to exit:
    (gdb) q
    The program is running. Exit anyway? (y or n) y


    second bug: a segmentation fault

    Now, to illustrate a segfault, change the denominator to be non-zero, eg. a=4.0 Compile the modified code, and run it to see what happens:

    ./a.out
    0.250000
    Segmentation fault



    Notice the Segmentation fault message, and the fact that the job exited with code 139. To debug it in gdb:

    gdb a.out
    (gdb) r
    Starting program: /nar_sfs/work/snuser/bugs/a.out
    0.250000
    Program received signal SIGSEGV, Segmentation fault.
    0x0000000000400514 in arrayq (f=0x7fbfffe980, q=12000000) at /nar_sfs/work/snuser/bugs/bugs.c:10
    10 printf("%f\n",f[q]);

    Note that the program stops automatically when it hits the segmentation fault, and shows you which function it is in, the values of the input variables, and the line in the source. One can then try printing out the values of the array, to see why it would have a problem:

    (gdb) p f
    $1 = (float *) 0x7fbfffe980
    (gdb) p f[1]
    $2 = 1
    (gdb) p f[9]
    $3 = 9
    (gdb) p f[q]
    Cannot access memory at address 0x7fc2dc5580


    So it is clear that the program is trying to access something it shouldn't be. Note that this is lucky - had one accidently tried to access something just outs ide the array bounds:

    (gdb) p f[11]
    $4 = 0
    (gdb) p f[1000]
    $5 = 7.03598541e+22
    (gdb) p f[10000]
    Cannot access memory at address 0x7fc00085c0

    It would have resulted in a valid number and the program would have carried on, but the results of the program would have been wrong. So one can't count on an array out of bounds to always result in a segmentation fault. Often segmentation faults occur when there are problems with pointers, since they may point to innaccessable addresses, or when a program tries to use too much memory. Using a debugger greatly helps in identifying these sorts of problems.

    using core files


    If a program uses a lot of memory, does not trigger an error condition in a reproducible manner, or takes a long time before it reaches the error condition then it shouldn't be debugged interactively (at least in the first instance). In these situations one should submit the debugging instrumented program to the cluster as a compute job such that it will produce a core file when it crashes. A core file contains the state of the program at the time it crashed - one can then load this file into the debugger to inspect the state and determine what caused the problem.


    By default your Linux environment may not configured to produce core files. To enable core files, when using the bash shell on your system (the default shell) one must set the core limit to be non-zero. Setting it to be unlimited should suffice, eg.

    ulimit -c unlimited

    then when one runs a program that crashes it should indicate that it has produced (dumped) a core file, eg.

    gcc -g bugs.c
    ./a.out
    0.250000
    Segmentation fault (core dumped)

    The core file should appear in the present working directory with a name of the form core.PID , where PID is the process id of the program instance that crashed. Note: for anything more complex than the examples provided in this tutorial you should submit this as a job to the cluster, in which case the core file will be placed in the working directory used by the job but one must submit their job with the -f permitcoredump option specified to sqsub .


    One can then load this into gdb as an additional argument to gdb, eg.

    gdb a.out core.10966
    #0 0x0000000000400514 in arrayq (f=0x7fbfffdfc0, q=12000000) at /home/merz/bugs/bugs.c:10
    10 printf("%f\n",f[q]);
    (gdb) where
    #0 0x0000000000400514 in arrayq (f=0x7fbfffdfc0, q=12000000) at /home/merz/bugs/bugs.c:10
    #1 0x00000000004005f3 in main (argc=1, argv=0x7fbfffe0f8) at /home/merz/bugs/bugs.c:26
    (gdb) q


    Note that in this case one does not need to run the program in the debugger - it will simply inspect the state of the core file and use the debugging-instrumented binary to display the type of error and where it occurs. One may then run the gdb where command to get the stack backtrace, etc., to further identify the problem.

    As long as one sets their core size limit with the ulimit command before submitting their job, and submits their job with the sqsub -f permitcoredump flag, then this environment setting should propagate to their job and the program should generate a core. Keep in mind that this setting will not persist between logins, so you should either put it in your shell configuration file (eg. ~/.bash_profile ) or run it any time you log into a system if you want your programs to produce a core when they crash.

    debugging interactively



    If you need to view the state of the program leading up to the crash, perhaps repeatedly, then a core file won't suffice and it is suggested that one submit this as an interactive job (avoid running this on the login node!). If possible, one should try to resume the program from a checkpoint that is near to the crash to avoid waiting a long time while the program reaches the erroneous state.


    One can start gdb as follows:

    gdb ./a.out

    One can then proceed to debug in the usual fashion:

    r
    (gdb) Starting program: /nar_sfs/work/snuser/bugs/a.out
    Program received signal SIGSEGV, Segmentation fault.
    0x0000000000400514 in arrayq (f=0x7fbfffd740, q=12000000)
    at /nar_sfs/work/snuser/bugs/bugs.c:10
    10 printf("%f\n",f[q]);

    When you exit the debugger the job will automatically terminate

    (gdb) q

    Note: you may not see the (gdb} prompt, or it may appear out of order (as above), but you can proceed as though it were there.


    Examples


    FORTRAN CODEbugs.fC CODEbugs.c
     program bugs
         implicit none
         real a,b
         real c(10)
         integer p
         a=0.0
         b=1.0
         do p=1,10
             c(p)=p
         enddo
         p=12000000
         call divide(a,b)
         call arrayq(c,p)
     end program
     
     subroutine divide(d,e)
         implicit none
         real d,e
         print *,e/d
     end subroutine
     
     subroutine arrayq(f,g)
         implicit none
         real f(10)
         integer g
         print *,f(g)
     end subroutine
     #include <stdio.h>
     
     void divide(float d, float e)
     {
         printf("%f\n",e/d);
     }
     
     void arrayq(float f[], int q)
     {
         printf("%f\n",f[q]);
     }
     
     int main(int argc, char **argv)
     {
         float a,b;
         float c[10];
         int p;
         a=0.0;
         b=1.0;
         for (p=0;p<10;p++)
         {
             c[p]=(float)p;
         }
         p=12000000;
        divide(a,b);
        arrayq(c,p);
        return(0);
     }



    Source ::

    Wednesday, August 7, 2013



    Matrix Market File format & PetSc Bin


    If you have developed a CFD solver then most likely you have generated a sparse linear system which needs to be solved one way or the other. Apart from the standard Intel or other libraries, it would be definitely advantageous to use the PetSc library. PetSc has huge capabilities varying from linear solvers, pre-conditioners, eigenvalue analysis, to the CPU / GPU implementations.

    Getting to know PetSc will take sometime but it is greatly beneficial to employ matrix and vector operations. The only requirement now is to convert the system we have to PetSc known format. This is good for initial testing only. Here are two routines developed by P.Kumar (PetSc) to convert the Matrix in COO format or Matrix Market file format to PetSc Matrix bin format, and the other to convert vectors.

    MatrixMarket_to_PetScBin.c

    VectorMarket_to_PetScBin.c

    README.TXT

    Subscribe to RSS Feed Follow me on Twitter!