<p>I am currently writing a fairly basic pathtracer as a compute shader in C++/CUDA, which has been running on a GTX 1080, and I have noticed that when logging some statistics, I get around ~1 billion ray-entity collision checks per second, which translates into a few tens of millions of rays per second, depending on the scene. While changing the scene effects the number of rays produced, the number of ray-entity collision checks (including bvh node collision checks) remains fairly consistent around this ~1 Billion/s mark.</p>
<p>My concern is that a GTX 1080 is rated for ~8Tflops, and while I understand it's naive to neglect memory transfer costs in non-trivial access patterns, <em>8000</em> &quot;operations&quot; per, for the most part, AABB collision checks, feels rather excessive. Does anyone have any experience with optimising a pathtracer, or know what a good value for rays/checks per second might look like? I am more concerned about the collision checks per second, as it seems to reflect the effeciency of the code a lot more and I believe the rays per second has a lot more potential to be improved algorithmically. I would nevertheless appreciate any leads into optimisations I could look into making.</p>
<p>For reference, the main optimisations I have made thus far are</p>
<ul>
<li>Removing polymorphism from all objects, using an enum to represent object type and a switch statement to bind a collision detection function to an entity</li>
<li>Using a BVH, implemented similarly as in Peter Shirley's <a href="https://raytracing.github.io/books/RayTracingTheNextWeek.html" rel="nofollow noreferrer">Raytracing: The Next Week</a></li>
<li>Converting scene to a SoA, allocated via cudaMalloc. Though often times device side functions reconstruct structs within the object, which may be voiding some of the benefits it yields.</li>
<li>inlining where possible, device compiler optimisations -O2 and fast math, branchless code where possible, unrolled loops, passing by const reference or move, generally (what I like to believe) good C++ practice.</li>
<li>floating point precision (i.e no doubles)</li>
<li>Reduced quality to 1 reflection, and 1 sample per pixel to check for divergence. Regardless, still around ~1-2 Billion entity collision checks/sec when extrapolated from a tiny runtime</li>
</ul>

CC BY-SA 4.0
2024-01-06T16:05:27.593Z
The overall 8 teraflop rating is for full utilization. Path tracing can't take full advantage of all available compute power on the 1080 series of GPU's.
pmw1234
CC BY-SA 4.0
2024-01-07T14:18:04.477Z
@pmw1234 Just being curious: does that mean the latest series can be fully utilized by path tracing? Is that because of the RT-cores? Why can't path tracing take full advantage of all available compute power on the 1080 series of GPU's?
Enigmatisms
CC BY-SA 4.0
2024-01-07T14:39:57.507Z
I think you hit the nail on the head. The cores used for raytracing on  the 1080 are general purpose and software may be able to get every core working on the problem but that will leave other parts of the GPU idle, the overall number includes specialized hardware units and an idealized path that puts every part of the GPU to work, as you pointed out, without regard to data delays. RT cores have hardware dedicated (or at the very least "tuned" ) to intersection computations like ray triangle intersections. Also RT cores are "tuned" to work with CUDA cores.
pmw1234
CC BY-SA 4.0
2024-01-08T02:11:50.340Z
@pmw1234 Considering I am writing this in CUDA as a compute shader, is there any reason why "Pathtracing cant take full advantage", or is because of an algorithmic issue? I have written other shaders that have been much closer to full utilization, so thus far I simply dismissed pathtracing as requiring "too many non sequential memory accesses to ever be fast", but it would be nice to know if this is actually the case. I don't see why ray triangle intersection functions need to be any different (slower) to any other shader compute function.
Jack Freeth
<pathtracing><c++><optimisation><cuda>
What performance to expect from an optimised pathtracer?
Jack Freeth
# What performance to expect from an optimised pathtracer?

(Asked by: username_0 on 2024-01-05)

I am currently writing a fairly basic pathtracer as a compute shader in C++/CUDA, which has been running on a GTX 1080, and I have noticed that when logging some statistics, I get around ~1 billion ray-entity collision checks per second, which translates into a few tens of millions of rays per second, depending on the scene. While changing the scene effects the number of rays produced, the number of ray-entity collision checks (including bvh node collision checks) remains fairly consistent around this ~1 Billion/s mark.

My concern is that a GTX 1080 is rated for ~8Tflops, and while I understand it's naive to neglect memory transfer costs in non-trivial access patterns, *8000* "operations" per, for the most part, AABB collision checks, feels rather excessive. Does anyone have any experience with optimising a pathtracer, or know what a good value for rays/checks per second might look like? I am more concerned about the collision checks per second, as it seems to reflect the effeciency of the code a lot more and I believe the rays per second has a lot more potential to be improved algorithmically. I would nevertheless appreciate any leads into optimisations I could look into making.

For reference, the main optimisations I have made thus far are

* Removing polymorphism from all objects, using an enum to represent object type and a switch statement to bind a collision detection function to an entity
* Using a BVH, implemented similarly as in Peter Shirley's [Raytracing: The Next Week](https://raytracing.github.io/books/RayTracingTheNextWeek.html)
* Converting scene to a SoA, allocated via cudaMalloc. Though often times device side functions reconstruct structs within the object, which may be voiding some of the benefits it yields.
* inlining where possible, device compiler optimisations -O2 and fast math, branchless code where possible, unrolled loops, passing by const reference or move, generally (what I like to believe) good C++ practice.
* floating point precision (i.e no doubles)
* Reduced quality to 1 reflection, and 1 sample per pixel to check for divergence. Regardless, still around ~1-2 Billion entity collision checks/sec when extrapolated from a tiny runtime

**Tags:** pathtracing, c++, optimisation, cuda

> **Comments:**
> **username_1**: The overall 8 teraflop rating is for full utilization. Path tracing can't take full advantage of all available compute power on the 1080 series of GPU's.  
> **username_2**: @username_1 Just being curious: does that mean the latest series can be fully utilized by path tracing? Is that because of the RT-cores? Why can't path tracing take full advantage of all available compute power on the 1080 series of GPU's?  
> **username_1**: I think you hit the nail on the head. The cores used for raytracing on  the 1080 are general purpose and software may be able to get every core working on the problem but that will leave other parts of the GPU idle, the overall number includes specialized hardware units and an idealized path that puts every part of the GPU to work, as you pointed out, without regard to data delays. RT cores have hardware dedicated (or at the very least "tuned" ) to intersection computations like ray triangle intersections. Also RT cores are "tuned" to work with CUDA cores.  
> **username_0**: @username_1 Considering I am writing this in CUDA as a compute shader, is there any reason why "Pathtracing cant take full advantage", or is because of an algorithmic issue? I have written other shaders that have been much closer to full utilization, so thus far I simply dismissed pathtracing as requiring "too many non sequential memory accesses to ever be fast", but it would be nice to know if this is actually the case. I don't see why ray triangle intersection functions need to be any different (slower) to any other shader compute function.  

---

## 0 answers

