Skip to content

Commit

Permalink
Provide --trace-allocations switch
Browse files Browse the repository at this point in the history
When turned on, Pythran instrument the generated code so that it dumps
line number information about python statements that lead to memory allocation.
  • Loading branch information
serge-sans-paille committed Feb 5, 2024
1 parent d9f4cfc commit 801e1af
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 11 deletions.
2 changes: 2 additions & 0 deletions docs/CLI.rst
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ To know more options about Pythran, you can check::
usage: pythran [-h] [-o OUTPUT_FILE] [-P] [-E] [-e] [-v] [-w] [-V] [-p pass]
[-I include_dir] [-L ldflags] [-D macro_definition]
[-U macro_definition] [--config config] [-ftime-report]
[--trace-allocations]
input_file
pythran: a python to C++ compiler
Expand All @@ -103,5 +104,6 @@ To know more options about Pythran, you can check::
-U macro_definition any macro undef relevant to the underlying C++ compiler
--config config config additional params
-ftime-report report time spent in each optimization/transformation
--trace-allocations instrument execution to trace memory allocations
It's a megablast!
23 changes: 17 additions & 6 deletions docs/MANUAL.rst
Original file line number Diff line number Diff line change
Expand Up @@ -459,10 +459,6 @@ One can use ``-o <filename>`` or ``--output=<filename>`` to control the name of
the generated file. If ``<filename>`` contains the ``%{ext}`` pattern, it is
replaced by the extension that matches your current platform.

A failing compilation? A lust for c++ tangled code? Give a try to the ``-E``
switch that stops the compilation process right after c++ code generation, so
that you can inspect it.

Want more performance? Big fan of ``-Ofast -march=native``? Pythran
_automagically_ forwards these switches to the underlying compiler!

Expand Down Expand Up @@ -530,8 +526,8 @@ code you can call from a C++ program. In that case there is **no** need for a
particular Pythran specification.


Read the optimized Python code
------------------------------
Understanding the optimized Python code
--------------------------------------

Curious Python developers might want to study how Pythran transforms their
codes. With the ``-P`` switch, Pythran optimizes the Python code, prints the
Expand All @@ -540,6 +536,21 @@ formatter is often useful::

$> pythran -P arc_distance.py | yapf

The ``-E`` switch stops the compilation process right after c++ code generation, so
that you can inspect it::

$> pythran -E arc_distance.py

In the example above, an ``arc_distance.cpp`` file is generated and it provides
some pleasant code reading to the educated eyes. Setting the
``backend.annotate`` option to ``true``, for instance through ``--config
backend.annotate=true``, generates extra comments with hints on the origin of
each statement.

One can also trace every allocation within the generated kernel by passing
``--trace-allocations`` to the compiler. When run, the resulting code dumps on
the stanard error stream information about stack allocation and their origin.


Customizing Your ``.pythranrc``
-------------------------------
Expand Down
8 changes: 6 additions & 2 deletions pythran/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from pythran.cxxgen import Value, FunctionDeclaration, EmptyStatement, Nop
from pythran.cxxgen import FunctionBody, Line, ReturnStatement, Struct, Assign
from pythran.cxxgen import For, While, TryExcept, ExceptHandler, If, AutoFor
from pythran.cxxgen import StatementWithComments
from pythran.cxxgen import StatementWithComments, InstrumentedStatement
from pythran.openmp import OMPDirective
from pythran.passmanager import Backend
from pythran.syntax import PythranSyntaxError
Expand Down Expand Up @@ -227,7 +227,11 @@ def add_line_info(self, node, cxx_node):
if isinstance(node, ast.FunctionDef):
head, tail = cxx_node
return head, [StatementWithComments(t, line) for t in tail]
return StatementWithComments(cxx_node, line)
if cfg.get('backend', 'annotation_kind') == 'lineno':
return InstrumentedStatement(cxx_node,
'pythran_trace_lineno({});'.format(node.lineno))
else:
return StatementWithComments(cxx_node, line)

