Mam prosty raytracer w python. renderowanie obrazu 200x200 zajmuje 4 minuty, co zdecydowanie jest zbyt duże jak na mój gust. Chcę poprawić sytuację.Poprawa wydajności funkcji uderzenia raytracing
Kilka punktów: Fotografuję wiele promieni na każdy piksel (w celu zapewnienia wygładzenia krawędzi), co daje łącznie 16 promieni na piksel. 200x200x16 to suma 640000 promieni. Każdy promień musi zostać przetestowany pod kątem wpływu na wiele obiektów Sfery na scenie. Ray jest również dość trywialny przedmiot
class Ray(object):
def __init__(self, origin, direction):
self.origin = numpy.array(origin)
self.direction = numpy.array(direction)
Kula jest nieco bardziej skomplikowane i wykonuje logikę dla przebojowej/nohit:
class Sphere(object):
def __init__(self, center, radius, color):
self.center = numpy.array(center)
self.radius = numpy.array(radius)
self.color = color
@profile
def hit(self, ray):
temp = ray.origin - self.center
a = numpy.dot(ray.direction, ray.direction)
b = 2.0 * numpy.dot(temp, ray.direction)
c = numpy.dot(temp, temp) - self.radius * self.radius
disc = b * b - 4.0 * a * c
if (disc < 0.0):
return None
else:
e = math.sqrt(disc)
denom = 2.0 * a
t = (-b - e)/denom
if (t > 1.0e-7):
normal = (temp + t * ray.direction)/self.radius
hit_point = ray.origin + t * ray.direction
return ShadeRecord.ShadeRecord(normal=normal,
hit_point=hit_point,
parameter=t,
color=self.color)
t = (-b + e)/denom
if (t > 1.0e-7):
normal = (temp + t * ray.direction)/self.radius hit_point = ray.origin + t * ray.direction
return ShadeRecord.ShadeRecord(normal=normal,
hit_point=hit_point,
parameter=t,
color=self.color)
return None
Teraz wpadłem trochę profilowanie i wydaje się, że najdłuższa przetwarzanie czas jest w funkcji hit()
ncalls tottime percall cumtime percall filename:lineno(function)
2560000 118.831 0.000 152.701 0.000 raytrace/objects/Sphere.py:12(hit)
1960020 42.989 0.000 42.989 0.000 {numpy.core.multiarray.array}
1 34.566 34.566 285.829 285.829 raytrace/World.py:25(render)
7680000 33.796 0.000 33.796 0.000 {numpy.core._dotblas.dot}
2560000 11.124 0.000 163.825 0.000 raytrace/World.py:63(f)
640000 10.132 0.000 189.411 0.000 raytrace/World.py:62(hit_bare_bones_object)
640023 6.556 0.000 170.388 0.000 {map}
To mnie nie zaskakuje, a ja chcę maksymalnie zmniejszyć tę wartość. Mijam do linii profilowania, a wynik jest
Line # Hits Time Per Hit % Time Line Contents
==============================================================
12 @profile
13 def hit(self, ray):
14 2560000 27956358 10.9 19.2 temp = ray.origin - self.center
15 2560000 17944912 7.0 12.3 a = numpy.dot(ray.direction, ray.direction)
16 2560000 24132737 9.4 16.5 b = 2.0 * numpy.dot(temp, ray.direction)
17 2560000 37113811 14.5 25.4 c = numpy.dot(temp, temp) - self.radius * self.radius
18 2560000 20808930 8.1 14.3 disc = b * b - 4.0 * a * c
19
20 2560000 10963318 4.3 7.5 if (disc < 0.0):
21 2539908 5403624 2.1 3.7 return None
22 else:
23 20092 75076 3.7 0.1 e = math.sqrt(disc)
24 20092 104950 5.2 0.1 denom = 2.0 * a
25 20092 115956 5.8 0.1 t = (-b - e)/denom
26 20092 83382 4.2 0.1 if (t > 1.0e-7):
27 20092 525272 26.1 0.4 normal = (temp + t * ray.direction)/self.radius
28 20092 333879 16.6 0.2 hit_point = ray.origin + t * ray.direction
29 20092 299494 14.9 0.2 return ShadeRecord.ShadeRecord(normal=normal, hit_point=hit_point, parameter=t, color=self.color)
Więc wydaje się, że większość czasu spędza się w tym fragmencie kodu:
temp = ray.origin - self.center
a = numpy.dot(ray.direction, ray.direction)
b = 2.0 * numpy.dot(temp, ray.direction)
c = numpy.dot(temp, temp) - self.radius * self.radius
disc = b * b - 4.0 * a * c
Gdzie tak naprawdę nie zobaczyć wiele optymalizować. Czy masz pojęcie, jak uczynić ten kod bardziej wydajnym bez przechodzenia w C?
+1 Bardzo dobrze się prezentuje. Nie znam Pythona, więc pytanie: czy numpy.dot wywołuje implementację C? Jeśli nie, być może można poprawić prędkość, wykonując ręczne obliczenia produktu dot. – Phrogz
Tak, numpy jest zaimplementowany w C. Dlatego mam wrażenie, że nie ma wiele do zyskania, aby ponownie zaimplementować funkcję trafienia w C. –
są wektorami jednostek kierunku wektory jednostek? czy możesz uczynić je wektorami jednostkowymi w '__init__'? jeśli tak, twoja matematyka z produktu dot staje się prostsza. – underrun