From 0690f5adc62e8bc867d18bc5c1af621c9296d83b Mon Sep 17 00:00:00 2001
From: Martin Bauer <martin.bauer@fau.de>
Date: Wed, 29 May 2019 15:39:28 +0200
Subject: [PATCH] Added function to run compiled C benchmark code

---
 pystencils/kerncraft_coupling/__init__.py     |  3 +-
 .../kerncraft_coupling/generate_benchmark.py  | 79 +++++++++++++++++--
 2 files changed, 76 insertions(+), 6 deletions(-)

diff --git a/pystencils/kerncraft_coupling/__init__.py b/pystencils/kerncraft_coupling/__init__.py
index 33a2ed21f..7822b3b54 100644
--- a/pystencils/kerncraft_coupling/__init__.py
+++ b/pystencils/kerncraft_coupling/__init__.py
@@ -1,3 +1,4 @@
 from .kerncraft_interface import PyStencilsKerncraftKernel, KerncraftParameters
+from .generate_benchmark import generate_benchmark, run_c_benchmark
 
-__all__ = ['PyStencilsKerncraftKernel', 'KerncraftParameters']
+__all__ = ['PyStencilsKerncraftKernel', 'KerncraftParameters', 'generate_benchmark', 'run_c_benchmark']
diff --git a/pystencils/kerncraft_coupling/generate_benchmark.py b/pystencils/kerncraft_coupling/generate_benchmark.py
index 339e1bb30..f63be049a 100644
--- a/pystencils/kerncraft_coupling/generate_benchmark.py
+++ b/pystencils/kerncraft_coupling/generate_benchmark.py
@@ -1,4 +1,8 @@
 from jinja2 import Template
+import os
+import subprocess
+from pystencils.include import get_pystencils_include_path
+from pystencils.cpu.cpujit import get_compiler_config, run_compile_step
 from pystencils.backends.cbackend import generate_c, get_headers
 from pystencils.sympyextensions import prod
 from pystencils.data_types import get_base_type
@@ -10,6 +14,8 @@ benchmark_template = Template("""
 #include <stdint.h>
 #include <stdbool.h>
 #include <math.h>
+#include <stdio.h>
+
 {{ includes }}
 
 {%- if likwid %}
@@ -18,7 +24,8 @@ benchmark_template = Template("""
 
 #define RESTRICT __restrict__
 #define FUNC_PREFIX
-void dummy(double *);
+void dummy(void *);
+void timing(double* wcTime, double* cpuTime);
 extern int var_false;
 
 
@@ -72,19 +79,30 @@ int main(int argc, char **argv)
       likwid_markerStartRegion("loop");
       {%- endif %}
     }
-
+    
+    {%- if timing %}
+    double wcStartTime, cpuStartTime, wcEndTime, cpuEndTime;
+    timing(&wcStartTime, &cpuStartTime);
+    {%- endif %}
+    
     for (; repeat > 0; --repeat)
     {
       {{kernelName}}({{call_argument_list}});
 
       // Dummy calls
       {%- for field_name, dataType, size in fields %}
-      if(var_false) dummy({{field_name}});
+      if(var_false) dummy((void*){{field_name}});
       {%- endfor %}
       {%- for constantName, dataType in constants %}
-      if(var_false) dummy(&{{constantName}});
+      if(var_false) dummy((void*)&{{constantName}});
       {%- endfor %}
     }
+    {%- if timing %}
+    timing(&wcEndTime, &cpuEndTime);
+    if( warmup == 0)
+        printf("%e\\n", (wcEndTime - wcStartTime) / atoi(argv[1]) );
+    {%- endif %}
+
   }
 
   {%- if likwid %}
@@ -101,7 +119,18 @@ int main(int argc, char **argv)
 """)
 
 
-def generate_benchmark(ast, likwid=False, openmp=False):
+def generate_benchmark(ast, likwid=False, openmp=False, timing=False):
+    """Return C code of a benchmark program for the given kernel.
+
+    Args:
+        ast: the pystencils AST object as returned by create_kernel
+        likwid: if True likwid markers are added to the code
+        openmp: relevant only if likwid=True, to generated correct likwid initialization code
+        timing: add timing output to the code, prints time per iteration to stdout
+
+    Returns:
+        C code as string
+    """
     accessed_fields = {f.name: f for f in ast.fields_accessed}
     constants = []
     fields = []
@@ -135,5 +164,45 @@ def generate_benchmark(ast, likwid=False, openmp=False):
         'constants': constants,
         'call_argument_list': ",".join(call_parameters),
         'includes': includes,
+        'timing': timing,
     }
     return benchmark_template.render(**args)
+
+
+def run_c_benchmark(ast, inner_iterations, outer_iterations=3):
+    """Runs the given kernel with outer loop in C
+
+    Args:
+        ast:
+        inner_iterations: timings are recorded around this many iterations
+        outer_iterations: number of timings recorded
+
+    Returns:
+        list of times per iterations for each outer iteration
+    """
+    import kerncraft
+
+    benchmark_code = generate_benchmark(ast, timing=True)
+    with open('bench.c', 'w') as f:
+        f.write(benchmark_code)
+
+    kerncraft_path = os.path.dirname(kerncraft.__file__)
+
+    extra_flags = ['-I' + get_pystencils_include_path(),
+                   '-I' + os.path.join(kerncraft_path, 'headers')]
+
+    compiler_config = get_compiler_config()
+    compile_cmd = [compiler_config['command']] + compiler_config['flags'].split()
+    compile_cmd += [*extra_flags,
+                    os.path.join(kerncraft_path, 'headers', 'timing.c'),
+                    os.path.join(kerncraft_path, 'headers', 'dummy.c'),
+                    'bench.c',
+                    '-o', 'bench',
+                    ]
+    run_compile_step(compile_cmd)
+
+    results = []
+    for _ in range(outer_iterations):
+        benchmark_time = float(subprocess.check_output(['./bench', str(inner_iterations)]))
+        results.append(benchmark_time)
+    return results
-- 
GitLab