Skip to content
Snippets Groups Projects
benchmark.py 7.02 KiB
import math
import os
import time

import numpy as np
import sympy as sp
from git import Repo
from influxdb import InfluxDBClient
from kerncraft.machinemodel import MachineModel
from kerncraft.models import ECM, Benchmark, Roofline, RooflineIACA
from kerncraft.prefixedunit import PrefixedUnit

from pystencils import Assignment, Field, create_kernel
from pystencils.kerncraft_coupling import KerncraftParameters, PyStencilsKerncraftKernel


def output_benchmark(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 output_ecm(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 output_roofline(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 output_roofline_iaca(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 report_analysis(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 = output_benchmark(analysis)
        elif model is ECM:
            output = output_ecm(analysis)
        elif model is Roofline:
            output = output_roofline(analysis)
        elif model is RooflineIACA:
            output = output_roofline_iaca(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]

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

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


if __name__ == '__main__':
    main()