def skip_line_info(self, node, cxx_node):
return cxx_node
Expand Down
11 changes: 11 additions & 0 deletions pythran/cxxgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,17 @@ def generate(self):
yield s


class InstrumentedStatement(object):
def __init__(self, stmt,instrumentation):
self.stmt = stmt
self.instrumentation = instrumentation

def generate(self):
yield self.instrumentation
for s in self.stmt.generate():
yield s


class ReturnStatement(Statement):
def generate(self):
yield "return " + self.text + ";"
Expand Down
26 changes: 23 additions & 3 deletions pythran/pythonic/include/utils/allocate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,46 @@
#define PYTHONIC_INCLUDE_UTILS_ALLOCATE_HPP

#include <cstdlib>
#ifdef PYTHRAN_TRACE_ALLOCATION
#include <cstdio>
#endif

PYTHONIC_NS_BEGIN

namespace utils
{

#ifdef PYTHRAN_TRACE_ALLOCATION
extern size_t pythran_allocation_site;
#define pythran_trace_lineno(n) pythonic::utils::pythran_allocation_site = n;
#define pythran_trace_allocation(n) \
do { \
fprintf(stderr, ":%d: Allocating %d bytes\n", \
pythonic::utils::pythran_allocation_site, n); \
} while (0)
#else
#define pythran_trace_lineno(s)
#define pythran_trace_allocation(n)
#endif

template <class T>
inline T *allocate(size_t nmemb)
{
pythran_trace_allocation(sizeof(T) * nmemb);
return (T *)malloc(sizeof(T) * nmemb);
}

template <class T>
inline T *callocate(size_t nmemb)
{
pythran_trace_allocation(sizeof(T) * nmemb);
return (T *)calloc(nmemb, sizeof(T));
}

template <class T>
inline T *reallocate(T *prev, size_t nmemb)
{
pythran_trace_allocation(sizeof(T) * nmemb);
return (T *)realloc(prev, sizeof(T) * nmemb);
}

Expand Down Expand Up @@ -61,9 +81,9 @@ namespace utils
template <class U>
constexpr bool operator!=(const allocator<U> &) const
{
return false;
}
}; // namespace utils
return false;
}
}; // namespace utils

} // namespace utils
PYTHONIC_NS_END
Expand Down
10 changes: 10 additions & 0 deletions pythran/pythonic/utils/allocate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,15 @@
#define PYTHONIC_UTILS_ALLOCATE_HPP

#include "pythonic/include/utils/allocate.hpp"
PYTHONIC_NS_BEGIN

namespace utils
{
#ifdef PYTHRAN_TRACE_ALLOCATION
size_t pythran_allocation_site;
#endif
}

PYTHONIC_NS_END

#endif
3 changes: 3 additions & 0 deletions pythran/pythran.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,6 @@ max_heterogeneous_sequence_size = 16
# set to true if you want intermediate C++ code to be annotated with a reference
# to the original python code
annotate = false

# set to 'lineno' if you want to generate line number instead of python extract
annotation_kind = 'comment'
10 changes: 10 additions & 0 deletions pythran/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,21 @@ def run():
action='store_true',
help='report time spent in each optimization/transformation')

parser.add_argument('--trace-allocations', dest='trace_allocations',
action='store_true',
help='instrument execution to trace memory allocations')

parser.convert_arg_line_to_args = convert_arg_line_to_args

args, extra = parser.parse_known_args(sys.argv[1:])
args.extra_flags = extra

if args.trace_allocations:
args.defines.append('PYTHRAN_TRACE_ALLOCATION')
args.config.append("backend.annotate=1")
args.config.append("backend.annotation_kind=lineno")


if args.raw_translate_only:
args.translate_only = True
args.undefs.append('ENABLE_PYTHON_MODULE')
Expand Down

0 comments on commit 801e1af

Please sign in to comment.