Производительность PyPy резко падает

#python #memory #triangulation #pypy #voxel

#python #память #триангуляция #pypy #воксель

Вопрос:

Я работаю над вокселизацией для моделей STL на основе разреженных воксельных восьмеричных деревьев. У меня есть 3D-массив, содержащий около 100 000 треугольников (каждый имеет 3 точки со значением x, y, z для каждой точки).

Мне нужно вычислить несколько точек и значений для каждого треугольника. Я написал этот алгоритм в стандартном CPython, и для запуска потребовалось 3 секунды, что для меня было слишком медленно (около 0,5 с было бы неплохо).

Я переключился на PyPy в качестве JIT-компилятора, и он работал очень хорошо. Если я запускаю код без набора A точек / значений (см. Код) для каждого треугольника (вычисляю ограничивающую рамку и нормаль), он превосходит CPython в 10 и более раз, и если я запускаю код без набора B точек, он также превосходит CPython. В тот момент, когда я запускаю алгоритм с обоими наборами, он работает намного медленнее, чем CPython (или Set A solo Set B solo вместе взятые).

Вы хоть представляете, в чем проблема прямо здесь? Я думал, что это может быть проблема с памятью. Я назначил больше памяти кучи для pypy в параметрах виртуальной машины, не сработало. Я удалил все переменные после того, как итерация цикла (с «del») не сработала.

Я использую Windows 10 Home версии 10.0.18363 на компьютере с ОС x64.

Я использую pypy3.6-v7.3.2rc1-win32 в PyCharm Community Edition 2020.2.2

Это параметры виртуальной машины:

 -Xmx2048m
-Xms750m 
-XX:ReservedCodeCacheSize=240m
-XX: UseConcMarkSweepGC
-XX:SoftRefLRUPolicyMSPerMB=50
-ea
-XX:CICompilerCount=2
-Dsun.io.useCanonPrefixCache=false
-Djdk.http.auth.tunneling.disabledSchemes=""
-XX: HeapDumpOnOutOfMemoryError
-XX:-OmitStackTraceInFastThrow
-Djdk.attach.allowAttachSelf=true
-Dkotlinx.coroutines.debug=off
-Djdk.module.illegalAccess.silent=true
  

Большое вам спасибо за каждое предложение.

 import trimesh as tr
import sys
import datetime

def doT(a,b):
    res=a[0]*b[0] a[1]*b[1] a[2]*b[2]
    return res

def doT2(a,b):
    res=a[0]*b[0] a[1]*b[1]
    return res

def minus (a,b):
    res=[a[0]-b[0],a[1]-b[1],a[2]-b[2]]
    return res

def plus (a,b):
    res = [a[0]   b[0], a[1]   b[1], a[2]   b[2]]
    return res
def crosS(a,b):
    res=[a[1]*b[2]-a[2]*b[1],a[2]*b[0]-a[0]*b[2],a[0]*b[1]-a[1]-b[0]]
    return res

def miN(a,b):
    if a<b:
        return a
    else:
        return b

def maX(a,b):
    if a>b:
        return a
    else:
        return b

trim=tr.load("Stanford_Bunny.stl")
trim.rezero()
triangle_list_tr=trim.triangles
triangle_list=triangle_list_tr.tolist()

