Yet another C/C++ flame

2 views
Skip to first unread message

Javier Loureiro Varela

unread,
Nov 11, 2010, 8:55:45 PM11/11/10
to Lista Codepixel
Hace tiempo (bastante) se discutió por aquí el tema de la programación de objetos, como lo veíamos, etc.

Bueno, el caso es que, hace un año o así, hice un pequeño motor de render usando micropolígonos, una implementación directa del paper original de REYES... el tema es que, tras malas experiencias con el rendimiento en otro código, decidí hacerlo en C. Realmente en C++, pero sin objetos, templates y demás temas. Pero quizás, un paso más allá. Lo orienté totalmente a los datos, más que a la arquitectura en sí.

El tema es que, al simplificar de tal modo la arquitectura, el código fue realmente sencillo, y en 2 semanas tenía todo funcionando, a un rendimiento más que aceptable. El código es robusto, todo está muy clarito, etc. Pero claro, la ventaja es que, realmente fue la implementación de un paper que explica la arquitectura con bastante detalle. De todos modos, me hizo pensar bastante, y sobre todo, me sentí muy cómodo con el resultado final.

Poco después, revisando código, me di cuenta que mi clase vector estaba haciendo montón de copias por el mundo adelante. Si, es verdad que es muy chachi  hacer cosas como "a= (k1*b)/(1-k1)*c" y que internamente todo funcione. Pero había muchos casos donde copiaba datos innecesariamente de un sitio a otro. Por ejemplo, muchas veces tenia simplemente un array de floats, y para operar, pues acababa creando una clase(una copia) , operando, y volvendo a copiar al destino (por ejemplo , cosas como p0(1.2.3) ; hit.p = t*p0; Ya sé, es posible hacer montón de optimizaciones, etc. Pero leyendo un articulo de joel on software, me planteé realmente lo que conseguimos con la abstracción. Y es verdad que muchas veces tenemos que saber "por arte de magia". Por ejemplo, saber que hay referencias por ahí, si la memoria está alineada, si se hacen bloqueos multithread, etc. Cosas que sabemos, pero no están explícitamente en el código. Y otra cosa. Las dependencias. Usar una clase vector en un include, nos mete una dependencia del API tremenda. Lo mismo con el resto de objetos y clases...

Así que me puse a reescribir mi proyecto de raytracer desde cero, con estos puntos en mente. Y creo que la conclusión es que, uno de los principales problemas para avanzar es precisamente intentar cerrarse a la arquitectura de clases. Clases, herencias, metoso virtuales, etc. No quiero decir con esto que sean malos, o que esté en contra de la POO. Pero creo que hay que plantearse muchas veces si realmente necesitamos definir un interfaz, si necesitamos heredar y crear una clase base, etc.

Por ejemplo, me gustan muchas cosas del diseño de la boost. Para lo que hace, temas realmente genéricos que tienen que funcionar siempre, en todos los casos, es ideal. Igual que la STL. Y las Qt, me encantan (un gran ejemplo de buena POO). Pero no creo que, al menos en mi caso, sea lo mejor. Y es que creo que, una solución simple es mucho más dificil de desarrollar, que un montón de clases que dan una falsa sensación de abstracción.

Uno de los mayores "éxitos" que tuve tras este tipo de programación fue que, pasar el código de intersección del BVH a la GPU me llevó 2 días. El código es tan tan simplificado, que poco más tuve que hacer que copiar el BVH a memoria de la GPU, copiar y pegar el codigo del traverse, y un par de optimizaciones. Antes hubiese tenido unas cuantas clases de por medio, que permitiesen abstraer el BVH por si un dia quiero meter elefantes o grapadoras, a parte de triángulos, que incluyesen varios templates por si se abstrae el concepto del espacio/tiempo, que quizás querramos extender. Pero que costaría mucho meter en una GPU, o en cualquier proyecto externo (lo cual es una enorme contradicción con la supuesta abstracción)... El raytracer ahora corre a una velocidad muy buena, con unos tiempos muy competitivos, que al menos para mí serían impensables hace un año.

Esto claramente es fácil de rebatir. Y no es más que una opinión. Es fácil argumentar que si quiero meter otra estructura de aceleración, tengo que cambiar montón de cosas. Es cierto. Es fácil argumentar que si quiero externder el raytracer, tengo que meter montón de cambios, y es cierto. En algún punto la cosa se desmadra. Ahí está la mano del artista :)

