Tuesday, July 31, 2012

Matlab vs Go: Performance

I'll keep it brief.

I have some parallel CPU-bound code for financial analysis in Matlab, that doesn't make use of any special Matlab package and lags a bit in terms of performance. I decided it would be easy and fun to translate it into Go and see how well it would run.

In Matlab, I made use of the parfor construct that runs a given piece of code over the elements of an array, distributing the computation among a pool of workers, either local (as in my case) or remote. No such thing exists in Go, but it only took about 10 lines of code to implement a local parfor based on goroutines.

I tested the speedup of both codes going from 1 to 4 processes/goroutines (on a quad-core machine). It was nice to see some consistency in the numbers with Matlab showing a speedup of 2.58 and Go of 2.62.

I then compared pure execution times and, hear, hear, in both sequential and parallel scenarios I got an improvement of about 13.5 times from Matlab to Go.

Quite impressive. The code I used is pretty much just fixed/floating point arithmetic over large collections of data in memory. Other kinds of programs may yield different results, and of course if you're programming in Matlab you'll have the advantage of being able to use its vast collection of technical packages.

All in all, way to Go!

Sunday, July 29, 2012

Profiling Go with pprof under Windows

First post on a new blog! Ring the bell.
But no time for chitchat now... :)

So, I was trying to write some computationally intensive code in Go (gc 1.0.2 Win64) in the past few days and a couple of things about performance didn't seem quite right (more on this later).
Hence I looked into profiling options and found references to pprof and a nice article by Russ Cox about it. I eagerly dove into it, only to be quickly disappointed by an output that looked like this:

(pprof) top10
Total: 2113 samples
     298  14.1%  14.1%      298  14.1% 0000000000464d34
     179   8.5%  22.6%      179   8.5% 0000000000418e83
     157   7.4%  30.0%      157   7.4% 0000000000418e60
     112   5.3%  35.3%      112   5.3% 0000000000403293
     101   4.8%  40.1%      101   4.8% 0000000000464d4f
      83   3.9%  44.0%       83   3.9% 000000000040329c
      77   3.6%  47.7%       77   3.6% 0000000000418e7a
      62   2.9%  50.6%       62   2.9% 0000000000456a38
      37   1.8%  52.3%       37   1.8% 0000000000418e41
      37   1.8%  54.1%       37   1.8% 0000000000435f57


No function names, only addresses. A quick Googling led to a couple of unsolved forum questions about the same problem, pointing at Windows as a common factor. Reluctant at the prospect of rebooting and moving my work over to Ubuntu I set out to see if pprof (a Perl script under Go\pkg\tool\[platform]) could be easily fixed.

Suprises suprise, it was.

Two issues were preventing the script from working under Windows:
  1. The use of /tmp for temp files. I used $ENV{'TEMP'} instead.
  2. Redirecting to /dev/null to test availability of external commands. Using the NUL file instead works under Win, in principle (if the command line options of those commands, such as nm, are the same). It turns out that's not quite enough, but adding one command ("$nm -n $image") to the list of candidates in the sub GetProcedureBoundaries does the trick.
Finally I was able to get some readable output:

(pprof) top
Total: 3361 samples
    1043  31.0%  31.0%     1043  31.0% main.For5
     333   9.9%  40.9%      333   9.9% CompareAndSwapUint32
     303   9.0%  50.0%      303   9.0% runtime.procyield
     252   7.5%  57.5%      252   7.5% runtime.casp
     166   4.9%  62.4%     1068  31.8% sync.(*Mutex).Lock
     124   3.7%  66.1%      124   3.7% runtime.atomicload
     113   3.4%  69.4%      113   3.4% runtime.xadd
     110   3.3%  72.7%      110   3.3% runtime.atomicloadp
     104   3.1%  75.8%      104   3.1% math/rand.Int63
      83   2.5%  78.3%       83   2.5% sync/atomic.AddUint32

Nice!

The fixes above are just enough to get the text output working. A couple more were needed for the web/graph visualisation.
A modified script can be found here.
A simple DIFF with the supplied version can reveal my changes.

If you're just getting started with Go, in order to get all this to work, you will need to:
  1. Install Perl (e.g. ActivePerl).
  2. Add [YourPerl]\bin to your system or user PATH variable.
  3. Add Go\bin to your system or user PATH variable.
  4. Add  Go\pkg\tool\[platform] to your system or user PATH variable.
  5. Under Windows, launch pprof with:
    > perl  [...]\Go\pkg\tool\[platform]\pprof_win.pl [executable] [profile]
  6. Optional: if you want the web option to work, install graphviz and add its bin folder to your system or user PATH variable.
Ignore the error messages about files not found, and everything should work from the Windows command prompt. You may also be able to use the version of pprof supplied with go with shells like cygwin, MinGW, or Gow.

Enjoy!