Skip to content

Commit b87b19d

Browse files
Merge pull request #61 from pypr/fix-show-source
Many fixes and improvements
2 parents 5c62fa0 + 18e9f40 commit b87b19d

File tree

9 files changed

+263
-81
lines changed

9 files changed

+263
-81
lines changed

README.rst

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
ComPyle: execute a subset of Python on HPC platforms
1+
Compyle: execute a subset of Python on HPC platforms
22
======================================================
33

44
|Travis Status| |Appveyor Status| |Coverage Status| |Documentation Status|
@@ -14,38 +14,48 @@ ComPyle: execute a subset of Python on HPC platforms
1414
.. |Coverage Status| image:: https://codecov.io/gh/pypr/compyle/branch/master/graph/badge.svg
1515
:target: https://codecov.io/gh/pypr/compyle
1616

17-
ComPyle allows users to execute a restricted subset of Python (almost similar
18-
to C) on a variety of HPC platforms. Currently we support multi-core execution
19-
using Cython, and OpenCL and CUDA for GPU devices.
17+
Compyle allows users to execute a restricted subset of Python (almost similar
18+
to C) on a variety of HPC platforms. Currently we support multi-core CPU
19+
execution using Cython, and for GPU devices we use OpenCL or CUDA.
2020

2121
Users start with code implemented in a very restricted Python syntax, this code
2222
is then automatically transpiled, compiled and executed to run on either one CPU
23-
core, or multiple CPU cores (via OpenMP_) or on a GPU. ComPyle offers
23+
core, or multiple CPU cores (via OpenMP_) or on a GPU. Compyle offers
2424
source-to-source transpilation, making it a very convenient tool for writing HPC
2525
libraries.
2626

27-
Some simple yet powerful parallel utilities are provided which can allow you to
28-
solve a remarkably large number of interesting HPC problems.
27+
Some simple yet powerful parallel utilities are provided which can allow you
28+
to solve a remarkably large number of interesting HPC problems. Compyle also
29+
features JIT transpilation making it easy to use.
2930

30-
ComPyle also features JIT transpilation if you wish making it easy to use.
31+
Documentation and learning material is also available in the form of:
3132

32-
Documentation is available at: https://compyle.readthedocs.io
33+
- Documentation at: https://compyle.readthedocs.io
3334

34-
While ComPyle seems simple it is not a toy and is used heavily by the PySPH_
35-
project where ComPyle has its origins.
35+
- An introduction to compyle in the context of writing a parallel molecular
36+
dynamics simulator is in our `SciPy 2020 paper
37+
<http://conference.scipy.org/proceedings/scipy2020/compyle_pr_ab.html>`_.
38+
39+
- `Compyle poster presentation <https://docs.google.com/presentation/d/1LS9XO5pQXz8G5d27RP5oWLFxUA-Fr5OvfVUGsgg86TQ/edit#slide=id.p>`_
40+
41+
- You may also try Compyle online for free on a `Google Colab notebook`_.
42+
43+
While Compyle seems simple it is not a toy and is used heavily by the PySPH_
44+
project where Compyle has its origins.
3645

3746
.. _PySPH: https://github.com/pypr/pysph
47+
.. _Google Colab notebook: https://colab.research.google.com/drive/1SGRiArYXV1LEkZtUeg9j0qQ21MDqQR2U?usp=sharing
3848

3949

4050
Installation
4151
-------------
4252

43-
ComPyle is itself pure Python but depends on numpy_ and requires either Cython_
44-
or PyOpenCL_ or PyCUDA_ along with the respective backends of a C/C++ compiler,
45-
OpenCL and CUDA. If you are only going to execute code on a CPU then all you
46-
need is Cython.
53+
Compyle is itself largely pure Python but depends on numpy_ and requires
54+
either Cython_ or PyOpenCL_ or PyCUDA_ along with the respective backends of a
55+
C/C++ compiler, OpenCL and CUDA. If you are only going to execute code on a
56+
CPU then all you need is Cython.
4757