Personalmente, para evitar en lo posible esto, tengo todo dividido en "librerías estanco". Una ventaja de no usar clases, es que el API se simplifica muchísimo, lo que reduce dependencias. Así, al menos es más fácil acometer reescrituras sin tocar otras partes del sistema de forma sencilla. Mi experiencia es que eso es cierto y es lo que ocurre la mayoría de las veces. Sumado a un mínimo de unit testing, el resultado en general me gusta mucho.

Tambíen hay un punto importante que me gustaría destacar. Cuando comenzamos a programar algo desconocido, no sabemos a lo que nos enfrentamos. Y la abstracción nos ofrece una cierta protección ante eso. Pero si conocemos lo que vamos a programar, y sabemos limitar las opciones, lo que solemos necesitar es rendimiento, y menos abstracción.

Bueno, solo un par de ideas para la reflexión. Como digo, hay muchisimos casos donde el c++ tiene mucho sentido. Pero tampoco está de más plantearse, muchas veces, si realmente es necesario montar un tinglado para afrontar el problema.

Sé que en la lista hay gente que programa el caso contrario, llevando la programación al otro extremo. Qué tal la experiencia?

--
--
    Signed,
        Javier Loureiro Varela

Manuel Padron Martinez

unread,
Nov 12, 2010, 1:38:39 AM11/12/10
to code...@googlegroups.com
Como tu bien dices, todo depende de para que. Si busco rendimiento tengo claro que mientras mas me acerque a la maquina mejor. 

Entre C y C++ el ejplo mas claro es la gestión de memoria. hacer un new y un delete es mas lento que un malloc y un free. 

Por otro lado, para aplicaciones que no requieren rendimiento, por ejemplo cualquier aplicación de gestión, personalmente prefiero no pensar tanto en la maquina sino mas en el problema. A día de hoy ya no me veo programando algo de gestión en algo que no sea ruby ( que es lentisimo pero el nivel de abstracción esta por las nubes)

Pues eso, my 5 cents

Saludos desde las islas
--
Has recibido este mensaje porque estás suscrito a code...@googlegroups.com
Incluye tu información de perfil en http://groups.google.es/group/codepixel/web/lista-de-miembros
Para obtener más opciones, visita este grupo en http://groups.google.es/group/codepixel?hl=es.
 
http://www.codepixel.com

ArKangeL ArK

unread,
Nov 12, 2010, 3:50:11 AM11/12/10
to code...@googlegroups.com
Hola

Desde mi humilde opinión, creo que muchos de los problemas que tenemos tendrían que resolverlo los compiladores o frameworks en los que trabajemos. Aunque todo avance me doy cuenta de que siempre somos los programadores los que acabamos sirviendo al hardware y no al revés. Ahora mismo, para usar una aceleración GPU, o te adaptas a la programación vectorial o no te comes un torrao, y yo creo que eso no tendría que ser así.

Un programador tendría que ser libre de programar correctamente en POO y al mismo tiempo el hardware tendría que ejecutar perfectamente ese programa con el máximo de rendimiento, pero claro, te dirán que se usa menos silicio para hacer un procesador vectorial que uno para que el programador este cómodo.

Un saludo

LLORENS

Diego Fdez Goberna

unread,
Nov 12, 2010, 4:18:40 AM11/12/10
to code...@googlegroups.com
De acuerdo, pero creo que javier va mas allá de no usar POO para ganar
en rendimiento, sino también para ganar en simplicidad de código, y
simplificación de tu propias librerías y apis.. Lo que es
contradictorio pues para eso se inventó la POO, pero en muchos casos
puede jugar en nuestra contra.. Yo opino igual, y todo depende del
caso, claro.

Saludos!

Jon Valdés

unread,
Nov 12, 2010, 4:46:19 AM11/12/10
to code...@googlegroups.com
La abstracción siempre es enemiga de la concreción. Si necesitas hacer
las cosas exactamente de la manera que tú quieres para que el
rendimiento sea el mejor, no vas a poder abstraerte mucho. Y lo que es
más, vas a tener que tener en cuenta en todo momento la arquitectura,
si puedes aprovecharte de instrucciones SSE, si usar "array of
structures" o "structure of arrays", etcetera

