std::accumulate vs for loop, aplicación de raytracing
Esta pregunta se basa en este video en YouTube realizado con el propósito de revisar este proyecto.
En el video, el presentador está analizando el proyecto y descubrió que el siguiente bloque de código es la causa de problemas de rendimiento:
“`c++
std::optional<HitRecord> HittableObjectList::Hit(const Ray &r, float t<em>min, float t</em>max) const {
float closest<em>so</em>far = t_max;
<pre><code>return std::accumulate(begin(objects), end(objects), std::optional<HitRecord>{},
[&](const auto &temp_value, const auto &object) {
if(auto temp_hit = object -> Hit(r, t_min, closest_so_far); temp_hit) {
closest_so_far = temp_hit.value().t;
return temp_hit;
}
<pre><code> return temp_value;
</code></pre>
});
<pre><code><br />Yo asumiría que la función <code>std::accumulate</code> funcionaría de manera similar a un bucle <code>for</code>. Descontento con el impacto en el rendimiento allí (y porque, por alguna razón, el perfilador no perfilaba el código lambda [¿una limitación, tal vez?]), el revisor cambió el código a esto:
“`c++
std::optional<HitRecord> HittableObjectList::Hit(const Ray &r, float t<em>min, float t</em>max) const {
float closest<em>so</em>far = t_max;
std::optional<HitRecord> record{};
<pre><code>for(size_t i = 0; i < objects.size(); i++) {
const std::shared_ptr<HittableObject> &object = objects[i];
if(auto temp_hit = object -> Hit(r, t_min, closest_so_far); temp_hit) {
closest_so_far = temp_hit.value().t;
record = temp_hit;
}
}
return record;
Con este cambio, el tiempo de finalización pasó de 7 minutos y 30 segundos a 22 segundos.
Mis preguntas son:
– ¿Ambos bloques de código son idénticos? ¿Por qué std::accumulate
tiene una penalización tan enorme aquí?
– ¿Sería mejor el rendimiento si en lugar de usar auto
, se usara el tipo explícito?
El revisor mencionó sugerencias como evitar el uso de std::optional
y std:shared_ptr
aquí debido a la cantidad de llamadas realizadas y ejecutar este código en la GPU en su lugar, pero por ahora solo estoy interesado en los puntos mencionados anteriormente.
davy.ai
A pesar de que ambos bloques de código logran la misma funcionalidad, la implementación subyacente de
std::accumulate
parece incurrir en una penalidad significativa de rendimiento en este caso. Es posible que esto se deba a los gastos adicionales introducidos por la función lambda utilizada enstd::accumulate
, así como a la necesidad de construir objetosstd::optional<hitrecord>
en cada iteración.El uso de tipos explícitos en lugar de
auto
no necesariamente mejora el rendimiento, ya que el compilador generalmente es capaz de deducir el tipo correcto en tiempo de compilación. Sin embargo, puede mejorar la legibilidad del código y facilitar la comprensión de los tipos de datos subyacentes involucrados.