for triangle in triangle_list:
    'vertices of surface triangle'
    P1 = triangle[0]
    P2 = triangle[1]
    P3 = triangle[2]

    n = crosS(minus(P1, P2), minus(P3, P2))
    n_sum = n[0]   n[1]   n[2]
    n[0] = n[0] / abs(n_sum)
    n[1] = n[1] / abs(n_sum)
    n[2] = n[2] / abs(n_sum)

    P1x = triangle[0][0]
    P1y = triangle[0][1]
    P1z = triangle[0][2]
    P2x = triangle[1][0]
    P2y = triangle[1][1]
    P2z = triangle[1][2]
    P3x = triangle[2][0]
    P3y = triangle[2][1]
    P3z = triangle[2][2]
    counter =1
    
    #Set A start
    bbxmin = min(P1x, P2x, P3x)
    bbxmax = max(P1x, P2x, P3x)
    bbymin = min(P1y, P2y, P3y)
    bbymax = max(P1y, P2y, P3y)
    bbzmin = min(P1z, P2z, P3z)
    bbzmax = max(P1z, P2z, P3z)
    #Set A End
    
    #Set B Start
    P1_xy = [P1[0], P1[1]]
    P2_xy = [P2[0], P2[1]]
    P3_xy = [P3[0], P3[1]]
    
    if n[2] >= 0:
        e_xy_12 = [-1 * (P1[1] - P2[1]), P1[0] - P2[0]]
        e_xy_23 = [-1 * (P2[1] - P3[1]), P2[0] - P3[0]]
        e_xy_31 = [-1 * (P3[1] - P1[1]), P3[0] - P1[0]]
    else:
        e_xy_12 = [-1 * (P2[1] - P1[1]), P2[0] - P1[0]]
        e_xy_23 = [-1 * (P3[1] - P2[1]), P3[0] - P2[0]]
        e_xy_31 = [-1 * (P1[1] - P3[1]), P1[0] - P3[0]]

   
    P1_xz = [P1[0], P1[2]]
    P2_xz = [P2[0], P2[2]]
    P3_xz = [P3[0], P3[2]]
    
    if n[1] >= 0:
        e_xz_12 = [-1 * (P2[2] - P1[2]), P2[0] - P1[0]]
        e_xz_23 = [-1 * (P3[2] - P2[2]), P3[0] - P2[0]]
        e_xz_31 = [-1 * (P1[2] - P3[2]), P1[0] - P3[0]]
    else:
        e_xz_12 = [-1 * (P1[2] - P2[2]), P1[0] - P2[0]]
        e_xz_23 = [-1 * (P2[2] - P3[2]), P2[0] - P3[0]]
        e_xz_31 = [-1 * (P3[2] - P1[2]), P3[0] - P1[0]]
    
    

    P1_yz = [P1[1], P1[2]]
    P2_yz = [P2[1], P2[2]]
    P3_yz = [P3[1], P3[2]]
    
    if n[0] >= 0:
        e_yz_12 = [-1 * (P1[2] - P2[2]), P1[1] - P2[1]]
        e_yz_23 = [-1 * (P2[2] - P3[2]), P2[1] - P3[1]]
        e_yz_31 = [-1 * (P3[2] - P1[2]), P3[1] - P1[1]]
    else:
        e_yz_12 = [-1 * (P2[2] - P1[2]), P2[1] - P1[1]]
        e_yz_23 = [-1 * (P3[2] - P2[2]), P3[1] - P2[1]]
        e_yz_31 = [-1 * (P1[2] - P3[2]), P1[1] - P3[1]]
    #Set B End

    del bbxmin
    del bbxmax
    del bbymin
    del bbymax
    del bbzmin
    del bbzmax
    del P1x
    del P1y
    del P1z
    del P2x
    del P2y
    del P2z
    del P3x
    del P3y
    del P3z
    del P1_xy
    del P2_xy
    del P3_xy
    del P1_xz
    del P2_xz
    del P3_xz
    del P1_yz
    del P2_yz
    del P3_yz
    del P1
    del P2
    del P3
    del e_yz_12
    del e_yz_23
    del e_yz_31
    del e_xz_12
    del e_xz_23
    del e_xz_31
    del e_xy_12
    del e_xy_23
    del e_xy_31
  

Комментарии:

1. Я вижу, вы импортируете numpy. Это будет выполняться медленно в PyPy, поскольку оно написано с интенсивным использованием Python C-API. Возможно, trimesh также использует C-API, я с ним не знаком.

2. Спасибо вам за ваш ответ. Я знаю, что numpy и другие пакеты на основе C-APi замедлят производительность pypy. Я использую trimesh для импорта файла STL. Описанные различия в производительности появляются в другой части программы, но вы правы в том, что aorund trimesh был бы идеальным. Numpy больше не используется (за исключением части trimesh), и импорт — это просто остаток от более старой версии. Я отредактирую сообщение соответствующим образом и удалю импорт.

Ответ №1:

Pypy работает медленнее, если в коде много вложенных if else. CPython уже намного более оптимизирован для if else. Если вы используете python надлежащим образом (не так, как C), тогда pypy будет быстрее.