Y ahi es donde entra el compilador. Muchas veces un compilador puede
ayudarte a mejorar tu código, pero cuanto más trabajo tenga que hacer
para transformar tu código en algo ejecutable, más probabilidades hay
de que introduzca ineficiencias (funciones virtuales, copia de
objetos, inicializaciones innecesarias, branches, distribución en
memoria poco eficiente, etc).

Ah, relacionado 100% con lo que dice Javier, artículo interesante:
http://gamesfromwithin.com/data-oriented-design

Había por ahi una presentación que aplicaba estos conceptos para
acelerar precisamente una rutina de intersección rayo-triángulo, pero
ahora mismo no la encuentro (una que estaba hecha con post-its
¿alguien la recuerda? ).


Un saludoo


2010/11/12 Javier Loureiro Varela <dere...@gmail.com>:

Jon Valdés

unread,
Nov 12, 2010, 4:52:55 AM11/12/10
to code...@googlegroups.com
2010/11/12 ArKangeL ArK <arka...@gmail.com>:

> Ahora mismo, para usar una aceleración GPU, o te adaptas a
> la programación vectorial o no te comes un torrao, y yo creo que eso
> no tendría que ser así.

Ehhm... Las GPU son casi 100% procesadores vectoriales. ¿Cómo esperas
que tu código sea eficiente si no lo programas de forma vectorial?

> Un programador tendría que ser libre de programar correctamente en POO y al
> mismo tiempo el hardware tendría que ejecutar perfectamente ese programa con
> el máximo de rendimiento, pero claro, te dirán que se usa menos silicio para
> hacer un procesador vectorial que uno para que el programador este cómodo.

No, el problema es más bien que no hay varitas mágicas. ¿Quieres un
procesador no vectorial que vaya a toda leche? Ok, pero prepárate para
necesitar un sistema de refrigeración como el de una central nuclear.
Aparte de que consumirá una burrada de electricidad, claro.

Y en cuanto a POO, tampoco hay varitas mágicas. Si defines tus clases
de la zona de rendimiento crítico con 5 niveles de herencia con
funciones virtuales, las vtables que se generen van ser telita de la
buena. Buena suerte sacando buen rendimiento de ahí cuando el
procesador tenga que navegar 5 niveles de punteros para encontrar la
función a llamar (y tener un porrón de cache misses en el intento).

Un saludoo

Pablo Quesada Barriuso

unread,
Nov 12, 2010, 5:07:45 AM11/12/10
to code...@googlegroups.com
Estoy deacuerdo con Manuel,

a mi no se me ocurriría hacer una aplicación de gestión pensando en
sacarle el máximo rendimiento a la máquina, porque cuando la hayas
entregado al cliente y antes de que te de las gracias, es posible que
te pida un 'special button by the face' que te joderá bastante montar
según como hayas planteado el problema desde el principio.

Otro factor que creo que influye es cuanto puedes producir una vez
tengas el 'core' de tu aplicación. Me refiero en cuanto tiempo puedes
producir más funcionalidades. Otra cuestión es el tiempo de adaptación
de nuevos compañeros en el team, aunque esto esta más relacionado con
lo bien que hayas organizado el código y documentado, no ?

Well, my 2 cents :))

2010/11/12 Manuel Padron Martinez <mano...@gmail.com>:

--
Pablo Quesada Barriuso.
email: pab...@gmail.com
blog: http://pabloqb.evolution42.com

Ruben Penalva

unread,
Nov 12, 2010, 6:12:42 AM11/12/10
to code...@googlegroups.com
Cuando he visto el link al articulo de Llopis que ha enviado Jon, me he acordado de esto:
http://gamesfromwithin.com/the-always-evolving-coding-style

My 2 cents! :D

iSTO

unread,
Nov 12, 2010, 8:21:07 PM11/12/10
to codepixel
Personalmente creo que puedes lograr una gestión al extremo con C++,
todo depende de como plantees la estructura del programa; quizás
cuando nos ponemos en modo POO también usamos una serie de vicios que
hacen perder rendimiento.

No sé, mi experiencia me dice que los cuellos de botella no sólo están
en un new vs malloc :D

