1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
# Copyright (c) 2023, Manfred Moitzi
# License: MIT License
from __future__ import annotations
import time
import random
import math
import numpy as np
from ezdxf.acc import USE_C_EXT
from ezdxf.math import Matrix44, Vec2
from ezdxf.math._matrix44 import Matrix44 as PyMatrix44
RUNS = 10_000
POINT_COUNT = 1000
def points(count):
return [
Vec2(random.randint(-1000, 1000), random.randint(-1000, 1000))
for _ in range(count)
]
def make_cy_m44(sx, sy, angle, tx, ty):
return (
Matrix44.scale(sx, sy, 1)
@ Matrix44.z_rotate(angle)
@ Matrix44.translate(tx, ty, 0)
)
def make_py_m44(sx, sy, angle, tx, ty):
return (
PyMatrix44.scale(sx, sy, 1)
@ PyMatrix44.z_rotate(angle)
@ PyMatrix44.translate(tx, ty, 0)
)
def python_manual_transform(a, b, c, d, e, f, points: list[Vec2]):
# runs faster in pure Python mode and PyPy
return (Vec2(x * a + y * c + e, x * b + y * d + f) for x, y in points)
def transform_vertices(points, count):
m44 = make_cy_m44(2, 2, math.pi / 2, 100, 200)
for _ in range(count):
result = list(m44.transform_vertices(points))
def fast_2d_transform(points, count):
# CPython (c-extension): ~1.1x faster than a transform_vertices()
# PyPy: ~4x faster than a transform_vertices()
m44 = make_cy_m44(2, 2, math.pi / 2, 100, 200)
for _ in range(count):
result = list(m44.fast_2d_transform(points))
def array_2d_inplace_transform_cython(points, count):
# CPython (cython): 34.9x faster than fast_2d_transform()
array = np.array(points, dtype=np.float64)
m44 = make_cy_m44(2, 2, math.pi / 2, 100, 200)
for _ in range(count):
copy = array.copy()
m44.transform_array_inplace(copy, 2)
def array_2d_inplace_transform_python(points, count):
array = np.array(points, dtype=np.float64)
m44 = make_py_m44(2, 2, math.pi / 2, 100, 200)
for _ in range(count):
copy = array.copy()
m44.transform_array_inplace(copy, 2)
def translate_points_by_transform_vertices(points, count):
m44 = Matrix44.translate(10, 20, 0)
for _ in range(count):
result = list(m44.transform_vertices(points))
def translate_points_by_fast_2d_transform(points, count):
m44 = Matrix44.translate(10, 20, 0)
for _ in range(count):
result = list(m44.fast_2d_transform(points))
def translate_points_by_vec2_addition(points, count):
# CPython (c-extension):
# ~1.3x faster than transform_vertices()
# ~1.2x faster than fast_2d_transformation()
# PyPy:
# ~12-14x faster than transform_vertices()
# ~3-4x faster than fast_2d_transformation()
offset = Vec2(10, 20)
for _ in range(count):
result = [offset + p for p in points]
def translate_and_scale_points_by_vec2_add_mul(points, count):
# CPython (c-extension): 0.7x (slower) than fast_2d_transform()
# PyPy: ~3x faster than fast_2d_transform()
offset = Vec2(10, 20)
for _ in range(count):
result = [offset + (p * 2.0) for p in points]
def profile1(func, *args) -> float:
t0 = time.perf_counter()
func(*args)
t1 = time.perf_counter()
return t1 - t0
def profile(text, func0, func1, points):
run0 = profile1(func0, points, RUNS)
run1 = profile1(func1, points, RUNS)
ratio = run0 / run1
print(f"{func0.__name__} - {text} {run0:.3f}s")
print(f"{func1.__name__} - {text} {run1:.3f}s")
print(f"Ratio {ratio:.1f}x")
print(f"C-extension is {USE_C_EXT}")
print(f"Profiling 2d transformation:")
profile(
f"inplace transformation (fast/cy) of {POINT_COUNT} random points, {RUNS} times: ",
translate_points_by_fast_2d_transform,
array_2d_inplace_transform_cython,
points(POINT_COUNT),
)
profile(
f"inplace transformation (fast/py) of {POINT_COUNT} random points, {RUNS} times: ",
translate_points_by_fast_2d_transform,
array_2d_inplace_transform_python,
points(POINT_COUNT),
)
profile(
f"inplace transformation (py/cy) of {POINT_COUNT} random points, {RUNS} times: ",
array_2d_inplace_transform_python,
array_2d_inplace_transform_cython,
points(POINT_COUNT),
)
profile(
f"fast transform {POINT_COUNT} random points, {RUNS} times: ",
transform_vertices,
fast_2d_transform,
points(POINT_COUNT),
)
profile(
f"translate {POINT_COUNT} random points, {RUNS} times: ",
translate_points_by_fast_2d_transform,
translate_points_by_vec2_addition,
points(POINT_COUNT),
)
profile(
f"translate {POINT_COUNT} random points, {RUNS} times: ",
translate_points_by_transform_vertices,
translate_points_by_vec2_addition,
points(POINT_COUNT),
)
profile(
f"translate and scale {POINT_COUNT} random points, {RUNS} times: ",
translate_points_by_fast_2d_transform,
translate_and_scale_points_by_vec2_add_mul,
points(POINT_COUNT),
)
|