<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://oldwiki.scinet.utoronto.ca/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Groer</id>
	<title>oldwiki.scinet.utoronto.ca - User contributions [en-gb]</title>
	<link rel="self" type="application/atom+xml" href="https://oldwiki.scinet.utoronto.ca/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Groer"/>
	<link rel="alternate" type="text/html" href="https://oldwiki.scinet.utoronto.ca/index.php/Special:Contributions/Groer"/>
	<updated>2026-05-23T03:45:09Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.35.12</generator>
	<entry>
		<id>https://oldwiki.scinet.utoronto.ca/index.php?title=ATLAS&amp;diff=4849</id>
		<title>ATLAS</title>
		<link rel="alternate" type="text/html" href="https://oldwiki.scinet.utoronto.ca/index.php?title=ATLAS&amp;diff=4849"/>
		<updated>2012-06-27T18:42:05Z</updated>

		<summary type="html">&lt;p&gt;Groer: Created page with &amp;quot; == Place holder for instructions for ATLAS users on SciNet ==   Leslie Groer - June 27, 2012&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Place holder for instructions for ATLAS users on SciNet ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Leslie Groer - June 27, 2012&lt;/div&gt;</summary>
		<author><name>Groer</name></author>
	</entry>
	<entry>
		<id>https://oldwiki.scinet.utoronto.ca/index.php?title=Oldwiki.scinet.utoronto.ca:System_Alerts&amp;diff=3135</id>
		<title>Oldwiki.scinet.utoronto.ca:System Alerts</title>
		<link rel="alternate" type="text/html" href="https://oldwiki.scinet.utoronto.ca/index.php?title=Oldwiki.scinet.utoronto.ca:System_Alerts&amp;diff=3135"/>
		<updated>2011-05-12T16:25:57Z</updated>

		<summary type="html">&lt;p&gt;Groer: /* System Status: Normal */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== System Status: &amp;lt;span style=&amp;quot;color:#339900&amp;quot;&amp;gt;Normal&amp;lt;/span&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
You can check our twitter feed, @SciNetHPC, for updates.&lt;br /&gt;
&lt;br /&gt;
Last updated:  Thu May 12 12:23:35 EDT 2011&lt;br /&gt;
&lt;br /&gt;
'''NOTE: There is some UofT network reconfiguration and maintenance on Friday morning May 13 07:45-08:15 that most likely will disrupt external network connections and data transfers.  Local running jobs should not be affected.'''&lt;br /&gt;
&lt;br /&gt;
([[Previous_messages:|Previous messages]])&lt;/div&gt;</summary>
		<author><name>Groer</name></author>
	</entry>
	<entry>
		<id>https://oldwiki.scinet.utoronto.ca/index.php?title=Oldwiki.scinet.utoronto.ca:System_Alerts&amp;diff=3134</id>
		<title>Oldwiki.scinet.utoronto.ca:System Alerts</title>
		<link rel="alternate" type="text/html" href="https://oldwiki.scinet.utoronto.ca/index.php?title=Oldwiki.scinet.utoronto.ca:System_Alerts&amp;diff=3134"/>
		<updated>2011-05-12T16:25:21Z</updated>

		<summary type="html">&lt;p&gt;Groer: /* System Status: Normal */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== System Status: &amp;lt;span style=&amp;quot;color:#339900&amp;quot;&amp;gt;Normal&amp;lt;/span&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
You can check our twitter feed, @SciNetHPC, for updates.&lt;br /&gt;
&lt;br /&gt;
Last updated:  Thu May 12 12:23:35 EDT 2011&lt;br /&gt;
&lt;br /&gt;
NOTE: There is some UofT network reconfiguration and maintenance on Friday morning May 13 07:45-08:15 that most likely will disrupt external network connections and data transfers.  Local running jobs should not be affected.&lt;br /&gt;
&lt;br /&gt;
([[Previous_messages:|Previous messages]])&lt;/div&gt;</summary>
		<author><name>Groer</name></author>
	</entry>
	<entry>
		<id>https://oldwiki.scinet.utoronto.ca/index.php?title=Introduction_To_Performance&amp;diff=458</id>
		<title>Introduction To Performance</title>
		<link rel="alternate" type="text/html" href="https://oldwiki.scinet.utoronto.ca/index.php?title=Introduction_To_Performance&amp;diff=458"/>
		<updated>2009-08-13T19:10:52Z</updated>

		<summary type="html">&lt;p&gt;Groer: /* Compute Time */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==The Concepts of Parallel Performance==&lt;br /&gt;
