Mockingjay

deadly clickthroughs and other jabberjays

Haskell on Haswell

How does Haskell run on the new MacBook Air with the Haswell chip? In short, just fine. The mid-2013 MBA represents a practical tradeoff of a little CPU power for a lot of battery life; for Haskellers on the go, the Mac is a solid choice for software development.

Newbies can install Haskell on Mac with ease, using the prebuilt Haskell Platform installer, a typical Mac PKG compressed in a DMG virtual disk. Don’t forget to eject the disk, you silly goose.

Expert Haskellers may want to compile Haskell directly from source, with brew install haskell-platform. On newer Macs, this formula requires a minor tweak to gcc/clang in order to install, but the resulting Haskell Platform it creates in /usr/local/Cellar/haskell-platform/ is fully operational.

Haskell 7 was tested on a 13” mid-2013 MacBook Pro, with the 1.7GHz i7 version of the processor, and full upgrades to RAM and SDD.

$ specs haskell os ram hd cpu hardware
Specs:

specs 0.4
https://github.com/mcandre/specs#readme

cabal --version
cabal-install version 1.16.0.2
using version 1.16.0 of the Cabal library 

ghc --version
The Glorious Glasgow Haskell Compilation System, version 7.6.3

ghc-pkg field haskell-platform version
version: 2013.2.0.0

system_profiler SPSoftwareDataType | grep 'System Version'
System Version: OS X 10.8.4 (12E3200)

system_profiler | grep 'Memory:'
Memory: 8 GB
Memory:
Secure Virtual Memory: Enabled

df -h
Filesystem      Size   Used  Avail Capacity  iused     ifree %iused  Mounted on
/dev/disk0s2   465Gi   56Gi  409Gi    12% 14634549 107293680   12%   /
devfs          188Ki  188Ki    0Bi   100%      650         0  100%   /dev
map -hosts       0Bi    0Bi    0Bi   100%        0         0  100%   /net
map auto_home    0Bi    0Bi    0Bi   100%        0         0  100%   /home

system_profiler | grep Cores: 
Total Number of Cores: 2

system_profiler | grep Processors:
Number of Processors: 1

system_profiler | grep 'Model Identifier'
Model Identifier: MacBookAir6,2

For our first test, Hello World would be just too easy. Instead, we’ll compile a genetic algorithm to sift through the space of random strings until we eventually reach Hello World by testing each random sample for fitness to each of the characters in "Hello World!".

$ git clone https://github.com/mcandre/genetics
$ cd genetics/
$ cabal install -p random-fu random-source
$ make profile
ghc --make -O2 -threaded -rtsopts hellogenetics.hs -package base -package random-fu -package random-source -prof -auto-all -caf-all -o hellogenetics-profile
[1 of 2] Compiling Genetics         ( Genetics.hs, Genetics.o )
[2 of 2] Compiling Main             ( hellogenetics.hs, hellogenetics.o )
Linking hellogenetics-profile ...
time ./hellogenetics-profile +RTS -N1 -p -hc
Hello World!
27.79 real        26.47 user         1.17 sys

That’s <30 seconds from primordial soup to modern homo worldus. If the number of generations in hellogenetics.hs is lowered to 2 ^ 10, the program completes much faster, (0.41 real time), but the outputs tend to look like HZllo)Wo-kd!, hardly the civilized Hello World as we know him.

Moving on to the second program, we download an interpreted Cisco IOS7 password decryptor.

$ git clone https://github.com/mcandre/mcandre
$ cd mcandre/haskell/

$ ./ios7crypt.hs -h
ios7crypt.hs
-e <password>  --encrypt=<password>  Encrypt a password
-d <hash>      --decrypt=<hash>      Decrypt a hash
-t             --test                Unit test IOS7Crypt
-h             --help                Display usage information

$ ./ios7crypt.hs -e monkey
04560408042455
$ ./ios7crypt.hs -d 04560408042455
monkey

$ time ./ios7crypt.hs -t
+++ OK, passed 100 tests.

real    0m0.604s
user    0m0.530s
sys 0m0.066s

That makes for 100 passwords decrypted in slightly over half a second (0.604), not terrible, but not great.

Pro tip for Windows users: When running the same benchmark, either substitute ./ios7crypt.hs with runhaskell ios7crypt.hs for compatibility with cmd; or run ./ios7crypt.hs inside of git bash.

If someone wanted to break a bunch of router hashes in Haskell, he’d best rewrite the program in Cloud Haskell, farmed out to a thousand MacBook Airs.

On that note, we benchmark a parallel processing program for solving Project Euler 14, finding the longest Hailstone sequence under 1,000,000 elements.

$ git clone https://github.com/mcandre/projecteuler
$ tail problem14.hs 
longestChainIn = foldl maxLength [] . parMap rseq hailstone

longestChainUnder :: Integer -> Integer
longestChainUnder n = head $ (evens :: [Integer]) `par` (odds :: [Integer]) `pseq` maxLength evens odds
where
evens = longestChainIn [0, 2 .. n]
odds = longestChainIn [1, 3 .. n]

main :: IO ()
main = (print . longestChainUnder) 1000000
$ time ./problem14.hs -RTS -N
837799

real    3m26.333s
user    3m17.689s
sys 0m5.247s

The compiled version runs a bit faster, but it’s still not much fun to watch. Maybe a good time to heat up a slice of pizza.

$ ghc --make problem14.hs -with-rtsopts='-K5000M -RTS -N' -threaded -rtsopts -prof
$ time ./problem14 -RTS -N
837799

real    1m17.886s
user    1m0.447s
sys 0m9.234s

All in all, Haskell on Haswell is responsive and easy to use. Not bad for a laptop that plays League of Legends at a choppy 30 FPS on medium graphics, while compiling and running Haskell unit tests in the background.