Skip to content

Commit 323dd89

Browse files
Merge pull request #262 from KernelTuner/directives
OpenACC bug fixing
2 parents 8ebf8b8 + d2fabc9 commit 323dd89

File tree

2 files changed

+128
-56
lines changed

2 files changed

+128
-56
lines changed

kernel_tuner/utils/directives.py

Lines changed: 94 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,48 @@ def __init__(self, directive: Directive, lang: Language):
4848
self.language = lang
4949

5050

51+
class ArraySize(object):
52+
"""Size of an array"""
53+
54+
def __init__(self):
55+
self.size = list()
56+
57+
def __iter__(self):
58+
for i in self.size:
59+
yield i
60+
61+
def __len__(self):
62+
return len(self.size)
63+
64+
def clear(self):
65+
self.size.clear()
66+
67+
def get(self) -> int:
68+
length = len(self.size)
69+
if length == 0:
70+
return 0
71+
elif length == 1:
72+
return self.size[0]
73+
else:
74+
product = 1
75+
for i in self.size:
76+
product *= i
77+
return product
78+
79+
def add(self, dim: int) -> None:
80+
# Only allow adding valid dimensions
81+
if dim >= 1:
82+
self.size.append(dim)
83+
84+
85+
def fortran_md_size(size: ArraySize) -> list:
86+
"""Format a multidimensional size into the correct Fortran string"""
87+
md_size = list()
88+
for dim in size:
89+
md_size.append(f":{dim}")
90+
return md_size
91+
92+
5193
def is_openacc(directive: Directive) -> bool:
5294
"""Check if a directive is OpenACC"""
5395
return isinstance(directive, OpenACC)
@@ -120,7 +162,7 @@ def openacc_directive_contains_data_clause(line: str) -> bool:
120162
return openacc_directive_contains_clause(line, data_clauses)
121163

122164

123-
def create_data_directive_openacc(name: str, size: int, lang: Language) -> str:
165+
def create_data_directive_openacc(name: str, size: ArraySize, lang: Language) -> str:
124166
"""Create a data directive for a given language"""
125167
if is_cxx(lang):
126168
return create_data_directive_openacc_cxx(name, size)
@@ -129,17 +171,23 @@ def create_data_directive_openacc(name: str, size: int, lang: Language) -> str:
129171
return ""
130172

131173

132-
def create_data_directive_openacc_cxx(name: str, size: int) -> str:
174+
def create_data_directive_openacc_cxx(name: str, size: ArraySize) -> str:
133175
"""Create C++ OpenACC code to allocate and copy data"""
134-
return f"#pragma acc enter data create({name}[:{size}])\n#pragma acc update device({name}[:{size}])\n"
176+
return f"#pragma acc enter data create({name}[:{size.get()}])\n#pragma acc update device({name}[:{size.get()}])\n"
135177

136178

137-
def create_data_directive_openacc_fortran(name: str, size: int) -> str:
179+
def create_data_directive_openacc_fortran(name: str, size: ArraySize) -> str:
138180
"""Create Fortran OpenACC code to allocate and copy data"""
139-
return f"!$acc enter data create({name}(:{size}))\n!$acc update device({name}(:{size}))\n"
181+
if len(size) == 1:
182+
return f"!$acc enter data create({name}(:{size.get()}))\n!$acc update device({name}(:{size.get()}))\n"
183+
else:
184+
md_size = fortran_md_size(size)
185+
return (
186+
f"!$acc enter data create({name}({','.join(md_size)}))\n!$acc update device({name}({','.join(md_size)}))\n"
187+
)
140188

141189

142-
def exit_data_directive_openacc(name: str, size: int, lang: Language) -> str:
190+
def exit_data_directive_openacc(name: str, size: ArraySize, lang: Language) -> str:
143191
"""Create code to copy data back for a given language"""
144192
if is_cxx(lang):
145193
return exit_data_directive_openacc_cxx(name, size)
@@ -148,14 +196,18 @@ def exit_data_directive_openacc(name: str, size: int, lang: Language) -> str:
148196
return ""
149197

