Skip to content

Commit

Permalink
feature(bitwatts-eurosys-2015): add scripts relating to the article
Browse files Browse the repository at this point in the history
Adds the scripts used for processing and for generating charts displayed in the article.
  • Loading branch information
mcolmant committed Jun 3, 2015
1 parent 8b11658 commit 0222aab
Show file tree
Hide file tree
Showing 7 changed files with 275 additions and 10 deletions.
Expand Up @@ -39,7 +39,7 @@ import scalax.file.Path
*
* @author <a href="mailto:maxime.colmant@gmail.com">Maxime Colmant</a>
*/
object BitWatts extends App with Configuration {
object BitWatts extends Configuration(None) with App {
/**
* Main configuration.
*/
Expand Down Expand Up @@ -67,12 +67,15 @@ object BitWatts extends App with Configuration {
lazy val PSFormat = """\s*([\d]+)\s.*""".r

val currentPid = java.lang.management.ManagementFactory.getRuntimeMXBean.getName.split("@")(0).toInt
val libpfmHelper = new LibpfmHelper
libpfmHelper.init()

@volatile var powerMeters = Seq[PowerMeter]()

val shutdownHookThread = scala.sys.ShutdownHookThread {
powerMeters.foreach(powerMeter => powerMeter.shutdown())
powerMeters = Seq()
libpfmHelper.deinit()
}

def figure9Experiment(): Unit = {
Expand All @@ -93,9 +96,7 @@ object BitWatts extends App with Configuration {
}
}

LibpfmHelper.init()

val powerapi = PowerMeter.loadModule(LibpfmCoreModule())
val powerapi = PowerMeter.loadModule(LibpfmCoreModule(None, libpfmHelper))
powerMeters :+= powerapi
val externalPMeter = PowerMeter.loadModule(PowerSpyModule())
powerMeters :+= externalPMeter
Expand Down Expand Up @@ -153,7 +154,6 @@ object BitWatts extends App with Configuration {

externalPMeter.shutdown()
powerapi.shutdown()
LibpfmHelper.deinit()
powerMeters = Seq()
}

Expand All @@ -178,9 +178,7 @@ object BitWatts extends App with Configuration {
}
}

LibpfmHelper.init()

val powerapi = PowerMeter.loadModule(LibpfmCoreProcessModule())
val powerapi = PowerMeter.loadModule(LibpfmCoreProcessModule(None, libpfmHelper))
powerMeters :+= powerapi
val externalPMeter = PowerMeter.loadModule(PowerSpyModule())
powerMeters :+= externalPMeter
Expand Down Expand Up @@ -257,14 +255,13 @@ object BitWatts extends App with Configuration {
path.moveTo(Path(s"$figure10P/${path.name}", '/'), true)
})

LibpfmHelper.deinit()
powerMeters = List()
}