48-
You should be able to install ComPyle by doing::
58+
You should be able to install Compyle by doing::
4959

5060
$ pip install compyle
5161

@@ -95,3 +105,5 @@ Examples
95105

96106
Some simple examples and benchmarks are available in the `examples
97107
<https://github.com/pypr/compyle/tree/master/examples>`_ directory.
108+
109+
You may also run these examples on the `Google Colab notebook`_

compyle/api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from .extern import Extern
1010
from .low_level import Kernel, LocalMem, Cython
1111
from .parallel import (
12-
Elementwise, Reduction, elementwise
12+
Elementwise, Reduction, Scan, elementwise
1313
)
1414
from .translator import (
1515
CConverter, CStructHelper, OpenCLConverter, detect_type, ocl_detect_type,

compyle/jit.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,8 @@ def __init__(self, func, backend=None):
293293
self.func = func
294294
self._config = get_config()
295295
self.cython_gen = CythonGenerator()
296+
self.source = '# Code jitted, call the function to generate the code.'
297+
self.all_source = self.source
296298
if backend == 'opencl':
297299
from .opencl import get_context, get_queue
298300
self.queue = get_queue()
@@ -369,6 +371,8 @@ def __init__(self, reduce_expr, map_func=None, dtype_out=np.float64,
369371
self.neutral = neutral
370372
self._config = get_config()
371373
self.cython_gen = CythonGenerator()
374+
self.source = '# Code jitted, call the function to generate the code.'
375+
self.all_source = self.source
372376
if backend == 'opencl':
373377
from .opencl import get_context, get_queue
374378
self.queue = get_queue()
@@ -450,6 +454,8 @@ def __init__(self, input=None, output=None, scan_expr="a+b",
450454
else:
451455
self.neutral = neutral
452456
self._config = get_config()
457+
self.source = '# Code jitted, call the function to generate the code.'
458+
self.all_source = self.source
453459
self.cython_gen = CythonGenerator()
454460
if backend == 'opencl':
455461
from .opencl import get_context, get_queue

compyle/parallel.py

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
from mako.template import Template
1313
import numpy as np
14-
import time
1514

1615
from .config import get_config
1716
from .profile import profile
@@ -412,6 +411,10 @@ def __init__(self, func, backend=None):
412411
self._config = get_config()
413412
self.cython_gen = CythonGenerator()
414413
self.queue = None
414+
# This is the source generated for the user code.
415+
self.source = '# Source not yet generated.'
416+
# This is all the source code used for the elementwise.
417+
self.all_source = '# Source not yet generated.'
415418
self.c_func = self._generate()
416419

417420
def _generate(self, declarations=None):
@@ -433,8 +436,12 @@ def _generate(self, declarations=None):
433436
self.func, 'is_serial', False),
434437
get_parallel_range=get_parallel_range
435438
)
439+
# This is the user code source.
440+
self.source = self.tp.get_code()
436441
self.tp.add_code(src)
437442
self.tp.compile()
443+
# All the source code for the elementwise
444+
self.all_source = self.tp.source
438445
return getattr(self.tp.mod, 'py_' + self.name[7:])
439446
elif self.backend == 'opencl':
440447
py_data, c_data = self.cython_gen.get_func_signature(self.func)
@@ -462,6 +469,10 @@ def _generate(self, declarations=None):
462469
operation=expr,
463470
preamble="\n".join([cluda_preamble, preamble])
464471
)
472+
# only code we generate is saved here.
473+
self.source = "\n".join([cluda_preamble, preamble])
474+
all_source = knl.get_kernel(False)[0].program.source
475+
self.all_source = all_source or self.source
465476
return knl
466477
elif self.backend == 'cuda':
467478
py_data, c_data = self.cython_gen.get_func_signature(self.func)
@@ -487,6 +498,10 @@ def _generate(self, declarations=None):
487498
operation=expr,
488499
preamble="\n".join([cluda_preamble, preamble])
489500
)
501+
# only code we generate is saved here.
502+
self.source = cluda_preamble + preamble
503+
# FIXME: it is difficult to get the sources from pycuda.
504+
self.all_source = self.source
490505
return knl
491506

