Skip to content
Snippets Groups Projects
benchmark.py 7.09 KiB
Newer Older
import os
import math
import time
import numpy as np
import sympy as sp
from influxdb import InfluxDBClient
from git import Repo
from kerncraft.models import ECM, Benchmark, Roofline, RooflineIACA
from kerncraft.machinemodel import MachineModel
from kerncraft.prefixedunit import PrefixedUnit
from pystencils.kerncraft_coupling import KerncraftParameters, PyStencilsKerncraftKernel
from pystencils import Field, Assignment, create_kernel


def outputBenchmark(analysis):
    output = {}
    keys = ['Runtime (per repetition) [s]', 'Iterations per repetition',
            'Runtime (per cacheline update) [cy/CL]', 'MEM volume (per repetition) [B]',
            'Performance [MFLOP/s]', 'Performance [MLUP/s]', 'Performance [MIt/s]', 'MEM BW [MByte/s]']
    copies = {key: analysis[key] for key in keys}
    output.update(copies)

    for cache, metrics in analysis['data transfers'].items():
        for metric_name, metric_value in metrics.items():
            fixed = metric_value.with_prefix('')
            output[cache + ' ' + metric_name + ' ' + fixed.prefix + fixed.unit] = fixed.value

    for level, value in analysis['ECM'].items():
        output['Phenomenological ECM ' + level + ' cy/CL'] = value
    return output


def outputECM(analysis):
    output = {}
    keys = ['T_nOL', 'T_OL', 'cl throughput', 'uops']
    copies = {key: analysis[key] for key in keys}
    output.update(copies)

    if 'memory bandwidth kernel' in analysis:
        output['memory bandwidth kernel' + analysis['memory bandwidth kernel'] + analysis['memory bandwidth'].prefix +
               analysis['memory bandwidth'].unit] = analysis['memory bandwidth'].value

    output['scaling cores'] = int(analysis['scaling cores']) if not math.isinf(analysis['scaling cores']) else -1

    for key, value in analysis['cycles']:
        output[key] = value
    return output


def outputRoofline(analysis):
    output = {}
    keys = ['min performance']#'bottleneck level'
    copies = {key: analysis[key] for key in keys}
    output.update(copies)
    # TODO save bottleneck information (compute it here)

    #fixed = analysis['max_flops'].with_prefix('G')
    #output['max GFlop/s'] = fixed.value

    #if analysis['min performance'] > max_flops:
    #    # CPU bound
    #    print('CPU bound with {} cores(s)'.format(self._args.cores), file=output_file)
    #    print('{!s} due to CPU max. FLOP/s'.format(max_flops), file=output_file)
    #else:
    # Memory bound
    bottleneck = analysis['mem bottlenecks'][analysis['bottleneck level']]
    output['bottleneck GFlop/s'] = bottleneck['performance'].with_prefix('G').value
    output['bottleneck level'] = bottleneck['level']
    output['bottleneck bw kernel'] = bottleneck['bw kernel']
    output['bottleneck arithmetic intensity'] = bottleneck['arithmetic intensity']

    for i, level in enumerate(analysis['mem bottlenecks']):
        if level is None:
            continue
        for key, value in level.items():
            if isinstance(value, PrefixedUnit):
                fixed = value.with_prefix('G')
                output['level ' + str(i) + ' ' + key + ' [' + fixed.prefix + fixed.unit + ']'] = 'inf' if isinstance(
                    fixed.value, float) and math.isinf(fixed.value) else fixed.value
            else:
                output['level ' + str(i) + ' ' + key] = 'inf' if isinstance(value, float) and math.isinf(
                    value) else value
    return output


def outputRooflineIACA(analysis):
    output = {}
    keys = ['min performance'] #'bottleneck level'
    copies = {key: analysis[key] for key in keys}
    #output.update(copies)
    # TODO save bottleneck information (compute it here)

    #fixed = analysis['max_flops'].with_prefix('G')
    #output['max GFlop/s'] = fixed.value

    #if analysis['min performance'] > max_flops:
    #    # CPU bound
    #    print('CPU bound with {} cores(s)'.format(self._args.cores), file=output_file)
    #    print('{!s} due to CPU max. FLOP/s'.format(max_flops), file=output_file)
    #else:
    # Memory bound
    bottleneck = analysis['mem bottlenecks'][analysis['bottleneck level']]
    output['bottleneck GFlop/s'] = bottleneck['performance'].with_prefix('G').value
    output['bottleneck level'] = bottleneck['level']
    output['bottleneck bw kernel'] = bottleneck['bw kernel']
    output['bottleneck arithmetic intensity'] = bottleneck['arithmetic intensity']

    for i, level in enumerate(analysis['mem bottlenecks']):
        if level is None:
            continue
        for key, value in level.items():
            if isinstance(value, PrefixedUnit):
                fixed = value.with_prefix('G')
                output['level ' + str(i) + ' ' + key + ' [' + fixed.prefix + fixed.unit + ']'] = 'inf' if isinstance(
                    fixed.value, float) and math.isinf(fixed.value) else fixed.value
            else:
                output['level ' + str(i) + ' ' + key] = 'inf' if isinstance(value, float) and math.isinf(
                    value) else value
    return output


def reportAnalysis(ast, models, machine, tags, fields=None):
    kernel = PyStencilsKerncraftKernel(ast, machine)
    client = InfluxDBClient('i10grafana.informatik.uni-erlangen.de', 8086, 'pystencils',
                            'roggan', 'pystencils')
    repo = Repo(search_parent_directories=True)
    commit = repo.head.commit
    point_time = int(time.time())

    for model in models:
        benchmark = model(kernel, machine, KerncraftParameters())
        benchmark.analyze()
        analysis = benchmark.results
        if model is Benchmark:
            output = outputBenchmark(analysis)
        elif model is ECM:
            output = outputECM(analysis)
        elif model is Roofline:
            output = outputRoofline(analysis)
        elif model is RooflineIACA:
            output = outputRooflineIACA(analysis)
        else:
            raise ValueError('No valid model for analysis given!')

        if fields is not None:
            output.update(fields)

        output['commit'] = commit.hexsha

        json_body = [
            {
                'measurement': model.__name__,
                'tags': tags,
                'time': point_time,
                'fields': output
            }
        ]
        client.write_points(json_body, time_precision='s')


def main():
    size = [20, 200, 200]
    arr = np.zeros(size)
    a = Field.create_from_numpy_array('a', arr, index_dimensions=0)
    b = Field.create_from_numpy_array('b', arr, index_dimensions=0)
    s = sp.Symbol("s")
    rhs = a[0, -1, 0] + a[0, 1, 0] + \
          a[-1, 0, 0] + a[1, 0, 0] + \
          a[0, 0, -1] + a[0, 0, 1]

    updateRule = Assignment(b[0, 0, 0], s * rhs)
    ast = create_kernel([updateRule])
    INPUT_FOLDER = "./"
    machineFilePath = os.path.join(INPUT_FOLDER, "SkylakeSP_Gold-5122_allinclusive.yaml")
    machine = MachineModel(path_to_yaml=machineFilePath)
    tags = {
                    'host': os.uname()[1],
                    'project': 'pystencils',
                    'kernel': 'jacobi_3D ' + str(size)
                }

    reportAnalysis(ast, [ECM, Roofline, RooflineIACA, Benchmark], machine, tags)


if __name__ == '__main__':
    main()
    while False:
        main()
        time.sleep(3600)