def printHelp(): Unit = {
val str =
"""
|BitWatts Spirals Team / University of Neuchatel"
|BitWatts Spirals Team / University of Neuchâtel"
|
|EuroSys host experiments.
|
Expand Down
26 changes: 26 additions & 0 deletions bitwatts-eurosys-2015/src/universal/scripts/figure10/figure10.gp
@@ -0,0 +1,26 @@
# Plot the data processed with the process.py script.
# @author: Maxime Colmant

set term postscript color eps enhanced 22
set output "figure10.eps"

set ylabel "Power (W)"
set xlabel "Time (sec)"

set format x "%.0s"
set format y "%.0s"

set pointsize 0.75
set key below horizontal center

mdblue = '#000066'
hp1 = '#d7301f'
hp2 = '#fc8d59'
hpapi = '#fdcc8a'
hidle = '#fef0d9'

plot "results/data.csv" using 0:($4+$3+$2+$1) title 'x264' lc rgb hp1 lt 1 lw 4 with filledcurves x1, \
"results/data.csv" using 0:($4+$3+$2) title 'freqmine' lc rgb hp2 lt 3 lw 4 with filledcurves x1, \
"results/data.csv" using 0:($4+$3) title 'BitWatts' lc rgb hpapi lt 2 lw 4 with filledcurves x1, \
"results/data.csv" using 0:($4) title 'Idle power' lc rgb hidle lt 1 lw 4 with filledcurves x1, \
"results/data.csv" using 0:($5) title 'PowerSpy' lc rgb mdblue lt -1 lw 2 with lines
105 changes: 105 additions & 0 deletions bitwatts-eurosys-2015/src/universal/scripts/figure10/process.py
@@ -0,0 +1,105 @@
# Script to process the data used for plotting the Figure 10.
# The data come from the corresponding experiment in bitwatts-eurosys-2015.
# @author: Maxime Colmant
import os
import glob
import re
import numpy as np
import csv
import itertools as it
import shutil
import argparse

parser = argparse.ArgumentParser()
parser.add_argument("path", help="experiment data path.")
parser.add_argument('idle', help = 'idle power of the machine.')
args = parser.parse_args()

separator = '=\n'
powers_filename = 'output-external.dat'
powerapi_filename = 'output-powerapi.dat'
powerapi_p1_filename = 'output-powerapi-p1.dat'
powerapi_p2_filename = 'output-powerapi-p2.dat'

output_path = os.path.join('results')

# Creates the output data directory
if os.path.exists(output_path):
shutil.rmtree(output_path)

os.mkdir(output_path)

# Cleans the result file
if os.path.isfile('data.csv'):
os.remove('data.csv')

data = []
relative_errors = []

real_powers_f = open(os.path.join(args.path, powers_filename))
real_powers_l = real_powers_f.readlines()
real_powers_f.close()

papi_powers_f = open(os.path.join(args.path, powerapi_filename))
papi_powers_l = papi_powers_f.readlines()
papi_powers_f.close()

papi_p1_powers_f = open(os.path.join(args.path, powerapi_p1_filename))
papi_p1_powers_l = papi_p1_powers_f.readlines()
papi_p1_powers_f.close()

papi_p2_powers_f = open(os.path.join(args.path, powerapi_p2_filename))
papi_p2_powers_l = papi_p2_powers_f.readlines()
papi_p2_powers_f.close()

# First part
sub1_real_powers_l = list(it.takewhile(lambda line: line != separator, real_powers_l))
sub1_real_powers_d = np.array([ float(re.search('power=([0-9]+\.[0-9]+)$', line, re.IGNORECASE).group(1)) for line in sub1_real_powers_l if line != '' and line != '\n' ])
real_powers_l = list(it.dropwhile(lambda line: line != separator, real_powers_l))[1:]

sub1_papi_powers_l = list(it.takewhile(lambda line: line != separator, papi_powers_l))
sub1_papi_powers_d = np.array([ float(re.search('power=([0-9]+\.[0-9]+)$', line, re.IGNORECASE).group(1)) for line in sub1_papi_powers_l if line != '' and line != '\n' ])
papi_powers_l = list(it.dropwhile(lambda line: line != separator, papi_powers_l))[1:]

sub1_papi_p1_powers_l = list(it.takewhile(lambda line: line != separator, papi_p1_powers_l))
sub1_papi_p1_powers_d = np.array([ float(re.search('power=([0-9]+\.[0-9]+)$', line, re.IGNORECASE).group(1)) for line in sub1_papi_p1_powers_l if line != '' and line != '\n' ])
papi_p1_powers_l = list(it.dropwhile(lambda line: line != separator, papi_p1_powers_l))[1:]

## Sync. the data size
min_l = min(len(sub1_real_powers_d), len(sub1_papi_powers_d), len(sub1_papi_p1_powers_d))
sub1_real_powers_d = sub1_real_powers_d[0:min_l]
sub1_papi_powers_d = sub1_papi_powers_d[0:min_l]
sub1_papi_p1_powers_d = sub1_papi_p1_powers_d[0:min_l]

relative_errors.extend([ (real - (float(args.idle) + papi + p1)) / real for p1, papi, real in zip(sub1_papi_p1_powers_d, sub1_papi_powers_d, sub1_real_powers_d) if p1 > 0 ])
data.extend([ [0, p1, papi, float(args.idle), real] for p1, papi, real in zip(sub1_papi_p1_powers_d, sub1_papi_powers_d, sub1_real_powers_d) if p1 > 0 ])

# Second part
sub2_real_powers_d = np.array([ float(re.search('power=([0-9]+\.[0-9]+)$', line, re.IGNORECASE).group(1)) for line in real_powers_l if line != '' and line != '\n' ])
sub2_papi_powers_d = np.array([ float(re.search('power=([0-9]+\.[0-9]+)$', line, re.IGNORECASE).group(1)) for line in papi_powers_l if line != '' and line != '\n' ])
sub2_papi_p1_powers_d = np.array([ float(re.search('power=([0-9]+\.[0-9]+)$', line, re.IGNORECASE).group(1)) for line in papi_p1_powers_l if line != '' and line != '\n' ])
sub2_papi_p2_powers_d = np.array([ float(re.search('power=([0-9]+\.[0-9]+)$', line, re.IGNORECASE).group(1)) for line in papi_p2_powers_l if line != '' and line != '\n' ])

## Sync. the data size
min_l = min(len(sub2_real_powers_d), len(sub2_papi_powers_d), len(sub2_papi_p1_powers_d), len(sub2_papi_p2_powers_d))
sub2_real_powers_d = sub2_real_powers_d[0:min_l]
sub2_papi_powers_d = sub2_papi_powers_d[0:min_l]
sub2_papi_p1_powers_d = sub2_papi_p1_powers_d[0:min_l]
sub2_papi_p2_powers_d = sub2_papi_p2_powers_d[0:min_l]

relative_errors.extend([ (real - (float(args.idle) + papi + p1 + p2)) / real for p1, p2, papi, real in zip(sub2_papi_p1_powers_d, sub2_papi_p2_powers_d, sub2_papi_powers_d, sub2_real_powers_d) if p1 > 0 and p2 > 0 ])
data.extend([ [p2, p1, papi, float(args.idle), real] for p1, p2, papi, real in zip(sub2_papi_p1_powers_d, sub2_papi_p2_powers_d, sub2_papi_powers_d, sub2_real_powers_d) if p1 > 0 and p2 > 0 ])

# Writes the results in files
with open('%s/data.csv' % (output_path), 'a') as csvfile:
writer = csv.writer(csvfile, quotechar='', quoting=csv.QUOTE_NONE, delimiter=' ')
writer.writerow(['#', 'x264', 'freqmine', 'idle', 'measured'])

for line in data:
writer.writerow(line)

# Writes statistical informations about errors
with open("%s/errors.txt" % output_path, 'a') as f:
f.write("Avg. BitWatts: %fW\n" % (np.average(np.append(sub1_papi_powers_d, sub2_papi_powers_d))))
f.write("median relative error: %f%%, max relative error: %f%%\n" % ((np.median(relative_errors) * 100), (max(relative_errors) * 100)))

38 changes: 38 additions & 0 deletions bitwatts-eurosys-2015/src/universal/scripts/figure6/figure6.gp
@@ -0,0 +1,38 @@
# Plot the formulae written inside the formula.inc file.
# Be careful of the curve names.
# @author: Maxime Colmant
set term postscript dashed color eps enhanced 22
set termoption dash
set output "figure6.eps"

load 'formulae.inc'

mred = '#CC0000'
mgreen = '#009900'
mlblue = '#6699FF'
mblack = '#000000'

hp1 = '#d7301f'
hp2 = '#fc8d59'
hpapi = '#fdcc8a'
hidle = '#fef0d9'

set style line 1 lc rgb mred lw 4
set style line 2 dt 3 lc rgb mgreen lw 6
set style line 3 lc rgb "orange" lw 8
set style line 4 dt 4 lc rgb mblack lw 6
set style line 5 dt 3 lc rgb mlblue lw 8

set xlabel "# Unhalted cycles"
set ylabel "Estimated Power (W)"

set key bottom right spacing 1.2
set xrange [0:10e8]
set xtics 1e8
set format x "%.0te^%01T"

plot e(x) title '2.660 GHz' ls 1 with lines, \
d(x) title '2.527 GHz' ls 2 with lines, \
c(x) title '2.394 GHz' ls 3 with lines, \
b(x) title '2.261 GHz' ls 4 with lines, \
a(x) title '2.128 GHz' ls 5 with lines
@@ -0,0 +1,8 @@
# Some of the formulae generated by the sampling (see the PowerAPI's repository).
# @author: Maxime Colmant
a(x) = 90.18100211772799 + (3.205474819910001E-8 * x - 1.5778886140222546E-17 * x * x) * 4
b(x) = 89.91678535950723 + (3.413907478079189E-8 * x - 1.6123712450486037E-17 * x * x) * 4
c(x) = 89.68970555443457 + (3.569634036989102E-8 * x - 1.5498817702512596E-17 * x * x) * 4
d(x) = 90.91658043271491 + (3.333663293066566E-8 * x - 1.3041444594382363E-17 * x * x) * 4
e(x) = 89.79126236117554 + (3.9985158977460974E-8 * x - 1.5370128810306314E-17 * x * x) * 4
29 changes: 29 additions & 0 deletions bitwatts-eurosys-2015/src/universal/scripts/figure8-9/figure9.gp
@@ -0,0 +1,29 @@
# Plot the data processed with the process.py script.
# @author: Maxime Colmant

set term postscript color eps enhanced 22
set output "figure9.eps"
set termoption dashed
set style boxplot nooutliers
set style data boxplot
set style fill solid 0.8 border -1
set boxwidth 0.5 absolute
set border 2 lt 1 lc rgb "black"
set ylabel "Relative error"

set xtics ("blackscholes" 1,"bodytrack" 2,"facesim" 3,"fluidanimate" 4,"freqmine" 5,"swaptions" 6,"vips" 7,"x264" 8)
set xtics rotate by -45 nomirror font ",16"
set format y "%.00f%%"

unset key

mred = '#CC0000'

plot 0 lt 3 linecolor rgb "black",\
'errors-data/blackscholes/errors.csv' u (1):($2*100) lt 1 linecolor rgb mred,\
'errors-data/bodytrack/errors.csv' u (2):($2*100) lt 1 linecolor rgb mred,\
'errors-data/facesim/errors.csv' u (3):($2*100) lt 1 linecolor rgb mred,\
'errors-data/fluidanimate/errors.csv' u (4):($2*100) lt 1 linecolor rgb mred,\
'errors-data/freqmine/errors.csv' u (5):($2*100) lt 1 linecolor rgb mred,\
'errors-data/swaptions/errors.csv' u (6):($2*100) lt 1 linecolor rgb mred,\
'errors-data/vips/errors.csv' u (7):($2*100) lt 1 linecolor rgb mred
62 changes: 62 additions & 0 deletions bitwatts-eurosys-2015/src/universal/scripts/figure8-9/process.py
@@ -0,0 +1,62 @@
# Script to process the data used for plotting the Figure 9.
# The data come from the corresponding experiment in bitwatts-eurosys-2015.
# @author: Maxime Colmant
import os
import glob
import re
import numpy as np
import csv
import shutil
import argparse

parser = argparse.ArgumentParser()
parser.add_argument("path", help="experiment data path.")
parser.add_argument('idle', help = 'idle power of the machine.')
args = parser.parse_args()

powers_filename = 'output-external.dat'
powerapi_filename = 'output-powerapi.dat'

output_path = os.path.join('errors-data')

# Creates the output data directory
if os.path.exists(output_path):
shutil.rmtree(output_path)

os.mkdir(output_path)

for benchmark in filter((lambda name: os.path.isdir(os.path.join(args.path, name))), os.listdir(args.path)):
benchmark_path = os.path.join(args.path, benchmark)
benchmark_output_path = os.path.join(output_path, benchmark)

if os.path.exists(benchmark_output_path):
shutil.rmtree(benchmark_output_path)

# Creates the output data directory for the benchmark
os.mkdir(benchmark_output_path)

# Reads files and converts data into numpy arrays
real_powers_f = open(os.path.join(benchmark_path, powers_filename))
real_powers_d = np.array([ float(re.search('power=([0-9]+\.[0-9]+)$', line, re.IGNORECASE).group(1)) for line in real_powers_f ])
real_powers_f.close()

papi_powers_f = open(os.path.join(benchmark_path, powerapi_filename))
papi_powers_d = np.array([ float(re.search('power=([0-9]+\.[0-9]+)$', line, re.IGNORECASE).group(1)) for line in papi_powers_f ])
papi_powers_f.close()

# Computes the errors
absolute_errors = real_powers_d - (papi_powers_d + float(args.idle))
relative_errors = (real_powers_d - (papi_powers_d + float(args.idle))) / real_powers_d
errors = zip(absolute_errors, relative_errors)

# Writes errors for each benchmark for the gnuplot script
with open("%s/errors.csv" % benchmark_output_path, 'a') as csvfile:
writer = csv.writer(csvfile, quotechar='', quoting=csv.QUOTE_NONE, delimiter=' ')
writer.writerow(['#', 'abs_error', 'rel_error'])

for abs_error, rel_error in errors:
writer.writerow([abs_error, rel_error])

# Writes statistical informations about errors
with open("%s/global-errors.txt" % output_path, 'a') as f:
f.write("%s => median relative error: %f%%, max relative error: %f%%\n" % (benchmark, (np.median(relative_errors) * 100), (max(relative_errors) * 100)))

0 comments on commit 0222aab

Please sign in to comment.