492507
def _correct_opencl_address_space(self, c_data):
@@ -551,6 +566,9 @@ def __init__(self, func, backend=None):
551566
def __getattr__(self, name):
552567
return getattr(self.elementwise, name)
553568

569+
def __dir__(self):
570+
return sorted(dir(self.elementwise) + ['elementwise'])
571+
554572
def __call__(self, *args, **kwargs):
555573
self.elementwise(*args, **kwargs)
556574

@@ -588,6 +606,10 @@ def __init__(self, reduce_expr, map_func=None, dtype_out=np.float64,
588606
self._config = get_config()
589607
self.cython_gen = CythonGenerator()
590608
self.queue = None
609+
# This is the source generated for the user code.
610+
self.source = '# Source not yet generated.'
611+
# This is all the source code used.
612+
self.all_source = '# Source not yet generated.'
591613
self.c_func = self._generate()
592614

593615
def _generate(self, declarations=None):
@@ -621,8 +643,11 @@ def _generate(self, declarations=None):
621643
openmp=self._config.use_openmp,
622644
get_parallel_range=get_parallel_range
623645
)
646+
# This is the user code source.
647+
self.source = self.tp.get_code()
624648
self.tp.add_code(src)
625649
self.tp.compile()
650+
self.all_source = self.tp.source
626651
return getattr(self.tp.mod, 'py_' + self.name)
627652
elif self.backend == 'opencl':
628653
if self.func is not None:
@@ -661,6 +686,17 @@ def _generate(self, declarations=None):
661686
arguments=arguments,
662687
preamble="\n".join([cluda_preamble, preamble])
663688
)
689+
# only code we generate is saved here.
690+
self.source = "\n".join([cluda_preamble, preamble])
691+
if knl.stage_1_inf.source:
692+
self.all_source = "\n".join([
693+
"// ------ stage 1 -----",
694+
knl.stage_1_inf.source,
695+
"// ------ stage 2 -----",
696+
knl.stage_2_inf.source,
697+
])
698+
else:
699+
self.all_source = self.source
664700
return knl
665701
elif self.backend == 'cuda':
666702
if self.func is not None:
@@ -697,6 +733,10 @@ def _generate(self, declarations=None):
697733
arguments=arguments,
698734
preamble="\n".join([cluda_preamble, preamble])
699735
)
736+
# only code we generate is saved here.
737+
self.source = cluda_preamble + preamble
738+
# FIXME: it is difficult to get the sources from pycuda.
739+
self.all_source = self.source
700740
return knl
701741

702742
def _correct_return_type(self, c_data):
@@ -780,6 +820,9 @@ def __init__(self, reduce_expr, map_func=None, dtype_out=np.float64,
780820
neutral=neutral,
781821
backend=backend)
782822

823+
def __dir__(self):
824+
return sorted(dir(self.reduction) + ['reduction'])
825+
783826
def __getattr__(self, name):
784827
return getattr(self.reduction, name)
785828