Y esto lo dice alguien que tiene una neurona cruzada y prefiere C más
que de C++ y que una vez termina la primera ronda de implementación
pasa a la de asm { ... }.

Mis motivos para preferir C normalmente tienen que ver conque
normalmente me es más fácil portar y exportar funcionalidad.

JMCL

unread,
Nov 13, 2010, 3:10:11 AM11/13/10
to codepixel
Buenos días a todos...

Hablo desde mi experiencia particular en esto del desarrollo de
soft. ...

Creo, Javier ( es mi opinión particular) que lo que comentas destila
la esencia de la programacion objetos vs estructurada:

En programas que reúnen uno o varios de estos puntos:

1.-"relativamente" cortos
2.- en los que están implicadas pocas personas
3.- la eficiencia es una necesidad

Una programacion ad-hoc estructurada sin parafernalias de estructuras
complejas ( abstracion , polimorfismo, etc...) y totalmente orientada
al dato
permite rendimientos muy altos en los tiempos de desarrollo y en los
de ejecución del programa ...

pero...

Sin embargo ( insisto , es mi opinion particular) cuando tienes que
hacer un proyecto muy complejo, que implica a muchas personas y con
cientos de miles de lineas si
no introduces abstraccion de datos creo que este tipo de programacion
deja de ser eficiente...

y es más, llega a convertirse en una autentica pesadilla puesto que
hace que TODAS las personas del equipo que necesiten acceder cada uno
a ciertos datos concreto tienen que ser
conscientes de todas la estructura de datos y se vuelva loca en la
gestión de todos esos tipos de datos .. disparándose los tiempos de
modificación...y pisándose entre ellas las modificaciones en paralelo
del mismo programa...

Mi experiencia profesional ( y particular) es que

1.- los proyectos muy orientados a la eficiencia y al dato no permiten
crecimientos en funcionalidades puesto que para incluir nuevas
funcionalidades requieren modificación de todo un programa muy tuneado
para conseguir esa eficiencia y requieren tiempos de desarrollo muy
largos para volver a poner a punto
Son super-eficientes si sólo dependen de una sola persona ( y es un
tio bueno claro) puesto que tienes tu estructura de datos en la cabeza
y no tienes que preguntar a nadie..
tu te lo guisas y tu te lo comes... y vas al grano

2.- Los proyectos muy grandes con amplia abstracción de datos y
"convenientemente" modularizados a la hora del diseño teniendo en
mente a donde quieres ir permiten una
gran flexibilidad , reusabilidad por personas que no tienen porque
ser expertas y permiten compartimentar el código y por tanto repartir
tareas...
No quiere decir esto que por tener objetos , abstracción de datos y
polimorfismo tengas un buen programa ... ( de hecho muchas veces
ocurre lo contrario) sino que si usas estas
tecnologías de modo inteligente puedes tener un muy buen soft.

Esta claro que los tipo 1 SIEMPRE podrán ser más eficientes que los
del punto 2 considerando eficiencia como velocidad de ejecución e
incluso considerándola como tiempos de desarrollo con poca gente y
"relativa" complejidad ...

mientras que los del tipo 2 pueden ser mas eficientes ( siempre claro
que no los haya pensado un membrillo ;-) ) en tiempos de desarrollo
con mucha gente y con cierto análisis de "profiling inteligente" se
pueden sacar mejorar rendimientos de manera a veces sorprendente...

Supongo de todas formas que cada uno tiene sus propias experiencias
que pueden o no coincidir con esto, claro esta...


Un saludo a todos

PD: Entiendo tu problema con las GPUs, ;-) , pero creo que
precisamente el "porting" a éstas te obliga ya por naturaleza a
trabajar orientado al dato por los problemas de
rendimiento en los empaquetamientos , y la bajada de "perfomances" si
no gestionas bien el dato... vamos que no te puedes "abstraer" del
propio dato..;-)

Por curiosidad .. ¿usas OpenCL o CUDA? ... si es OpenCL ... sobre
NVIDIA o ATI... te tiran los compiladores igual? ... es que parece que
hay algun problema de compatibilidad entre el código OpenCL para
NVIDIA frente al compilador de ATI ...


PD: Da gusto ver que hay gente inquieta por este país .. que siga
asi !!

Manolo Padron Martinez

