Cythonで行プロファイリング

# 概要

Cythonコード「`*.pyx`」内の「`cdef`」関数内を行プロファイリングする方法

# 1. ライブラリのインストール

```
pip install line_profiler
```

# 2. pyxファイルの改変

## 2.1. ファイル冒頭に行プロファイル宣言追加

pyxファイルの先頭にコメント行として、プロファイル宣言を設定する

変更前）

```
# -*- coding: utf-8 -*-
#
import math
...
```

変更後）

```
# -*- coding: utf-8 -*-
#
# cython: profile=True
# cython: linetrace=True
# cython: binding=True
# distutils: define_macros=CYTHON_TRACE_NOGIL=1
import math
...
```

## 2.2. profileデコレータの呼び出し

`cdef` には関数デコレータが付けられないため、呼び出したい関数をprofileで囲む

変更前）

```python
a = funca(a1, a2)

funcb(a, b1)
```

変更後）

```python
pfa = profile(funca)
a = pfa(a1, a2)

pfb = profile(funcb)
pfb(a, b1)
```

## 2.3. `cdef` 関数を `cpdef`関数に変更

`cdef` 関数のままだと、`stringsource.wrap` でエラーになる

変更前）

```python
cdef funca(a1, a2):
    return a1 * a2
```

変更後）

```python
cpdef funca(a1, a2):
    return a1 * a2
```

# 3. setup.py の変更

## 3.1. Extentionのパラメーター追加

変更前）

```python
       Extension("service.AService", sources=["service/AService.pyx"], include_dirs=['.']), 
```

変更後）

```python
       Extension("service.AService", sources=["service/AService.pyx"], include_dirs=['.'], define_macros=[('CYTHON_TRACE', '1')]), 
```

## 3.2. cythonizeの変更

```python
setup(name="*", cmdclass={"build_ext": build_ext}, ext_modules=cythonize(setup_ext.ext, annotate=True, \
      compiler_directives={'language_level': "3"}))
```

変更後）

```python
setup(name="*", cmdclass={"build_ext": build_ext}, ext_modules=cythonize(setup_ext.ext, annotate=True, \
      compiler_directives={'language_level': "3", 'profile': True, 'linetrace': True, 'binding': True}))
```

# 4. コンパイル

`setup.py` のコンパイル時点で、`kernprof`を呼び出す

```
kernprof -l setup.py build_ext --inplace
```

# 5. プロファイル実行

```
kernprof -l -v executor.py
```

# 結果

```
Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
   503                                               cpdef dict prepare_avoidance_dataset(self, int data_set_idx, str direction):
   504         2      29445.0  14722.5      0.1          logger.info("接触回避準備【No.%s - %s】", (data_set_idx + 1), direction)
   505
   506                                                   cdef int aidx, fno, from_fno, prev_block_fno, to_fno
   507                                                   cdef float block_x_distance, block_z_distance, x_distance, z_distance
   508                                                   cdef list all_avoidance_list, fnos, prev_collisions
   509                                                   cdef dict all_avoidance_axis, rep_avbone_global_3ds, rep_avbone_global_mats, rep_global_3ds, rep_matrixs, avoidance_list
   510                                                   cdef str avoidance_name, bone_name
   511                                                   cdef bint collision, near_collision
   512                                                   cdef BoneLinks arm_link, avodance_link
   513                                                   cdef ArmAvoidanceOption avoidance_options
   514                                                   cdef MOptionsDataSet data_set
   515                                                   cdef OBB obb
   516                                                   cdef RigidBody avoidance
   517                                                   cdef MVector3D rep_x_collision_vec, rep_z_collision_vec
   518
   519         2       1638.0    819.0      0.0          logger.copy(self.options)
   520                                                   # 処理対象データセット
   521         2         29.0     14.5      0.0          data_set = self.options.data_set_list[data_set_idx]
   522

(略)
```

# 後始末

プロファイリングが終わったら、コードを元に戻しておくこと