HPCE / hpce-2018-cw5

2 stars 5 forks source link

The deadline for this assignment is

22:00 Mon 4 Mar 2019

Code infrastructure

This repository contains a simple framework for describing and executing small computational puzzles. Each puzzle is derived from puzzler::Puzzle, which provides a number of methods for creating inputs, executing puzzles, and checking outputs. Of particular interest to you is the puzzler::Puzzle::Execute method, which is the method that you need to accelerate, and is the function that is timed for performance purposes. Each input problem has an associated "scale", which is a measure of computational complexity - in general, as the scale number goes up, the problem will take longer.

There are four puzzles that you need to work on as a pair:

Each puzzle represents the computational core of some common calulation, though all have been simplified to just a few lines of code. It's not too difficult to work out what the underlying application is, and you may find it interesting to look at existing solutions in the literature. However, no specialised knowledge of each puzzles application domain is needed, and the expectation is that only the standard techniques from the lectures and earlier courseworks are needed.

The repository has a number of different directories:

The reason for all this strange indirection is that I want to give maximum freedom for you to do strange things within your implementation (example definitions of "strange" include CMake) while still having a clean abstraction layer between your code and the client code.

Deliverables

The deliverable sections are:

Performance metric

Performance is defined in terms of the number of puzzles of increasing scale executed within a fixed time budget. The time budget is fixed at $t_B=60$ seconds for each puzzle, and the budget includes the entirety of Puzzle::Execute.

The sequence of scales is determined according to a schedule for each puzzle, which maps an index $i>=1$ to a sequence of scales $s_1,s_2,s_3,...$. The scale schedule is intended to match the inherent scaling properties of each puzzle, and balances some exploration of smaller scales, while also allowing faster implementations to push up to larger scales. The sequence as given is defined ising a random term $U$ to make it non-deterministic; so it is possible to work out what the sequence looks like, but not the precise numbers (to avoid over-fitting). $U$ is a uniform random number in the range $[0,1)$. For assessment purposes, the sequence of input scales and actual inputs for a given puzzle will be identical across all submissions.

To create a continuous metric, the puzzle which is executing when the timer expires will be added pro-rata based on "how far" it had got through. If we have a sequence of scales $s_1, s_2, s_3, ...$ and an implementation which takes $f(s)$ seconds at scale $s$, then the puzzles in the sequence will complete at $tj = \sum{i=1}^{j} f(i)$. The sequence of complete times $t_1,t_2,t3, ... t{n},t_{n+1}$, will eventually exceed the time budget $tB$ at some point $n$, which occurs when $t{n} <= tB < t{n+1}$. We consider $n$ puzzles to be completed, and $(tB-t{n})/(t{n+1}-t{n})$ of the final puzzle to have been completed, for a final metric of:

n+(t_B-t_{n})/(t_{n+1}-t_{n})

In order to manage execution time in assessment (e.g. in the case of an infinite loop), if $1.5*tB < t{n+1}$ then we assume that $t_{n+1} = \infty$. Practically speaking this means that if $t_B=60$ then the timeout for the final puzzle is 90 seconds.

Puzzle Sequence Reference Competent Expert
rank 50+(i*30+U*20)^2 43.3910 44.7968 60.0788
decompose 20+(i*30+U*10)^1.2 15.7040 27.2232 38.1773
ising 20+(i*20+U*5) 18.5639 18.7988 35.9099
integral 20+(i*20+U*5) 23.2298 37.3449 170.683

The expert observed that they weren't at the limits of performance on any puzzle, only of implementation time, so if you end up with higher performance don't assume you've done something wrong.

Getting Started

Drop into the root of the repo and do make all. This should produce a number of executables:

The different programs are intended to make it easy to write infrastructure for testing performance and correctness. For example, if one wished to test an engine called "ising.tbb" at a number of different scales, one could script something along these lines:

#!/bin/bash
ENGINE="ising.tbb"
PUZZLE=${ENGINE%%.*}
SCALES="100 150 200"
WORKING=.tmp
mkdir ${WORKING}
for SCALE in $SCALES ; do
  bin/create_puzzle_input ${PUZZLE} ${SCALE} > ${WORKING}/${PUZZLE}.${SCALE}.input
  cat ${WORKING}/${PUZZLE}.${SCALE}.input | bin/execute_puzzle "ref" > ${WORKING}/${PUZZLE}.${SCALE}.ref
  cat ${WORKING}/${PUZZLE}.${SCALE}.input | bin/execute_puzzle ${ENGINE} > ${WORKING}/${PUZZLE}.${SCALE}.${ENGINE}.got
  bin/compare_puzzle_output ${WORKING}/${PUZZLE}.${SCALE}.input ${WORKING}/${PUZZLE}.${SCALE}.ref ${WORKING}/${PUZZLE}.${SCALE}.${ENGINE}.got
  if [[ $? -ne 0 ]] ; then
    >&2 echo "FAIL"
    exit 1
  fi
done

Submission

The code in github forms your submission, though you must submit your final hash via blackboard for time-keeping and non-repudiation purposes. Pushes to github after the deadline will not be treated as submissions, unless the new hash is also submitted after the deadline. All members of each group must submit the same hash to show agreement.