@@ -812,6 +855,10 @@ def __init__(self, input=None, output=None, scan_expr="a+b",
812855
else:
813856
self.neutral = neutral
814857
self._config = get_config()
858+
# This is the source generated for the user code.
859+
self.source = '# Source not yet generated.'
860+
# This is all the source code used for the elementwise.
861+
self.all_source = '# Source not yet generated.'
815862
self.cython_gen = CythonGenerator()
816863
self.queue = None
817864
self.c_func = self._generate()
@@ -895,7 +942,6 @@ def _append_cython_arg_data(self, all_py_data, all_c_data, py_data,
895942
all_c_data[1].extend(self._filter_ignored(c_data[1], select))
896943

897944
def _generate_cython_code(self, declarations=None):
898-
name = self.name
899945
all_py_data = [[], []]
900946
all_c_data = [[], []]
901947

@@ -911,7 +957,9 @@ def _generate_cython_code(self, declarations=None):
911957
# Process segment function
912958
use_segment = True if self.is_segment_func is not None else False
913959
py_data, c_data, segment_expr = self._wrap_cython_code(
914-
self.is_segment_func, func_type='segment', declarations=declarations)
960+
self.is_segment_func, func_type='segment',
961+
declarations=declarations
962+
)
915963
self._append_cython_arg_data(all_py_data, all_c_data, py_data, c_data)
916964

917965
# Process output expression
@@ -963,8 +1011,10 @@ def _generate_cython_code(self, declarations=None):
9631011
is_segment_start_expr=segment_expr,
9641012
complex_map=self.complex_map
9651013
)
1014+
self.source = self.tp.get_code()
9661015
self.tp.add_code(src)
9671016
self.tp.compile()
1017+
self.all_source = self.tp.source
9681018
return getattr(self.tp.mod, 'py_' + self.name)
9691019

9701020
def _wrap_ocl_function(self, func, func_type=None, declarations=None):
@@ -1053,6 +1103,18 @@ def _generate_opencl_kernel(self, declarations=None):
10531103
is_segment_start_expr=segment_expr,
10541104
preamble=preamble
10551105
)
1106+
self.source = preamble
1107+
if knl.first_level_scan_info.kernel.program.source:
1108+
self.all_source = '\n'.join([
1109+
'// ----- Level 1 ------',
1110+
knl.first_level_scan_info.kernel.program.source,
1111+
'// ----- Level 2 ------',
1112+
knl.second_level_scan_info.kernel.program.source,
1113+
'// ----- Final output ------',
1114+
knl.final_update_info.kernel.program.source,
1115+
])
1116+
else:
1117+
self.all_source = self.source
10561118
return knl
10571119

10581120
def _generate_cuda_kernel(self, declarations=None):
@@ -1073,6 +1135,9 @@ def _generate_cuda_kernel(self, declarations=None):
10731135
is_segment_start_expr=segment_expr,
10741136
preamble=preamble
10751137
)
1138+
self.source = preamble
1139+
# FIXME: Difficult to get the pycuda sources
1140+
self.all_source = self.source
10761141
return knl
10771142

10781143
def _add_address_space(self, arg):
@@ -1113,11 +1178,13 @@ def _massage_arg(self, x):
11131178
def __call__(self, **kwargs):
11141179
c_args_dict = {k: self._massage_arg(x) for k, x in kwargs.items()}
11151180
if self._get_backend_key() in self.output_func.arg_keys:
1116-
output_arg_keys = self.output_func.arg_keys[self._get_backend_key()]
1181+
output_arg_keys = self.output_func.arg_keys[
1182+
self._get_backend_key()
1183+
]
11171184
else:
11181185
raise ValueError("No kernel arguments found for backend = %s, "
1119-
"use_openmp = %s, use_double = %s" %
1120-
self._get_backend_key())
1186+
"use_openmp = %s, use_double = %s" %
1187+
self._get_backend_key())
11211188

11221189
if self.backend == 'cython':
11231190
size = len(c_args_dict[output_arg_keys[1]])
@@ -1165,6 +1232,9 @@ def __init__(self, input=None, output=None, scan_expr="a+b",
11651232
complex_map=complex_map,
11661233
backend=backend)
11671234

1235+
def __dir__(self):
1236+
return sorted(dir(self.scan) + ['scan'])
1237+
11681238
def __getattr__(self, name):
11691239
return getattr(self.scan, name)
11701240

0 commit comments

Comments
 (0)