From 0b6b1589fbd70e743047f78279d30b66f0d2fc5c Mon Sep 17 00:00:00 2001
From: Martin Bauer <martin.bauer@fau.de>
Date: Sun, 25 Jun 2017 19:29:58 +0200
Subject: [PATCH] Initial work on close kerncraft integration

---
 field.py                                      |  12 +-
 kerncraft/2d-5pt.c_compilable.c               |  24 ++++
 ...2d-5pt.c_compilable.c___twodfivept___1.yml |  46 +++++++
 kerncraft/2d-5pt.yml                          |  34 +++++
 kerncraft/__init__.py                         |   0
 kerncraft/kerncraft_test.py                   | 120 ++++++++++++++++++
 6 files changed, 232 insertions(+), 4 deletions(-)
 create mode 100644 kerncraft/2d-5pt.c_compilable.c
 create mode 100644 kerncraft/2d-5pt.c_compilable.c___twodfivept___1.yml
 create mode 100644 kerncraft/2d-5pt.yml
 create mode 100644 kerncraft/__init__.py
 create mode 100644 kerncraft/kerncraft_test.py

diff --git a/field.py b/field.py
index 6a00e61f5..c3a83e07e 100644
--- a/field.py
+++ b/field.py
@@ -354,6 +354,13 @@ def extractCommonSubexpressions(equations):
     return equations
 
 
+def getLayoutFromStrides(strides, indexDimensionIds=[]):
+    coordinates = list(range(len(strides)))
+    relevantStrides = [stride for i, stride in enumerate(strides) if i not in indexDimensionIds]
+    result = [x for (y, x) in sorted(zip(relevantStrides, coordinates), key=lambda pair: pair[0], reverse=True)]
+    return normalizeLayout(result)
+
+
 def getLayoutOfArray(arr, indexDimensionIds=[]):
     """
     Returns a list indicating the memory layout (linearization order) of the numpy array.
@@ -368,10 +375,7 @@ def getLayoutOfArray(arr, indexDimensionIds=[]):
 
     The indexDimensionIds parameter leaves specifies which coordinates should not be
     """
-    coordinates = list(range(len(arr.shape)))
-    relevantStrides = [stride for i, stride in enumerate(arr.strides) if i not in indexDimensionIds]
-    result = [x for (y, x) in sorted(zip(relevantStrides, coordinates), key=lambda pair: pair[0], reverse=True)]
-    return normalizeLayout(result)
+    return getLayoutFromStrides(arr.strides, indexDimensionIds)
 
 
 def createNumpyArrayWithLayout(shape, layout):