150198

151-
def exit_data_directive_openacc_cxx(name: str, size: int) -> str:
199+
def exit_data_directive_openacc_cxx(name: str, size: ArraySize) -> str:
152200
"""Create C++ OpenACC code to copy back data"""
153-
return f"#pragma acc exit data copyout({name}[:{size}])\n"
201+
return f"#pragma acc exit data copyout({name}[:{size.get()}])\n"
154202

155203

156-
def exit_data_directive_openacc_fortran(name: str, size: int) -> str:
204+
def exit_data_directive_openacc_fortran(name: str, size: ArraySize) -> str:
157205
"""Create Fortran OpenACC code to copy back data"""
158-
return f"!$acc exit data copyout({name}(:{size}))\n"
206+
if len(size) == 1:
207+
return f"!$acc exit data copyout({name}(:{size.get()}))\n"
208+
else:
209+
md_size = fortran_md_size(size)
210+
return f"!$acc exit data copyout({name}({','.join(md_size)}))\n"
159211

160212

161213
def correct_kernel(kernel_name: str, line: str) -> bool:
@@ -165,7 +217,7 @@ def correct_kernel(kernel_name: str, line: str) -> bool:
165217

166218
def find_size_in_preprocessor(dimension: str, preprocessor: list) -> int:
167219
"""Find the dimension of a directive defined value in the preprocessor"""
168-
ret_size = None
220+
ret_size = 0
169221
for line in preprocessor:
170222
if f"#define {dimension}" in line:
171223
try:
@@ -209,45 +261,43 @@ def extract_code(start: str, stop: str, code: str, langs: Code, kernel_name: str
209261
return sections
210262

211263

212-
def parse_size(size: Any, preprocessor: list = None, dimensions: dict = None) -> int:
264+
def parse_size(size: Any, preprocessor: list = None, dimensions: dict = None) -> ArraySize:
213265
"""Converts an arbitrary object into an integer representing memory size"""
214-
ret_size = None
266+
ret_size = ArraySize()
215267
if type(size) is not int:
216268
try:
217269
# Try to convert the size to an integer
218-
ret_size = int(size)
270+
ret_size.add(int(size))
219271
except ValueError:
220272
# If size cannot be natively converted to an int, we try to derive it from the preprocessor
221-
if preprocessor is not None:
222-
try:
273+
try:
274+
if preprocessor is not None:
223275
if "," in size:
224-
ret_size = 1
225276
for dimension in size.split(","):
226-
ret_size *= find_size_in_preprocessor(dimension, preprocessor)
277+
ret_size.add(find_size_in_preprocessor(dimension, preprocessor))
227278
else:
228-
ret_size = find_size_in_preprocessor(size, preprocessor)
229-
except TypeError:
230-
# preprocessor is available but does not contain the dimensions
231-
pass
279+
ret_size.add(find_size_in_preprocessor(size, preprocessor))
280+
except TypeError:
281+
# At least one of the dimension cannot be derived from the preprocessor
282+
pass
232283
# If size cannot be natively converted, nor retrieved from the preprocessor, we check user provided values
233284
if dimensions is not None:
234285
if size in dimensions.keys():
235286
try:
236-
ret_size = int(dimensions[size])
287+
ret_size.add(int(dimensions[size]))
237288
except ValueError:
238289
# User error, no mitigation
239290
return ret_size
240291
elif "," in size:
241-
ret_size = 1
242292
for dimension in size.split(","):
243293
try:
244-
ret_size *= int(dimensions[dimension])
294+
ret_size.add(int(dimensions[dimension]))
245295
except ValueError:
246296
# User error, no mitigation
247-
return None
297+
return ret_size
248298
else:
249299
# size is already an int. no need for conversion
250-
ret_size = size
300+
ret_size.add(size)
251301

252302
return ret_size
253303

@@ -297,8 +347,13 @@ def wrap_data(code: str, langs: Code, data: dict, preprocessor: list = None, use
297347
intro += create_data_directive_openacc_cxx(name, size)
298348
outro += exit_data_directive_openacc_cxx(name, size)
299349
elif is_openacc(langs.directive) and is_fortran(langs.language):
300-
intro += create_data_directive_openacc_fortran(name, size)
301-
outro += exit_data_directive_openacc_fortran(name, size)
350+
if "," in data[name][1]:
351+
# Multi dimensional
352+
pass
353+
else:
354+
# One dimensional
355+
intro += create_data_directive_openacc_fortran(name, size)
356+
outro += exit_data_directive_openacc_fortran(name, size)
302357
return intro + code + outro
303358

304359

@@ -537,9 +592,9 @@ def allocate_signature_memory(data: dict, preprocessor: list = None, user_dimens
537592
p_type = data[parameter][0]
538593
size = parse_size(data[parameter][1], preprocessor, user_dimensions)
539594
if "*" in p_type:
540-
args.append(allocate_array(p_type, size))
595+
args.append(allocate_array(p_type, size.get()))
541596
else:
542-
args.append(allocate_scalar(p_type, size))
597+
args.append(allocate_scalar(p_type, size.get()))
543598

544599
return args
545600

@@ -579,11 +634,15 @@ def add_present_openacc(
579634
return new_body
580635

581636

582-
def add_present_openacc_cxx(name: str, size: int) -> str:
637+
def add_present_openacc_cxx(name: str, size: ArraySize) -> str:
583638
"""Create present clause for C++ OpenACC directive"""
584-
return f" present({name}[:{size}]) "
639+
return f" present({name}[:{size.get()}]) "
585640

586641

587-
def add_present_openacc_fortran(name: str, size: int) -> str:
642+
def add_present_openacc_fortran(name: str, size: ArraySize) -> str:
588643
"""Create present clause for Fortran OpenACC directive"""
589-
return f" present({name}(:{size})) "
644+
if len(size) == 1:
645+
return f" present({name}(:{size.get()})) "
646+
else:
647+
md_size = fortran_md_size(size)
648+
return f" present({name}({','.join(md_size)})) "

test/utils/test_directives.py

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
from pytest import raises
2-
31
from kernel_tuner.utils.directives import *
42

53

@@ -44,21 +42,31 @@ def test_openacc_directive_contains_data_clause():
4442

4543

4644
def test_create_data_directive():
45+
size = ArraySize()
46+
size.add(1024)
4747
assert (
48-
create_data_directive_openacc("array", 1024, Cxx())
48+
create_data_directive_openacc("array", size, Cxx())
4949
== "#pragma acc enter data create(array[:1024])\n#pragma acc update device(array[:1024])\n"
5050
)
51+
size.clear()
52+
size.add(35)
53+
size.add(16)
5154
assert (
52-
create_data_directive_openacc("matrix", 35, Fortran())
53-
== "!$acc enter data create(matrix(:35))\n!$acc update device(matrix(:35))\n"
55+
create_data_directive_openacc("matrix", size, Fortran())
56+
== "!$acc enter data create(matrix(:35,:16))\n!$acc update device(matrix(:35,:16))\n"
5457
)
55-
assert create_data_directive_openacc("array", 1024, None) == ""
58+
assert create_data_directive_openacc("array", size, None) == ""
5659

5760

5861
def test_exit_data_directive():
59-
assert exit_data_directive_openacc("array", 1024, Cxx()) == "#pragma acc exit data copyout(array[:1024])\n"
60-
assert exit_data_directive_openacc("matrix", 35, Fortran()) == "!$acc exit data copyout(matrix(:35))\n"
61-
assert exit_data_directive_openacc("matrix", 1024, None) == ""
62+
size = ArraySize()
63+
size.add(1024)
64+
assert exit_data_directive_openacc("array", size, Cxx()) == "#pragma acc exit data copyout(array[:1024])\n"
65+
size.clear()
66+
size.add(35)
67+
size.add(16)
68+
assert exit_data_directive_openacc("matrix", size, Fortran()) == "!$acc exit data copyout(matrix(:35,:16))\n"
69+
assert exit_data_directive_openacc("matrix", size, None) == ""
6270

6371

6472
def test_correct_kernel():
@@ -69,16 +77,16 @@ def test_correct_kernel():
6977

7078

7179
def test_parse_size():
72-
assert parse_size(128) == 128
73-
assert parse_size("16") == 16
74-
assert parse_size("test") is None
75-
assert parse_size("n", ["#define n 1024\n"]) == 1024
76-
assert parse_size("n,m", ["#define n 16\n", "#define m 32\n"]) == 512
77-
assert parse_size("n", ["#define size 512\n"], {"n": 32}) == 32
78-
assert parse_size("m", ["#define size 512\n"], {"n": 32}) is None
79-
assert parse_size("rows,cols", dimensions={"rows": 16, "cols": 8}) == 128
80-
assert parse_size("n_rows,n_cols", ["#define n_cols 16\n", "#define n_rows 32\n"]) == 512
81-
assert parse_size("rows,cols", [], dimensions={"rows": 16, "cols": 8}) == 128
80+
assert parse_size(128).get() == 128
81+
assert parse_size("16").get() == 16
82+
assert parse_size("test").get() == 0
83+
assert parse_size("n", ["#define n 1024\n"]).get() == 1024
84+
assert parse_size("n,m", ["#define n 16\n", "#define m 32\n"]).get() == 512
85+
assert parse_size("n", ["#define size 512\n"], {"n": 32}).get() == 32
86+
assert parse_size("m", ["#define size 512\n"], {"n": 32}).get() == 0
87+
assert parse_size("rows,cols", dimensions={"rows": 16, "cols": 8}).get() == 128
88+
assert parse_size("n_rows,n_cols", ["#define n_cols 16\n", "#define n_rows 32\n"]).get() == 512
89+
assert parse_size("rows,cols", [], dimensions={"rows": 16, "cols": 8}).get() == 128
8290

8391

8492
def test_wrap_timing():
@@ -272,8 +280,8 @@ def test_extract_directive_data():
272280
def test_allocate_signature_memory():
273281
code = "#pragma tuner start vector_add a(float*:VECTOR_SIZE) b(float*:VECTOR_SIZE) c(float*:VECTOR_SIZE) size(int:VECTOR_SIZE)\n#pragma acc"
274282
data = extract_directive_data(code, Code(OpenACC(), Cxx()))
275-
with raises(TypeError):
276-
_ = allocate_signature_memory(data["vector_add"])
283+
args = allocate_signature_memory(data["vector_add"])
284+
assert args[3] == 0
277285
preprocessor = ["#define VECTOR_SIZE 1024\n"]
278286
args = allocate_signature_memory(data["vector_add"], preprocessor)
279287
assert type(args[0]) is np.ndarray
@@ -327,3 +335,8 @@ def test_add_present_openacc():
327335
code_cxx = "#pragma acc parallel num_gangs(32)\n\t#pragma acc loop\n\t//for loop\n"
328336
expected_cxx = "#pragma acc parallel num_gangs(32) present(array[:42])\n\t#pragma acc loop\n\t//for loop\n"
329337
assert add_present_openacc(code_cxx, acc_cxx, data, preprocessor, None) == expected_cxx
338+
code_f90 = "!$acc parallel async num_workers(16)\n"
339+
data = {"matrix": ["float*", "rows,cols"]}
340+
preprocessor = ["#define cols 18\n", "#define rows 14\n"]
341+
expected_f90 = "!$acc parallel async num_workers(16) present(matrix(:14,:18))\n"
342+
assert add_present_openacc(code_f90, acc_f90, data, preprocessor, None) == expected_f90

0 commit comments

Comments
 (0)