unread,
Nov 13, 2010, 4:32:12 AM11/13/10
to code...@googlegroups.com
> 1.- los proyectos muy orientados a la eficiencia y al dato no permiten
> crecimientos en funcionalidades puesto que para incluir nuevas
> funcionalidades requieren modificación de todo un programa muy tuneado
> para conseguir esa eficiencia y requieren tiempos de desarrollo muy
> largos para volver a poner a punto
>  Son super-eficientes si sólo dependen de una sola persona ( y es un
> tio bueno claro) puesto que tienes tu estructura de datos en la cabeza
> y no tienes que preguntar a nadie..
> tu te lo guisas y tu te lo comes... y vas al grano

Mmmm o no... vease el nucleo de linux (vale, si, es un caso muy
especifico) pero se programa en C a pelo y con un montón de
desarrolladores en ciclos medianamente cortos (para las
funcionalidades nuevas que se meten).

Creo (y corrijanme si me equivoco) que Javi se refería a programar en
C pero no a no programar con structs y modularización (vale a nivel de
ficheros y funciones pero modularización al fin y al cabo). No tiene
por que estár todo en la cabeza del tio (que este es un mal tio por
contraste con el otro :D)

> 2.- Los proyectos muy grandes con amplia abstracción de datos y
> "convenientemente" modularizados a la hora del diseño teniendo en
> mente a donde quieres ir permiten una
>  gran flexibilidad , reusabilidad por personas que no tienen porque
> ser expertas y permiten compartimentar el código y por tanto repartir
> tareas...
>   No quiere decir esto que por tener objetos , abstracción de datos y
> polimorfismo tengas un buen programa ... ( de hecho muchas veces
> ocurre lo contrario) sino que si usas estas
>  tecnologías de modo inteligente puedes tener un muy buen soft.

Bueno, aqui también va a gustos personales, pero por ejemplo un soft
en java, hiper modularizado, con mil patrones y demás a mi me parece
una pesadilla (precisamente por exceso de modularización)

alg...@gmail.com

unread,
Nov 13, 2010, 5:02:01 AM11/13/10
to code...@googlegroups.com
Totalmente de acuerdo con JM.
En resumen, la OOP surgió para resolver problemas que Javier no tiene. Es un código suyo, relativamente pequeño para él, mantenido exclusivamente por él, Javier conocía la problemática de forma experta antes de empezar si quiera a programar siendo tal el grado de conocimiento adquirido que el diseño está en su cabeza y no hay necesidad de explicitarlo/aclararlo antes de pasar a desarrollo. Añadimos eficiencia como factor clave y tenemos la imagen global de por qué para este caso le va mejor así.

Manolo, respecto al núcleo de linux yo creo que como tu bien dices se trata de un caso particular en el que la eficiencia prima claramente sobre el diseño, descartando por eso la OOP entre otros. Exactamente como tu ejemplo de usar OOP para una aplicación de gestión.

Saludos.

2010/11/13 Manolo Padron Martinez <mano...@gmail.com>

JMCL

unread,
Nov 14, 2010, 3:02:08 AM11/14/10
to codepixel
Jeje, Hola Manolo, por alusiones.....;-)

... Un poco de mea culpa porque cuando uno habla tiene tendencia a
generalizar...Mi expresion "super-eficientes si sólo dependen de una
sola persona" suena un poco "absoluta" , la cambio por "... en
general... "... Tu contraejemplo del kernel me vale ... pero estaremos
de acuerdo ( nunca me he metido en sus tripas, como mucho recompilarlo
con alguna opcion) que es un entorno altamente especializado ("buenos"
desarrolladores)...

...yo mas que nada lo he entendido como OOP frente a programacion
estructurada ...

Perdon de antemano porque de Java ni idea , pero en lo que conozco es
relaticamente parecido al C++ ( que lo conozco "bastante bien")
Mi opinion es que "en el justo medio" se encuentra la virtud..ni mucho
ni poco ...
... he vivido "spagueti code" en una sola rutina de 20000 lineas como
programas con "cientos" de clases a tutti-plen imposible en ambos
casos de hacerse una idea clara y coherente de que queria el
"escritor" del programa

Creo que en el fondo mas o menos hablamos de los mismo.

Un saludo
Reply all
Reply to author
Forward
0 new messages