diff --git a/kerncraft/2d-5pt.c_compilable.c b/kerncraft/2d-5pt.c_compilable.c
new file mode 100644
index 000000000..f22f12800
--- /dev/null
+++ b/kerncraft/2d-5pt.c_compilable.c
@@ -0,0 +1,24 @@
+#include <stdlib.h>
+
+#define M 512*512
+#define N 512*512
+
+void twodfivept(double s, int n, double a[M][n], double b[M][n]) {
+    for (int j = 1; j < (M - 1); ++j)
+        for (int i = 1; i < (n - 1); ++i)
+            b[j][i] = (a[j][i-1] + a[j][i+1] + a[j-1][i] + a[j+1][i]) * (s ? a[j][i-1] > 0 : 1.0);
+}
+
+int main(int argc, char **argv)
+{
+    double a[M][N];
+    double b[M][N];
+    
+    for(int i=0; i<M; ++i)
+        for(int j=0; j<N; ++j)
+            a[i][j] = b[i][j] = 42.0;
+  
+    twodfivept(0.23, N, a, b);
+    
+    return 0;
+}
diff --git a/kerncraft/2d-5pt.c_compilable.c___twodfivept___1.yml b/kerncraft/2d-5pt.c_compilable.c___twodfivept___1.yml
new file mode 100644
index 000000000..6f805a938
--- /dev/null
+++ b/kerncraft/2d-5pt.c_compilable.c___twodfivept___1.yml
@@ -0,0 +1,46 @@
+
+general:
+    analysis:
+        tool: LLVM (3.9.0svn)
+        build: Apr 19 2016 (14:38:27)
+    kernel:
+        file: 2d-5pt.c_compilable.c
+        function: twodfivept
+        scop number: 0
+
+arrays:
+    a:
+        type: double
+        dimension: [*, n]
+    b:
+        type: double
+        dimension: [*, n]
+
+loops:
+    -
+        index: i
+        start: 0
+        stop: 262142
+        step: 1
+    -
+        index: j
+        start: 0
+        stop: (-2+n)
+        step: 1
+
+data sources:
+    a:
+        - [1+i, j]
+        - [1+i, 2+j]
+        - [i, 1+j]
+        - [2+i, 1+j]
+
+data destinations:
+    b:
+        - [1+i, 1+j]
+
+flops:
+    "+":  3
+    "*":  1
+    "f+":  4
+    "f*":  2
diff --git a/kerncraft/2d-5pt.yml b/kerncraft/2d-5pt.yml
new file mode 100644
index 000000000..4404ae67e
--- /dev/null
+++ b/kerncraft/2d-5pt.yml
@@ -0,0 +1,34 @@
+arrays:
+    a:
+        type: double
+        dimension: [30, 50]
+    b:
+        type: double
+        dimension: [30, 50]
+
+loops:
+    -
+        index: i
+        start: 0
+        stop: 262142
+        step: 1
+    -
+        index: j
+        start: 0
+        stop: (-2+20)
+        step: 1
+
+data sources:
+    a:
+        - [1+i, j]
+        - [1+i, 2+j]
+        - [i, 1+j]
+        - [2+i, 1+j]
+
+data destinations:
+    b:
+        - [1+i, 1+j]
+
+flops:
+    "+":  3
+    "*":  1
diff --git a/kerncraft/__init__.py b/kerncraft/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/kerncraft/kerncraft_test.py b/kerncraft/kerncraft_test.py
new file mode 100644
index 000000000..ac810c0d3
--- /dev/null
+++ b/kerncraft/kerncraft_test.py
@@ -0,0 +1,120 @@
+import kerncraft
+from kerncraft.kernel import KernelCode
+from kernel import KernelDescription
+from pystencils.astnodes import LoopOverCoordinate
+from pystencils.cpu import createKernel
+from pystencils.field import getLayoutFromStrides
+from pystencils.sympyextensions import countNumberOfOperations
+from pystencils.transformations import typeAllEquations
+from pystencils import Field
+from collections import defaultdict
+
+
+class PyStencilsKerncraftKernel(kerncraft.kernel.Kernel):
+
+    def __init__(self, listOfEquations, typeForSymbol=None):
+        super(PyStencilsKerncraftKernel, self).__init__()
+
+        pystencilsAst = createKernel(listOfEquations, typeForSymbol=typeForSymbol)
+        self.ast = pystencilsAst
+        fieldsRead, fieldsWritten, assignments = typeAllEquations(listOfEquations, typeForSymbol)
+        allFields = fieldsRead.union(fieldsWritten)
+
+        # Loops
+        innerLoops = [l for l in pystencilsAst.atoms(LoopOverCoordinate) if l.isInnermostLoop]
+        if len(innerLoops) == 0:
+            raise ValueError("No loop found in pystencils AST")
+        elif len(innerLoops) > 1:
+            raise ValueError("pystencils AST contains multiple inner loops - only one can be analyzed")
+        else:
+            innerLoop = innerLoops[0]
+
+        self._loop_stack = []
+        curNode = innerLoop
+        while curNode is not None:
+            if isinstance(curNode, LoopOverCoordinate):
+                loopInfo = (curNode.loopCounterSymbol.name, curNode.start, curNode.stop, curNode.step)
+                self._loop_stack.append(loopInfo)
+            curNode = curNode.parent
+        self._loop_stack = list(reversed(self._loop_stack))
+
+        # Data sources & destinations
+        self._sources = defaultdict(list)
+        self._destinations = defaultdict(list)
+        for eq in listOfEquations:
+            for accessesDict, expr in [(self._destinations, eq.lhs), (self._sources, eq.rhs)]:
+                for fa in expr.atoms(Field.Access):
+                    coord = [sp.Symbol(LoopOverCoordinate.getLoopCounterName(i)) + off for i, off in enumerate(fa.offsets)]
+                    coord += list(fa.index)
+                    layout = getLayoutFromStrides(fa.field.strides)
+                    permutedCoord = [coord[i] for i in layout]
+                    accessesDict[fa.field.name].append(permutedCoord)
+
+        # Variables (arrays)
+        for field in allFields:
+            layout = getLayoutFromStrides(field.strides)
+            permutedShape = list(field.shape[i] for i in layout)
+            self.set_variable(field.name, str(field.dtype), permutedShape)
+        for param in pystencilsAst.parameters:
+            if not param.isFieldArgument:
+                self.set_variable(param.name, str(param.dtype), None)
+                self._sources[param.name] = [None]
+
+        # Datatype
+        self.datatype = list(self.variables.values())[0][0]
+
+        # Flops
+        operationCount = countNumberOfOperations(listOfEquations)
+        self._flops = {
+            '+': operationCount['adds'],
+            '*': operationCount['muls'],
+            '/': operationCount['divs'],
+        }
+
+        self.check()
+
+from kerncraft.iaca_marker import find_asm_blocks, userselect_block, select_best_block
+from kerncraft.models import ECM, ECMData
+from kerncraft.machinemodel import MachineModel
+from ruamel import yaml
+
+if __name__ == "__main__":
+    from pystencils import Field
+    import sympy as sp
+    import numpy as np
+    from pystencils.cpu import generateC
+
+    arr = np.zeros([80, 40], order='c')
+    #arr = np.zeros([40, 80, 3], order='f')
+    a = Field.createFromNumpyArray('a', arr, indexDimensions=0)
+    b = Field.createFromNumpyArray('b', arr, indexDimensions=0)
+
+    s = sp.Symbol("s")
+    rhs = a[0, -1](0) + a[0, 1] + a[-1, 0] + a[1, 0]
+    updateRule = sp.Eq(b[0, 0], s*rhs)
+    k = PyStencilsKerncraftKernel([updateRule])
+    print(generateC(k.ast))
+    kernelFile = "2d-5pt.c"
+    #k = KernelCode(open("/home/martin/dev/kerncraft/examples/kernels/" + kernelFile).read())]
+    descr = yaml.load(open("/home/martin/dev/pystencils/pystencils/kerncraft/2d-5pt.yml").read())
+    k = KernelDescription(descr)
+    k.print_kernel_info()
+    k.print_variables_info()
+    offsets = list(k.compile_global_offsets(1000))
+    print(offsets)
+
+    machineFilePath = "/home/martin/dev/kerncraft/examples/machine-files/emmy.yaml"
+    machine = MachineModel(path_to_yaml=machineFilePath)
+    #exit(0)
+    from kerncraft.kerncraft import create_parser
+    parser = create_parser()
+    parserArgs = parser.parse_args(["-m", machineFilePath, "-p", "ECMData", machineFilePath])
+
+    model = ECMData(k, machine, parserArgs)
+    model.analyze()
+    model.report()
+    #blocks = find_asm_blocks(open("/home/martin/dev/kerncraft/2d-5pt.c_compilable.s").readlines())
+    #userselect_block(blocks)
+    ##select_
+    #bestBlock = select_best_block(blocks)
+    #print(bestBlock)
-- 
GitLab