G cc повышенная оптимизация снижает скорость выполнения Cython - PullRequest
2 голосов
/ 07 августа 2020

Я использую MingW64 для компиляции Cython для Python 3.7. Задача представляет собой исключение, когда я вычисляю набор Джулии для сетки точек. Я слежу за книгой «High Performance Python», в которой рассказывается, как использовать Cython. Я ранее использовал флаг -O для настройки оптимизации для оборудования DSP и получил значительные улучшения, установив его выше, например -O3. Однако это не относится к коду Cython.

При выполнении того же самого с кодом Cython он стабильно дает более медленные результаты по мере увеличения оптимизации, что, кажется, не имеет смысла. Я получаю следующие тайминги:
-O1 = 0,41 с
-O2 = 0,46 с
-O3 = 0,47 с
-Ofast = 0,48 с
-Os = 0,419 с

У кого-нибудь есть идея, почему это, похоже, работает при противоположных оптимизациях?

Код cython находится в файле cythonfn.pyx

def calculate_z(maxiter, zs, cs):
    # add type primitives to improve execution time
    cdef unsigned int i, n
    cdef double complex z, c
    output = [0] * len(zs)
    for i in range(len(zs)):
        n = 0
        z = zs[i]
        c = cs
        # while n < maxiter and abs(z) < 2:
        while n < maxiter and (z.real * z.real + z.imag * z.imag) < 4: # increases performance
            z = z * z + c
            n += 1
            
        output[i] = n
    return output

Настройка здесь как setup.py

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

setup(
    cmdclass={'build_ext': build_ext},
    ext_modules=[Extension("calculate", ["cythonfn.pyx"])]
)

И основной код -

import calculate
import time

c_real,c_imag = -0.62772, -0.42193  # point for investigation
x1, x2, y1, y2 = -1.8, 1.8, -1.8, 1.8

def calc_julia(desired_width, max_iter):
    x_step = (float(x2 - x1) / float(desired_width))
    y_step = (float(y1 - y2) / float(desired_width))
    x = []
    y = []
    ycoord = y2
    while ycoord > y1:
        y.append(ycoord)
        ycoord += y_step
    xcoord = x1
    while xcoord < x2:
        x.append(xcoord)
        xcoord += x_step

    zs = []
    cs = complex(c_real, c_imag)
    for ycoord in y:
        for xcoord in x:
            zs.append(complex(xcoord, ycoord))
    st = time.time()
    output = calculate.calculate_z(max_iter, zs, cs)
    et = time.time()
    print(f"Took {et-st} seconds")

if __name__ == '__main__':
    calc_julia(desired_width=1000, max_iter=300)

И да Я знаю, что здесь есть ряд вещей, которые можно улучшить , например, cs может быть просто скаляром, поскольку все они одинаковы. Это скорее упражнение, но оно привело к неожиданным результатам для оптимизации.

1 Ответ

0 голосов
/ 08 августа 2020

Похоже, что ответ на этот вопрос хотя бы частично объясняется здесь, g cc флаг оптимизации -O3 делает код медленнее, чем -O2 .

Чтобы подвести итог результатов, -O3 фактически использует другую технику сравнения / ветвления, чем -O2, поэтому в этой задаче, где сравнение происходит много раз, важен выбор способа его выполнения. Из-за предсказуемости в l oop он добавляет время выполнения с помощью этой оптимизации.

Это все еще не решает причину перехода от -O1 к -O2, но этого источника достаточно для описания тот случай. Может быть, кто-то знает ответ на этот вопрос?

...