&lt;br /&gt;
Parallel computing used to be a very specialized domain; but now even making the best use of your laptop, which almost certainly has multiple independant computing cores, requires understanding the basic concepts of performance in a parallel environment.&lt;br /&gt;
&lt;br /&gt;
Most fundamentally, parallel programming allows three possible ways of getting more and better science done:&lt;br /&gt;
;Running your computation many times&lt;br /&gt;
:If you have a program that works in serial, having many processors available to you allows you to run many copies of the same program at once, improving your [[#Throughput|throughput]].   This (can be) a sort of trivial use of parallel computing and doesn't require very specialized hardware, but it can be extremely useful for, for instance, running parameter studies or sensitivity studies.   Best of all, this is essentially guaranteed to run efficiently if your serial code runs efficiently!  Because this doesn't require fancy hardware, it is a waste of resources to use the [[TCS_Quickstart|Tightly Coupled System]] for these sorts of tasks and instead they must be run on the [[GPC_Quickstart|General Purpose Cluster]].&lt;br /&gt;
;Running your computation faster&lt;br /&gt;
:This is what most people think of as parallel computing.  It can take a lot of work to make an existing code run efficiently on many processors, or to design a new code to make use of these resources, but when it works, one can achieve a substantial [[#Parallel_Speedup|speedup]] of individual jobs.  This might mean the difference between a computation running in a feasible length of time for a research project or taking years to complete --- so while it may be a lot of work, it may be your only option.    To determine whether your code runs well on many processors, you need to measure [[#Parallel_Speedup|speedup]] and [[#Efficiency|efficiency]]; to see how many processors one should use for a given problem you must run [[#Strong_Scaling_Tests|strong scaling tests]].&lt;br /&gt;
;Running your computation on larger problems&lt;br /&gt;
:One achieves speedup by using more processors on the same problem.  But by running your job in parallel you may have access to more resources other than just processors --- for instance, more memory, or more disks.   In this case, you may be able to run problems that simply wouldn't be possible on a single processor or a single computer; one can achieve significant '''''sizeup'''''.  To find how large a problem one can efficiently run, one measures [[#Efficiency|efficiency]] and runs [[#Weak_Scaling_Tests|weak scaling tests]].&lt;br /&gt;
&lt;br /&gt;
Of course, these aren't exclusive; one can take advantage of any combination of the above.   It may be that your problem runs efficiently on 8 cores but no more; however, you may be able to get use of more processors by running many jobs to explore parameter space, and already on 8 cores you may be able to consider larger problems than you can with just one!&lt;br /&gt;
&lt;br /&gt;
===Throughput===&lt;br /&gt;
&lt;br /&gt;
Throughput is the most fundamental measure of performance, and the one that ultimately most matters to most computational scientists -- if you have N computations that you need to have done for your research project, how quickly can you get them done?   Everything else we'll consider here is just a&lt;br /&gt;
way of increasing throughput T:&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
T = \frac{\mathrm{Number}\,\mathrm{of}\,\mathrm{computations}}{\mathrm{Unit}\,\mathrm{time}} .&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you have many indepdendent computations to perform (such as a parameter study or a sensitivity study) you can increase throughput almost arbitrarily by running them alongside each other at the same time, limited only by the number of processors available (or the wait time in the queue, or the disk space available, or some other external resource constraint).   This approach obviously doesn't work if you only have one computation to perform, or if later&lt;br /&gt;
computations require the output from previous ones.   In these cases, or when  the individual jobs take infeasibly long, or cannot be performed on only one processor,  one must resort to ''also'' using parallel programming techniques to parallelize the individual jobs.&lt;br /&gt;
&lt;br /&gt;
===Compute Time===&lt;br /&gt;
&lt;br /&gt;
Fundamental to everything else that follows is measuring the amount of time a computation takes on some problem size/amount of work &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; and some number of processors &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt;.   We'll denote this by &amp;lt;math&amp;gt;t(N,P)&amp;lt;/math&amp;gt;.   The easiest way to measure this time is with the &amp;lt;tt&amp;gt;time&amp;lt;/tt&amp;gt; command that comes on most flavours of Unix in &amp;lt;tt&amp;gt;/bin/time&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;/usr/bin/time&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/bin/time myprogram&lt;br /&gt;
...normal program output...&lt;br /&gt;
658.44user 0.85system 10:59.41elapsed &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The format of the times output at the end may vary from system to system, but the basic information returned will be the same.  The ''real'' or ''elapsed'' time listed is the actual [[Wallclock time]] that elapsed during the run, ''user'' or ''cpu'' is the [[CPU time]] that was actually spent doing your computation, and the ''system'' time is the system time that was spent doing system-related things during the run, such as waiting for file input/output.   Our goal will be to reduce the real wallclock time that the simulation takes as much as possible while still making efficient use of the resources available.&lt;br /&gt;
&lt;br /&gt;
===Parallel Speedup===&lt;br /&gt;
&lt;br /&gt;
The speedup of an individual job with some amount of work &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; as you go from some running it serially to running on &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt; is simply:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S(N,P) = \frac{t(N,P=1)}{t(N,P)} .&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That is, the time it takes to run the computation on P vs on one processor.   The way this is usually done is to run the parallel code on &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt; and&lt;br /&gt;
on &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; processor and take the ratio of the two times; but this is a form of cheating, as the parallel version of the code will generally have&lt;br /&gt;
overheads (even in the one-processor case) compared to the best available serial-only version of the code.   The best thing to do in considering the efficiency of the parallelization to compare the parallel code to the best available serial code that does the same job.&lt;br /&gt;
&lt;br /&gt;
If you are considering the speedup of a problem that doesn't fit onto one processor, of course, the concept of speedup can be generalized; one needn't start at &amp;lt;math&amp;gt;P=1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
It should go without saying that, while developing your parallel code and during performance tuning, you get the same results with multiple processors as with some `known good' serial test case; it is even easier to introduce bugs in parallel code than it is in serial code!&lt;br /&gt;
&lt;br /&gt;
===Efficiency===&lt;br /&gt;
&lt;br /&gt;
Once you have a parallel code and some timing results one can look at how efficiently you are making use of the resources as you use more and more processors.&lt;br /&gt;
The parallel efficiency of a computation of some fixed work size running on &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt; processors as compared to the &amp;lt;math&amp;gt;P=1&amp;lt;/math&amp;gt; case is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;E = \frac{S(N,P)}{P}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That is, if you get a speedup of &amp;lt;math&amp;gt;8 \times&amp;lt;/math&amp;gt; in going from one to eight processors, you are at 1.00 or 100% efficiency; anything less and you are at lower efficiency.  It isn't uncommon to achieve greater than 100% parallel efficiencies for small numbers of processors for some types of problems; as you go to more processors, you also have more processor cache, and thus more of the problems data can fit into fast cache.  This is called ''super-linear speedup'' and sadly seldom extends out to very many processors.  &lt;br /&gt;
&lt;br /&gt;
===Strong Scaling Tests===&lt;br /&gt;
&lt;br /&gt;
[[Image:scaling-example.png|thumb|right|320px|An example of a strong scaling test]]&lt;br /&gt;
&lt;br /&gt;
The figure to the right and data below shows an example of a result of a small strong scaling test --- running a fixed-size problem on a varying number of processors to see how the timing of the computation scales with the number of processors.   The code was an OpenMP code run on a node of the GPC.  The quantitative results follow below; the times were measured and then speedups and efficiencies were calculated as above.   &lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
! &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt;&lt;br /&gt;
! &amp;lt;math&amp;gt;t(N,P)&amp;lt;/math&amp;gt;&lt;br /&gt;
! &amp;lt;math&amp;gt;S(N,P)&amp;lt;/math&amp;gt;&lt;br /&gt;
! &amp;lt;math&amp;gt;E(N,P)&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 3:50 ||  -  ||  -   &lt;br /&gt;
|-&lt;br /&gt;
| 2 || 2:02 || 1.87x || 94 % &lt;br /&gt;
|-&lt;br /&gt;
| 4 || 1:05 || 3.52x || 88 %&lt;br /&gt;
|-&lt;br /&gt;
| 6 || 47.8 || 4.81x || 80 %&lt;br /&gt;
|-&lt;br /&gt;
| 8 || 43.6 || 5.28x || 66%&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The plot shows the compute time &amp;lt;math&amp;gt;t(N,P)&amp;lt;/math&amp;gt; as a function of P; if the code maintained 100% parallel efficiency, we would expect the scaling to be&lt;br /&gt;
as 1/P, so we plot it on a log-log scale.  Also shown is the ideal scaling case -- what the times would be if, using the &amp;lt;math&amp;gt;P=1&amp;lt;/math&amp;gt; timing as a normalization, we did get 100% efficiency.   We can see that past 4 cores the measured case starts to significantly deviate from the ideal, and it looks like things would only get worse past 8 cores.&lt;br /&gt;
&lt;br /&gt;
It's important to note here that scaling tests should be done on realistic problem sizes and for realistic lengths of time.   Generally, for either serial or parallel programs there will be some overhead both at initialization time and during the course of the computation; if the problem size is too small, the overhead during the course of the run might be a significant fraction of the real work, and the program will behave needlessly poorly.  Similarly, if the number of timesteps or iterations is too small, the initizalization overhead will similarly play a spuriously large role in the performance.&lt;br /&gt;
&lt;br /&gt;
The above behaviour is typical for a small computation; it won't scale to too many cores, and the efficiency becomes monotonically worse as one increases the number of cores in use.   The rate at which this happens will depend on the problem size and the type of computation.   How is one to tell where to stop;&lt;br /&gt;
how good an efficiency is good enough?    Certainly there are rules of thumb --- one shudders to see efficiencies below 50% --- but one can arrive at more meaningful and quantitative results by considering throughput.   Let's imagine we had 64 cores at our disposal, and we wanted to run 96 jobs as quickly as possible.   Our total time to completion of the 96 jobs would vary with the number of cores we ran per job as follows:&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
! &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt;&lt;br /&gt;
! Time for one job&lt;br /&gt;
! Time for all 96 jobs&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 3:50 || 7:40  (2 batches, 64 jobs then 32)&lt;br /&gt;
|- &lt;br /&gt;
| 2 || 2:02 || 7:08 (3 batches, 32,32,32)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 1:05 || 6:30 (6 batches, 6x16)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || 47.8 || 7:58 (10 batches, 9x10, 6)&lt;br /&gt;
|-&lt;br /&gt;
| 8 || 43.6 || 8:43 (12 batches)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If we use more than 4 processes per job in this case, it will actually take us longer to do all our runs!  For jobs that scale better with the number of processes (this could be a different program, or the same program with different problem size), we will find this turnover point to be at higher &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt;; for jobs that scale worse, lower &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Weak Scaling Tests===&lt;br /&gt;
&lt;br /&gt;
[[Image:weak-scaling-example.png|thumb|right|320px|An example of a weak scaling test]]&lt;br /&gt;
&lt;br /&gt;
The strong scaling test described above considers the performance of a parallel code with a fixed work size as the number of processors varies; this tells us how the parallel overhead behaves as you go to more and more processors.   A weak scaling test fixes the amount of work '''per processor''' and compares the execution time over number of processors.   Since each processor has the same amount to do, in the ideal case the execution time should remain constant.   While the strong scaling test tells you how the parallel overhead scales with &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt;, the weak scaling test tells you something weaker -- whether the parallel overhead varies faster or slower than the amount of work.   &lt;br /&gt;
&lt;br /&gt;
Nonetheless, the weak scaling test can be the relevant one for determining how large a problem size one can efficiently compute with a given parallel code and system.    An example of results for a weak scaling test on the GPC and TCS up to 256 processors (8 nodes of the TCS, 32 of the GPC) is shown to the right.   In this case we are maintaining extremely good efficiency up to at least 128 processors with constant work per process on both architectures.  It is possible to see different behaviour when first filling up a node (eg, for less than 8 processes for the GPC, or 64 for TCS) than when one starts crossing  nodes; one should understand this but it doesn't necessarily indicate problems.&lt;br /&gt;
&lt;br /&gt;
==Performance Tuning==&lt;br /&gt;
&lt;br /&gt;
'''You cannot improve what you cannot measure.'''   Performance tuning is an iterative process between running an '''instrumented''' version of your code, getting data on performance throughout the code, and attempting to make chances to the code that will make it run more efficiently.&lt;br /&gt;
&lt;br /&gt;
There are three main ways of instrumenting a code to find its performance.  The first is '''manually adding timers''' around important parts of the code to find out how much time is spent in each part.   This is worth thinking about doing when putting together a new code, as it means that you'll have a very robust way of finding out how well the different parts of the code perform on different platforms and with different compiler options, etc..  The results are, however, necessarily very coarse-grained; they are very useful for comparing performance under different situations, but give very little information about whether or not there are performance problems or what they might be.&lt;br /&gt;
&lt;br /&gt;
The second technique is '''sampling''', sometimes called `program counter sampling' or `statistical sampling'.   In this case, the program is run in an environment where it is interrupted briefly at some set frequency (typically something like 100 times per second) and the location of the program counter is jotted down before the program is resumed.  At the end of the program, these locations are translated into locations in the source code, and one has a statistical profile of where the program has spent its time.  &lt;br /&gt;
&lt;br /&gt;
Statistical sampling has several advantages.  It has a very low overhead --- the sampling procedure for instance takes much less time than a function call to a timer routine --- so that the program runs much as it would without the measurement process.  If the samples are taken often enough, the result is a very accurate picture of where your program is spending its time, allowing you to very quickly identify `hotspots' in the code and focus your attention on the most costly areas of the program.   This combination of relevant information and low-overhead makes statistical sampling the first resort for serious performance measurement.&lt;br /&gt;
&lt;br /&gt;
Sampling, however, has drawbacks.  While it lets you know where the program is spending its time, it doesn't tell you why, or how it got there in the first place.   For instance, in a parallel program you may be spending too much time in barriers of one sort or another (perhaps at &amp;lt;tt&amp;gt;MPI_WAITALL&amp;lt;/tt&amp;gt; calls in MPI, or implicit barriers at the end of &amp;lt;tt&amp;gt;parallel&amp;lt;/tt&amp;gt; sections in OpenMP) but unless you know where in the code that routine was called from, you can't address the problem.   In this case you need some sort of '''trace''' through the program which keeps track of which routine called what.   This is generally a much heavier-weight process, which can substantially increase the runtime of the code, running the risk of 'the Heisenburg effect' - measurement changing the system under observation.  On the other hand, sometimes you just need that level of information, so tracing packages or libraries must be used.&lt;br /&gt;
&lt;br /&gt;
A related method is the use of '''hardware counters''' --- counters within the CPU itself which keep track of performance-related information, such as the number of cache misses or branch mis-predictions within your code.   Using this information, either regularly throughout the code or once for the entire code run can give very specific information about performance problems.   Right now these counters are available on the TCS system but not on the GPC system, as the mainstream Linux kernel does not provide access to these counters.&lt;br /&gt;
&lt;br /&gt;
==Command-line Performance Tools==&lt;br /&gt;
&lt;br /&gt;
Many of the tools below can be used to examine both serial and parallel performance problems with a code.  We'd like to encourage you to tune serial performance first.  Worrying about parallel performance before the code performs well with a single task doesn't make much sense!  Profiling your code when running with one task  allows you to spot serial `hot spots' for optimization, as well as giving you more detailed understanding of where your program spends its time.   Further, any performance you make in the serial code will automatically speed up your parallel code.&lt;br /&gt;
&lt;br /&gt;
We've already talked about coarse-grained measurements such as timers within the code and using tools such as &amp;lt;tt&amp;gt;/bin/time&amp;lt;/tt&amp;gt;.  These are very useful for comparing overall performance between different platforms/parameters, but we won't need to discuss them further here.&lt;br /&gt;
&lt;br /&gt;
===gprof (profiling: everywhere)===&lt;br /&gt;
&lt;br /&gt;
A statistical sampling workhorse is &amp;lt;tt&amp;gt;gprof&amp;lt;/tt&amp;gt;, the GNU version of an old common Unix utility called prof.  To use this, the code must be re-compiled with both source-code symbols intact (&amp;lt;tt&amp;gt;-g&amp;lt;/tt&amp;gt;) and with profiling information available (for most compilers, this is &amp;lt;tt&amp;gt;-pg&amp;lt;/tt&amp;gt;; for the IBM compilers (xlf, xlc, xlC) it is &amp;lt;tt&amp;gt;-p&amp;lt;/tt&amp;gt;).  It is worth knowing because of its ubiquity, and because it contains much of the functionality of newer tools so that the same concepts occur in other concepts.&lt;br /&gt;
&lt;br /&gt;
So let's consider the following trivial program &amp;lt;tt&amp;gt;pi.c&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot; line=1&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;time.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
double calc_pi(long n) {&lt;br /&gt;
    long in = 0;&lt;br /&gt;
    long out = 0;&lt;br /&gt;
    long i;&lt;br /&gt;
    double x,y;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;n; i++) {&lt;br /&gt;
        x = drand48();&lt;br /&gt;
        y = drand48();&lt;br /&gt;
        if (x*x+y*y &amp;lt; 1) {&lt;br /&gt;
            in++;&lt;br /&gt;
        } else {&lt;br /&gt;
            out++;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 4.*(double)in/(double)(in+out);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv) {&lt;br /&gt;
    long n, defaultn=100000;&lt;br /&gt;
    double pi;&lt;br /&gt;
    time_t t;&lt;br /&gt;
&lt;br /&gt;
    /* seed random number generator */&lt;br /&gt;
    srand48(time(&amp;amp;t));&lt;br /&gt;
&lt;br /&gt;
    /* get number of tries */&lt;br /&gt;
    if (argc &amp;lt; 2 || (n=atoi(argv[1]))&amp;lt;1) {&lt;br /&gt;
        n = defaultn;&lt;br /&gt;
        printf(&amp;quot;Using default n = %ld\n&amp;quot;, n);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    pi = calc_pi(n);&lt;br /&gt;
    printf(&amp;quot;Pi = %lf\n&amp;quot;, pi);&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;   &lt;br /&gt;
&lt;br /&gt;
We can compile this with profiling on and run it:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ gcc -g -pg -o pi pi.c&lt;br /&gt;
$ ./pi 100000000&lt;br /&gt;
Pi = 3.141804&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Note that this isn't a very good way of calculating pi!).  On exit, this program creates a file called &amp;lt;tt&amp;gt;gmon.out&amp;lt;/tt&amp;gt;; this contains the profiling information about the run of the code.  We can take a look at this by using &amp;lt;tt&amp;gt;gprof&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ gprof pi gmon.out &lt;br /&gt;
Flat profile:&lt;br /&gt;
&lt;br /&gt;
Each sample counts as 0.01 seconds.&lt;br /&gt;
  %   cumulative   self              self     total           &lt;br /&gt;
 time   seconds   seconds    calls  ms/call  ms/call  name    &lt;br /&gt;
100.88      1.00     1.00        1   998.76   998.76  calc_pi&lt;br /&gt;
&lt;br /&gt;
index % time    self  children    called     name&lt;br /&gt;
                1.00    0.00       1/1           main [2]&lt;br /&gt;
[1]    100.0    1.00    0.00       1         calc_pi [1]&lt;br /&gt;
-----------------------------------------------&lt;br /&gt;
                                                 &amp;lt;spontaneous&amp;gt;&lt;br /&gt;
[2]    100.0    0.00    1.00                 main [2]&lt;br /&gt;
                1.00    0.00       1/1           calc_pi [1]&lt;br /&gt;
-----------------------------------------------&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first part tells us that essentially all of the time spent running was in the &amp;lt;tt&amp;gt;calc_pi()&amp;lt;/tt&amp;gt; routine (of course), and the second part attepts to be a call graph, showing that &amp;lt;tt&amp;gt;main&amp;lt;/tt&amp;gt; called &amp;lt;tt&amp;gt;calc_pi()&amp;lt;/tt&amp;gt; once.    An important concept in the timing is the `self' and `children' times for each routine, sometimes called the inclusive and exclusive time.   Because most routines call many other routines, its often useful to distinguish between the total amount of time spent between starting and ending the routine (the `inclusive' time) and that same time excluding the time spent in child routines (`exclusive' time).  &lt;br /&gt;
&lt;br /&gt;
The above results are fairly trivial and not very useful for this simple program, but in more complicated routines it can be very valuable to narrow down hotspots to particular regions of code.&lt;br /&gt;
&lt;br /&gt;
[[Image:Xprofiler.png|thumb|300px|The AIX tool &amp;lt;tt&amp;gt;Xprof&amp;lt;/tt&amp;gt; gives a visual representation of the &amp;lt;tt&amp;gt;gprof&amp;lt;/tt&amp;gt; output.]]&lt;br /&gt;
&lt;br /&gt;
In fact, gprof also allows you to view the time spent in the code by lines of code.   As you chop the program up finer, the statistical sampling gets less accurate; thus to look at the results by line of code you must be sure that your sample run was long enough to get meaningful data.  But the results can be extremely useful:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ gprof --line pi gmon.out&lt;br /&gt;
Flat profile:&lt;br /&gt;
&lt;br /&gt;
Each sample counts as 0.01 seconds.&lt;br /&gt;
  %   cumulative   self              self     total           &lt;br /&gt;
 time   seconds   seconds    calls  Ts/call  Ts/call  name    &lt;br /&gt;
 70.31      0.70     0.70                             calc_pi (pi.c:14 @ 40078b)&lt;br /&gt;
 14.27      0.84     0.14                             calc_pi (pi.c:17 @ 4007bc)&lt;br /&gt;
  5.10      0.89     0.05                             calc_pi (pi.c:11 @ 4007c1)&lt;br /&gt;
  4.08      0.93     0.04                             calc_pi (pi.c:15 @ 4007b5)&lt;br /&gt;
  3.06      0.96     0.03                             calc_pi (pi.c:13 @ 400781)&lt;br /&gt;
  2.55      0.98     0.03                             calc_pi (pi.c:12 @ 400777)&lt;br /&gt;
  1.53      1.00     0.02                             calc_pi (pi.c:11 @ 40076d)&lt;br /&gt;
  0.00      1.00     0.00        1     0.00     0.00  calc_pi (pi.c:5 @ 40074c)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where now we can see that the single line containing the radius calculation (&amp;lt;tt&amp;gt;if (x*x+y*y &amp;lt; 1)&amp;lt;/tt&amp;gt;) is 70% of the work for the entire program.  This tells you where you should spend your time to optimize the code.   Other tools exist for this sort of line-by-line analysis; &amp;lt;tt&amp;gt;gcov&amp;lt;/tt&amp;gt; in the gcc compiler suite counts the number of times a given source line is executed - the idea was for coverage analysis for test suites, but it certainly can be used for profiling as well; however, usually the amount of time spent at a line is more important than the number of executions.&lt;br /&gt;
&lt;br /&gt;
For parallel programs, &amp;lt;tt&amp;gt;gprof&amp;lt;/tt&amp;gt; will generally output a seperate &amp;lt;tt&amp;gt;gmon.out&amp;lt;/tt&amp;gt; file for each process; for threaded applications, output for all threads will be summed into the same &amp;lt;tt&amp;gt;gmon.out&amp;lt;/tt&amp;gt;.   It may be useful to sum up all the results and view them with gprof or to look at them individually.&lt;br /&gt;
&lt;br /&gt;
There are other tools for looking at the same data.   For instance, on the TCS system, the command &amp;lt;tt&amp;gt;Xprof&amp;lt;/tt&amp;gt; &lt;br /&gt;
(run the same way as &amp;lt;tt&amp;gt;gprof&amp;lt;/tt&amp;gt;; &amp;lt;tt&amp;gt;Xprof program_name gmon.out&amp;lt;/tt&amp;gt;) lets you look at the call tree as a graphical tree.  Each routine is shown by a block with a size proportional to the time spent in each routine; the width is the inclusive time, and the height is the exclusive time.  &lt;br /&gt;
&lt;br /&gt;
===hpmcount (performance counters: TCS)===&lt;br /&gt;
&lt;br /&gt;
On the TCS, &amp;lt;tt&amp;gt;hpmcount&amp;lt;/tt&amp;gt; allows the querying of the performance counter values over the course of a run.  Since here we are simply asking the CPU to report values it obtains during the run of a program, the code does not need to be instrumented; simply typing&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
hpmcount hpmcount_args program_name program_args&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will run the program and output the results from the hardware performance counters at the end.  So for instance, with our trivial pi program above,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
tcs-f11n05-$ hpmcount ./pi&lt;br /&gt;
Using default n = 100000&lt;br /&gt;
Pi = 3.144240&lt;br /&gt;
 Execution time (wall clock time): 0.020325 seconds&lt;br /&gt;
&lt;br /&gt;
 ########  Resource Usage Statistics  ########  &lt;br /&gt;
&lt;br /&gt;
 Total amount of time in user mode            : 0.012754 seconds&lt;br /&gt;
 Total amount of time in system mode          : 0.001486 seconds&lt;br /&gt;
 Maximum resident set size                    : 440 Kbytes&lt;br /&gt;
 Average shared memory use in text segment    : 0 Kbytes*sec&lt;br /&gt;
 Average unshared memory use in data segment  : 0 Kbytes*sec&lt;br /&gt;
 Number of page faults without I/O activity   : 53&lt;br /&gt;
 Number of page faults with I/O activity      : 1&lt;br /&gt;
 Number of times process was swapped out      : 0&lt;br /&gt;
 Number of times file system performed INPUT  : 0&lt;br /&gt;
 Number of times file system performed OUTPUT : 0&lt;br /&gt;
 Number of IPC messages sent                  : 0&lt;br /&gt;
 Number of IPC messages received              : 0&lt;br /&gt;
 Number of signals delivered                  : 0&lt;br /&gt;
 Number of voluntary context switches         : 6&lt;br /&gt;
 Number of involuntary context switches       : 0&lt;br /&gt;
&lt;br /&gt;
 #######  End of Resource Statistics  ########&lt;br /&gt;
&lt;br /&gt;
 Set: 1&lt;br /&gt;
 Counting duration: 0.014947083 seconds&lt;br /&gt;
  PM_FPU_1FLOP (FPU executed one flop instruction )          :          400093&lt;br /&gt;
  PM_FPU_FMA (FPU executed multiply-add instruction)         :          500030&lt;br /&gt;
  PM_FPU_FSQRT_FDIV (FPU executed FSQRT or FDIV instruction) :               1&lt;br /&gt;
  PM_CYC (Processor cycles)                                  :        58485795&lt;br /&gt;
  PM_RUN_INST_CMPL (Run instructions completed)              :        24238152&lt;br /&gt;
  PM_RUN_CYC (Run cycles)                                    :        70307511&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  Utilization rate                                 :          61.172 %&lt;br /&gt;
  Flop                                             :           1.400 Mflop&lt;br /&gt;
  Flop rate (flops / WCT)                          :          68.888 Mflop/s&lt;br /&gt;
  Flops / user time                                :         112.614 Mflop/s&lt;br /&gt;
  FMA percentage                                   :         111.103 %&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
There are a variety of sets of performance counters that can be reported; the default set isn't especially helpful for HPC-type computations; sets of performance counters can be specified on the commandline in the format  &amp;lt;tt&amp;gt;-d -s item,item,item&amp;lt;/tt&amp;gt;.  Sets 5 and 12 are very useful for showing memory performance (showing L1 and L2 cache misses) and set 6 is especially useful for shared memory profiling, giving statistics about how often off-processor memory had to be accessed.&lt;br /&gt;
&lt;br /&gt;
Showing the counters for the entire program will often tell you if there's a problem or not, but won't tell you where it is.  For more detailed information, one can [http://www.ncsa.uiuc.edu/UserInfo/Resources/Software/Tools/HPMToolkit/HPM_2_5_2.AIX.html  use the hpm library] to manually instrument different regions of your code, and get similar outputs to above for several different, smaller, regions of code.&lt;br /&gt;
&lt;br /&gt;
On the linux side, &amp;lt;tt&amp;gt;oprofile&amp;lt;/tt&amp;gt; allows the reporting of similar information, but to use it one must have root access to the linux machine.&lt;br /&gt;
&lt;br /&gt;
===cachegrind (Memory use analysis: GPC)===&lt;br /&gt;
&lt;br /&gt;
[[Image:Kcachegrind.png|thumb|kcachegrind, part of the KDE development package, can give graphical overviews of the output from cachegrind]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;valgrind&amp;lt;/tt&amp;gt; is a memory tool that is usually thought of in terms of finding memory-access bugs in large programs.  Rather than instrumenting a code or measuring counters, valgrind takes a fairly extreme approach -- it emulates your program running on a computer, essentially running a simulation of your program running on the same kind of computer valgrind is running on.   This has enormous overhead (runtimes can be up to 20x as long as normal) but the result is exquisitely detailed information about what your program is doing.&lt;br /&gt;
&lt;br /&gt;
Memory access is often a bottleneck for HPC codes, and cachegrind is a tool for valgrind which simulates the use of cache in your program, giving you line-by-line information on which parts of the code have cache performance issues.  Your code does not need to be recompiled, although compiling with &amp;lt;tt&amp;gt;-g&amp;lt;/tt&amp;gt; is necessary for the output to be useful.   Cachegrind is run as shown:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
valgrind --tool=cachegrind myprogram myprogram_arguments&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Overall results for the whole program are given at the end of the programs normal output, and more detailed information is saved in a file that begins with &amp;lt;tt&amp;gt;cachegrind.out&amp;lt;/tt&amp;gt;.   These output files are XML files - readable in principle by humans, but it is much easier to see what is going on with visual tools like kcachegrind (shown to the right) or, eventually, valkyrie (which also can be used for &amp;lt;tt&amp;gt;memcheck&amp;lt;/tt&amp;gt; output.)&lt;br /&gt;
&lt;br /&gt;
==Graphical Performance Tools==&lt;br /&gt;
&lt;br /&gt;
While graphical performance tools typically measure similar things to their command-line relations, a graphical display opens up the possibility of aggregating much more information and having increased flexibility in displaying it in a variety of ways; this can be very helpful, especially in the initial stages of finding performance problems.&lt;br /&gt;
&lt;br /&gt;
===OpenSpeedShop (profiling, MPI tracing: GPC)===&lt;br /&gt;
&lt;br /&gt;
[[Image:Speedshop2.png|thumb|OpenSpeedShop, like gprof will tell you where the hotspots are in the code by function]]&lt;br /&gt;
[[Image:Speedshop1.png|thumb|...or by line of code]]&lt;br /&gt;
&lt;br /&gt;
[http://www.openspeedshop.org OpenSpeedShop] is a tool that (will be) installed on GPC.  It provides the functionality of gprof, with the addition of hardware counter measurements (not currently supported on GPC machines) and options to do both lightweight and more detailed, more heavy-weight profiling.  OpenSpeedShop also contains enhanced support for dealing with parallel runs, and for tracing of MPI or I/O calls to find performance problems to those areas.   The parallel support is considerably more than what &amp;lt;tt&amp;gt;gprof&amp;lt;/tt&amp;gt; offers; bundling the data from thousands of tasks into one set of results is a significant algorithmic challenge in itself.  &lt;br /&gt;
&lt;br /&gt;
Another important addition, shared by many of the other graphical tools, is the idea of bundling results into different `experiments' --- bundles of an executable, measurement type, and resulting data --- which makes the iterative process of performance tuning much easier.  OpenSpeedShop, as with some other tools, has the ability to directly compare the results of different experiments, so one can more easily see if a particular change made things better or worse, and if so where.&lt;br /&gt;
&lt;br /&gt;
OpenSpeedshop does not require re-compilation of the executable (although as with all these tools, for the correlation with the source code to be useful, the code should be compiled with debugging symbols, the option for which is almost universally &amp;lt;tt&amp;gt;-g&amp;lt;/tt&amp;gt; to the compiler and linker.   The code is then either instrumented, or run in an instrumented environment.   Shown to the right are two of the views available for exmaining the timing results of one OpenMP code.&lt;br /&gt;
&lt;br /&gt;
OpenSpeedShop can be launched from the commandline and then used entirely through the gui: there are a variety of `wizards' which guide you through choosing how to instrument and run your experiment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ openss&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
this should be done from a directory containing the source code and the executable.   This is an excellent way to get started with this tool.  Once one is more familiar with the tool, one can run a variety of experiments on the command line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
openss –f program_name pcsamp&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where the above runs the &amp;lt;tt&amp;gt;pcsamp&amp;lt;/tt&amp;gt; (program counter sampling, as in gprof) measurement on the executable &amp;lt;tt&amp;gt;program_name&amp;lt;/tt&amp;gt;.  Then one can launch the gui to view the results.   There are options for instrumenting the executable in a variety of ways, and taking different measurements; the [http://www.openspeedshop.org OpenSpeedShop] web page contains links to documentation and tutorials.&lt;br /&gt;
&lt;br /&gt;
===PeekPerf (profiling, TCS)===&lt;br /&gt;
&lt;br /&gt;
[[Image:PeekPerf.png|thumb|An example of using PeekPerf]]&lt;br /&gt;
&lt;br /&gt;
[http://domino.research.ibm.com/comm/research_projects.nsf/pages/hpct.index.html Peekperf] is IBM's single graphical  `dashboard' providing access to many  performance measurement tools for exmaining Hardware Counter data, threads, message passing, IO, and memory access, several of which are available seperately as command-line tools.  Like OpenSpeedShop, it does not require re-compilation of the executable; an instrumented version of the code is generated at run time and this instrumented version is executed with whatever options you care to pass to it.   It does not have the same support for comparing experiments as OpenSpeedShop does, however it allows running several different types of measurements at once, seeing how they correlate in a given run; this is something OpenSpeedShop doesn't have.&lt;br /&gt;
&lt;br /&gt;
One starts peekperf at the commandline&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ peekperf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and tell peekperf which executableyou wish to run measurements on.   You then highlight which sorts of measurements you wish to make (which sorts are available depend on the type of program - threaded, OpenMPed, etc), select `generate an instrumented executable', and then `run the instrumented executable', giving it the name of either the instrumented executable or a script to run the same; the program will then begin displaying the resulting data as soon as the program has completed.  &lt;br /&gt;
&lt;br /&gt;
Understanding the interface and resulting data takes some practice, and the documentation is quite sparse; however the flexibility in the range of measurements to take makes this an excellent source of performance information for programs running on the TCS system.&lt;br /&gt;
&lt;br /&gt;
===Scalasca (profiling, tracing: TCS, GPC)===&lt;br /&gt;
[[Image:Scalasca.png|thumb|An example of using Scalasca]]&lt;br /&gt;
&lt;br /&gt;
[http://www.fz-juelich.de/jsc/scalasca/  Scalasca] is a sophisticated tool which takes the aggregation of data shown in the above graphical tools one step further and analyzes the results to pinpoint and display common performance problems; it scales extremely well and the graphical display makes it very easy for the user to find out where the performance issues are.&lt;br /&gt;
&lt;br /&gt;
Scalasca requires the code to be recompiled, and it has wrapper scripts to select and choose the right options to use.   If your code for instance is normally compiled with&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ifort -c myprog.f &lt;br /&gt;
ifort -o myprog myprog.o -lm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
then one can instead use&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
scalasca -instrument ifort -c myprog.f&lt;br /&gt;
scalasca -instrument ifort -o myprog myprog.o -lm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Scalasca then parses the rest of the command line and adds the necessary flags.   (If you are curious, &amp;lt;tt&amp;gt;scalasca -instrument -v&amp;lt;/tt&amp;gt; will show you what the resulting command line actually is.)   There is also a shortcut, &amp;lt;tt&amp;gt;skin&amp;lt;/tt&amp;gt;, which is equivalent to &amp;lt;tt&amp;gt;scalasca -instrument&amp;lt;/tt&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
When the new executable is generated, it's run in a similar way; if you normally run your program as&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
./myprog&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mpirun -np 5 ./myprog&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You'd instead do&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
scalasca -analyze ./myprog&lt;br /&gt;
scalasca -analyze mpirun -np 5 ./myprog&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The program will run as usual with only additional lines about output to files.   (Again, there is a shortcut available; &amp;lt;tt&amp;gt;scan&amp;lt;/tt&amp;gt; is equivalent to &amp;lt;tt&amp;gt;scalasca -analyze&amp;lt;/tt&amp;gt;.)  To then look at the results, one uses &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
scalasca -examine [epik directory name]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where the directory name is that created by the analyze program.&lt;br /&gt;
&lt;br /&gt;
A screenshot of the results is shown to the right for an OpenMP program, where wait times at implicit barriers at the end of parallel sections is selected as an important metric to show on the left; the middle panel shows the call tree indicating the context in which the delays occurred, and the panel on the right gives the breakdown for each thread.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- ===VTune/Thread Profiler (GPC)=== don't have license up for this yet --!&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Common Serial Performance Problems==&lt;br /&gt;
&lt;br /&gt;
===Poor use of cache===&lt;br /&gt;
A classic  problem for scientific codes is memory bandwidth; the capacity to do on-chip floating-point or integer operations has grown much faster than the ability to get numbers onto the chip in the first place.   One way around that is to use various levels of memory cache; when one number is needed from memory, a whole line of data is brought in from (slow) external memory to fast on-chip cache.  This makes the memory access modestly slower, but tends to greatly speed up performance since if you are going to do something to data in one part of memory you're typically going to be also doing to neighboring values.&lt;br /&gt;
&lt;br /&gt;
If you take advantage of data locality --- accessing memory in some kind of order rather than jumping around in memory --- cache can greatly increase the performance of your code.  On the other hand, if you '''do''' jump around in memory a lot, cache will actually hurt your performance.    &lt;br /&gt;
&lt;br /&gt;
The classic way this comes up is in accessing multidimensional arrays.  The example below is simplified; most cases aren't this extreme (or obvious!) but the idea is the same.  Let's consider the following FORTRAN code, which simply iterates a few time through a modestly sized multidimensional array:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;f&amp;quot;&amp;gt;  &lt;br /&gt;
      program memaccess&lt;br /&gt;
&lt;br /&gt;
      integer, parameter :: li=32,lj=32,lk=32,ll=32,lm=32&lt;br /&gt;
      real, dimension(li,lj,lk,ll,lm) :: a&lt;br /&gt;
      integer :: i,j,k,l,m&lt;br /&gt;
      integer :: iter&lt;br /&gt;
      &lt;br /&gt;
      a = 0.&lt;br /&gt;
&lt;br /&gt;
      do iter=1,10&lt;br /&gt;
      do m=1,lm &lt;br /&gt;
         do l=1,ll&lt;br /&gt;
             do k=1,lk&lt;br /&gt;
                 do j=1,lj&lt;br /&gt;
                     do i=1,li&lt;br /&gt;
                        a(i,j,k,l,m) = a(i,j,k,l,m)+ i+j+k+l+m&lt;br /&gt;
                     enddo&lt;br /&gt;
                 enddo&lt;br /&gt;
             enddo&lt;br /&gt;
         enddo&lt;br /&gt;
      enddo&lt;br /&gt;
      enddo&lt;br /&gt;
&lt;br /&gt;
      end program&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
The above program, which we'll suggestively call &amp;lt;tt&amp;gt;memaccess-good.f&amp;lt;/tt&amp;gt;, accesses array elements in the order that FORTRAN places in them in the computer's memory; FORTRAN lays out this array in memory as &amp;lt;tt&amp;gt;[a(1,1,1,1,1), a(2,1,1,1,1),... a(31,1,1,1,1), a(1,2,1,1,1)...]&amp;lt;/tt&amp;gt; and so on.  So by ordering our loops that way we are marching through memory in order, making maximum use of cache.    The resulting code can be timed:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ gfortran -O3 -o memaccess-good memaccess-good.f &lt;br /&gt;
$ time ./memaccess-good&lt;br /&gt;
&lt;br /&gt;
real    0m2.478s&lt;br /&gt;
user    0m2.337s&lt;br /&gt;
sys     0m0.094s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we reverse the order of the loops, so they go &amp;lt;tt&amp;gt; do i=1,li.. do j=1,lj,..... do m=1,lm&amp;lt;/tt&amp;gt;, however, we get&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ gfortran -O3 -o memaccess-bad memaccess-bad.f&lt;br /&gt;
$ time ./memaccess-bad&lt;br /&gt;
&lt;br /&gt;
real    0m19.622s&lt;br /&gt;
user    0m19.101s&lt;br /&gt;
sys     0m0.098s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A factor of 8 times worse!   Thus tools such as cachegrind can be extremely important for finding significant performance problems in memory-heavy codes.  &lt;br /&gt;
&lt;br /&gt;
C-based languages arrange their arrays the opposite ways in memory, so that the equivalent array in C would go as &amp;lt;tt&amp;gt;[a[0][0][0][0][0], a[0][0][0][0][1], ... a[0][0][0][0][31], a[0][0][0][1][0], ... ]&amp;lt;/tt&amp;gt;; thus `bad' array access in FORTRAN looks like `good' array access in C, and vice versa.&lt;br /&gt;
&lt;br /&gt;
==Common OpenMP Performance Problems==&lt;br /&gt;
&lt;br /&gt;
==Common MPI Performance Problems==&lt;br /&gt;
===Overuse of MPI_BARRIER===&lt;br /&gt;
===Many Small Messages===&lt;br /&gt;
Typically, a the time it takes for a message of size ''n'' to get from one node to another can be expressed in terms of a [[latency]] ''l'' and a [[bandwidth]] ''b'',&lt;br /&gt;
&amp;lt;math&amp;gt;t_c = l + \frac{n}{b} .&amp;lt;/math&amp;gt;&lt;br /&gt;
For small messages, the latency can dominate the cost of sending (and processing!) the message.  By&lt;br /&gt;
bundling many small messages into one, you can amortize that cost over many messages,  reducing&lt;br /&gt;
the time spent communicating.&lt;br /&gt;
===Not overlapping computation and communications===&lt;/div&gt;</summary>
		<author><name>Groer</name></author>
	</entry>
	<entry>
		<id>https://oldwiki.scinet.utoronto.ca/index.php?title=GPC_Quickstart&amp;diff=457</id>
		<title>GPC Quickstart</title>
		<link rel="alternate" type="text/html" href="https://oldwiki.scinet.utoronto.ca/index.php?title=GPC_Quickstart&amp;diff=457"/>
		<updated>2009-08-13T19:01:23Z</updated>

		<summary type="html">&lt;p&gt;Groer: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Computer&lt;br /&gt;
|image=[[Image:University_of_Tor_79284gm-a.jpg|center|300px|thumb]]&lt;br /&gt;
|name=General Purpose Cluster (GPC)&lt;br /&gt;
|installed=June 2009&lt;br /&gt;
|operatingsystem= Linux&lt;br /&gt;
|loginnode= gpc01..gpc04 (from &amp;lt;tt&amp;gt;login.scinet&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|numberofnodes=3780&lt;br /&gt;
|rampernode=16 Gb &lt;br /&gt;
|corespernode=8&lt;br /&gt;
|interconnect=1/4 on Infiniband, rest on GigE&lt;br /&gt;
|vendorcompilers=icc (C) ifort (fortran) icpc (C++)&lt;br /&gt;
|queuetype=[[Moab/Torque]]&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
The General Purpose Cluster is an extremely large cluster (ranked [http://www.top500.org/list/2009/06/100 16th] in the world at its inception, and fastest in Canada) and is where most simulations are to be done at SciNet.  It is an IBM iDataPlex cluster based on Intel's Nehalem architecture (one of the [http://www.hpcwire.com/features/HPC-Vendors-Jump-On-Nehalem-42360237.html first in the world] to make use of the new chips). The GPC consists of 3,780 nodes with a total of 30,240  2.5GHz cores, with 16GB RAM per node (2GB per core). Approximately one quarter of the cluster is interconnected with non-blocking 4x-DDR InfiniBand while the rest of the nodes are connected with gigabit ethernet. &lt;br /&gt;
&lt;br /&gt;
===Login===&lt;br /&gt;
&lt;br /&gt;
First login via ssh with your scinet account at &amp;lt;tt&amp;gt;login.scinet.utoronto.ca&amp;lt;/tt&amp;gt;, and from there you can proceed to the Development nodes to compile/test your code.&lt;br /&gt;
&lt;br /&gt;
===Compile/Devel Nodes===&lt;br /&gt;
&lt;br /&gt;
From a scinet login node you can ssh to &amp;lt;tt&amp;gt;gpc01&amp;lt;/tt&amp;gt;..&amp;lt;tt&amp;gt;gpc04&amp;lt;/tt&amp;gt;.  These nodes have the same hardware configuration as most of the compute nodes -- 8 Nehalem processing cores with 16GB RAM and Gigabit ethernet.  You can compile and test your codes on these nodes. To interactively test on more than 8 processors, or to test your code over an InfiniBand connection, you can submit an [[GPC_Quickstart#Submitting_an_Interactive_Job | interactive job request]].&lt;br /&gt;
&lt;br /&gt;
Your [[Storage_Quickstart | home directory]] is in &amp;lt;tt&amp;gt;/home/USER&amp;lt;/tt&amp;gt;; you have 10GB there that is backed up. This directory cannot be written to by the compute nodes! Thus, to run jobs, you'll use the &amp;lt;tt&amp;gt;/scratch/USER&amp;lt;/tt&amp;gt; directory. Here, there is a large amount of disk space, but it is not backed up. Thus it makes sense to keep your codes in /home, compile there, and then run them in the /scratch directory.&lt;br /&gt;
&lt;br /&gt;
===Environment Variables===&lt;br /&gt;
&lt;br /&gt;
A modules system is used to handle environment variables associated with different compilers, MPI versions, libraries etc.  To see all&lt;br /&gt;
the options available type &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
module avail&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
To load a module&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
module load intel&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
To unload a module&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
module unload intel&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
To unload all modules&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
module purge&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
These commands should go in your .bashrc files and/or in your submission scripts to make sure you&lt;br /&gt;
are using the correct packages.&lt;br /&gt;
&lt;br /&gt;
===Compilers===&lt;br /&gt;
&lt;br /&gt;
The intel compilers are icc/icpc/ifort for C/C++/Fortran, and are available with the default module &amp;quot;intel&amp;quot;.  The latest version of the GNU compiler suite (currently 4.4.0) is available by loading the &amp;quot;gcc&amp;quot; module.  To ensure that the intel compilers are in your &amp;lt;tt&amp;gt;PATH&amp;lt;/tt&amp;gt; and their libraries are in your &amp;lt;tt&amp;gt;LD_LIBRARY_PATH&amp;lt;/tt&amp;gt;, use the command&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
module load intel&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should likely go in your &amp;lt;tt&amp;gt;.bashrc&amp;lt;/tt&amp;gt; file so that it will automatically be loaded.&lt;br /&gt;
&lt;br /&gt;
===MPI===&lt;br /&gt;
&lt;br /&gt;
SciNet currently provides two sets of MPI libraries for the GPC [http://www.open-mpi.org/ OpenMPI] and [http://mvapich.cse.ohio-state.edu/ MVAPICH2].   Both sets of libraries will automatically work with both the infiniband and gigabit ethernet interconnects on the GPC system.   We recommend OpenMPI as the default, as it quite reliably demonstrates good performance.  &lt;br /&gt;
&lt;br /&gt;
Both sets of libraries are compiled with the gnu compiler suite and the intel compiler suite.   To use (for instance) the intel-compiled OpenMPI libraries, which we recommend as the default (and use for most of our examples here), use&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
module load openmpi intel&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
in your &amp;lt;tt&amp;gt;.bashrc&amp;lt;/tt&amp;gt;.   Other combinations behave similarly.&lt;br /&gt;
&lt;br /&gt;
Both sets of MPI libraries define the wrappers mpicc/mpicxx/mpif90/mpif77 as wrappers around the appropriate compilers, which ensure the appropriate include and library directories and used in the compilation and linking steps.&lt;br /&gt;
&lt;br /&gt;
We currently recommend the Intel + OpenMPI combination.  However, if you require the GNU compilers as well as MPI, then the module combination&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
module load gcc openmpi/1.3.2-gcc-v4.4.0-ofed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will enable development and runtime with gcc/g++/gfortran version 4.4 and OpenMPI version 1.3.2.  You can make this your default by putting the module load line in your ~/.bashrc file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Submitting A Batch Job===&lt;br /&gt;
&lt;br /&gt;
The SciNet machines are shared systems, and jobs that are to run on them are submitted to a queue; the&lt;br /&gt;
[[scheduler]] then orders the jobs in order to make the best use of the machine, and has them launched &lt;br /&gt;
when resources become availble.   The intervention of the scheduler can mean that the jobs aren't&lt;br /&gt;
quite run in a  first-in first-out order.&lt;br /&gt;
&lt;br /&gt;
The maximum [[wallclock time]] for a job in the queue is 48 hours; computations that will take longer than&lt;br /&gt;
this must be broken into 48-hour chunks and run as several jobs.  The usual way to do this is with [[checkpoints]],&lt;br /&gt;
writing out the complete state of the computation every so often in such a way that a job can be restarted from&lt;br /&gt;
this state information and continue on from where it left off.  Generating [[checkpoints]] is a good idea anyway,&lt;br /&gt;
as in the unlikely event of a hardware failure during your run, it allows you to restart without having lost much work.&lt;br /&gt;
&lt;br /&gt;
If your job should run in fewer than  48 hours, specify that in your script -- your job &lt;br /&gt;
will start sooner.   (It's easier for the [[scheduler]] to fit in a short job than a long job).  On the downside, the&lt;br /&gt;
job will be killed automatically by the queue manager software at the end of the specified [[wallclock time]], so if you&lt;br /&gt;
guess wrong you might lose some work.  So the standard procedure is to estimate how long your job will take and&lt;br /&gt;
add 10% or so. &lt;br /&gt;
&lt;br /&gt;
You interact with the queuing system through the queue/resource manager, [[Moab]] and [[Torque]].  To see all the jobs in the queue use&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
showq&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To submit your own job, you must write a script which describes the job and how it is to be run (a sample script [[GPC_Quickstart#Submission_Script | follows]]) and submit it to the queue, using the command&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
qsub SCRIPT-FILE-NAME&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
where you will replace &amp;lt;tt&amp;gt;SCRIPT-FILE-NAME&amp;lt;/tt&amp;gt; with the file containing the submission script.   This will return a job ID, for example 31415, which is used to identify the jobs.  Information about a queued job can be found using&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
checkjob JOB-ID&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
and jobs can be canceled with the command&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
canceljob JOB-ID&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Again, these commands have many options, which can be read about on their man pages.&lt;br /&gt;
&lt;br /&gt;
Much more information on the queueing system is available on our [[queue]] page.&lt;br /&gt;
&lt;br /&gt;
====Batch Submission Script====&lt;br /&gt;
&lt;br /&gt;
A sample submission script is shown below for an mpi job using ethernet with the &amp;lt;tt&amp;gt; #PBS &amp;lt;/tt&amp;gt; directives at the top and the rest being &lt;br /&gt;
what will be executed on the compute node.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
# MOAB/Torque submission script for SciNet GPC (ethernet)&lt;br /&gt;
#&lt;br /&gt;
#PBS -l nodes=2:ppn=8,walltime=1:00:00&lt;br /&gt;
#PBS -N test&lt;br /&gt;
&lt;br /&gt;
# DIRECTORY TO RUN - $PBS_O_WORDIR is directory job was submitted from&lt;br /&gt;
cd $PBS_O_WORKDIR&lt;br /&gt;
&lt;br /&gt;
# EXECUTION COMMAND; -np = nodes*ppn&lt;br /&gt;
mpirun -np 16 -hostfile $PBS_NODEFILE ./a.out&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The script above requests two nodes, using 8 processors per node, for a [[wallclock time]] of one hour.  (The resources required by the job are listed on the &amp;lt;tt&amp;gt;#PBS -l&amp;lt;/tt&amp;gt; line.)   Other options can be given in other &amp;lt;tt&amp;gt;#PBS&amp;lt;/tt&amp;gt; lines, such as &amp;lt;tt&amp;gt;#PBS -N&amp;lt;/tt&amp;gt;, which sets the the name of the job.   On the first of the two nodes, a shell is launched that changes directory to &amp;lt;tt&amp;gt;/scratch/USER/SOMEDIRECTORY&amp;lt;/tt&amp;gt; and then uses the &amp;lt;tt&amp;gt;mpirun&amp;lt;/tt&amp;gt; command to launch the job.   Assumed here is that the user has a line like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
module load openmpi intel&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
in their &amp;lt;tt&amp;gt;.bashrc&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Submitting an Interactive Job===&lt;br /&gt;
&lt;br /&gt;
It is sometimes convenient to run a job interactively; this can be very handy for debugging purposes.  In this case, you type a &amp;lt;tt&amp;gt;qsub&amp;lt;/tt&amp;gt; command which submits an interactive job to the queue; when the scheduler selects this job to run, then it starts a shell running on the first node of the job, which connects to your terminal.  You can then type any series of commands (for instance, the same commands listed as in the batch submission script above) to run a job interactively.&lt;br /&gt;
&lt;br /&gt;
For example, to start the same sort of job as in the batch submission script above, but interactively, one would type&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ qsub -I -l nodes=2:ppn=8,walltime=1:00:00&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is exactly the &amp;lt;tt&amp;gt;#PBS -l&amp;lt;/tt&amp;gt; line in the batch script above (which requests all 8 processors on each of 2 nodes for one hour), but prepended with a &amp;lt;tt&amp;gt;-I&amp;lt;/tt&amp;gt; for `interactive'.   When this job begins, your terminal will now show you as being logged in to one of the compute nodes, and one can type in any shell command, run &amp;lt;tt&amp;gt;mpirun&amp;lt;/tt&amp;gt;, etc.   When you exit the shell, the job will end.&lt;br /&gt;
&lt;br /&gt;
===Ethernet vs. Infiniband===&lt;br /&gt;
&lt;br /&gt;
About 1/4 of the GPC (862 nodes or 6896 cores) is connected with a high bandwidth low-latency fabric called&lt;br /&gt;
[http://en.wikipedia.org/wiki/InfiniBand InfiniBand].  Many jobs which require tight coupling to scale well greatly benefit from this interconnect;&lt;br /&gt;
other types of jobs, which have relatively modest communications, do not require this and run fine on Gigabit ethernet.&lt;br /&gt;
&lt;br /&gt;
Jobs which require the InfiniBand for good performance can request the nodes that have the `&amp;lt;tt&amp;gt;ib&amp;lt;/tt&amp;gt;' feature in the &amp;lt;tt&amp;gt;#PBS -l&amp;lt;/tt&amp;gt; line,&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#PBS -l nodes=2:ib:ppn=8,walltime=1:00:00&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Because there are a limited number of these nodes, your job will run faster if you do not request them (e.g. if you use the scripts as shown above), as this increases the number of nodes available to run your job. In fact, the InfiniBand nodes are to be used only for jobs that are known to scale well and  will benefit from this type of interconnect.    The MPI libraries provided by SciNet automatically correctly use either the InfiniBand or ethernet interconnect depending on which nodes your job runs on.&lt;br /&gt;
&lt;br /&gt;
===Large Memory Nodes===&lt;br /&gt;
&lt;br /&gt;
There are two stand-alone large memory (128GB) nodes, &amp;lt;tt&amp;gt;gpc-lrgmem01&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;gpc-lrgmem02&amp;lt;/tt&amp;gt; which can be used for data analysis of runs.  They have 8 cores and are intel machines running linux, but they are not the same architecture (Nehalem) as the GPC compute nodes, so codes will have to be compiled separately for these machines.  They can be logged into from &amp;lt;tt&amp;gt;login.scinet.utoronto.ca&amp;lt;/tt&amp;gt;.&lt;/div&gt;</summary>
		<author><name>Groer</name></author>
	</entry>
</feed>