Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Estructuras pequeñas en C++

Iniciado por Pogacha, 14 de Diciembre de 2007, 07:46:24 PM

« anterior - próximo »

Pogacha

Esta pregunta me da vueltas en la cabeza desde hace tiempo.

Supongamos que yo hago una estructura del tipo:

struct MyIntEspecial {
  int mValue;

 .. aqui defino operadores y otras funciones pero ninguna otra variable,
 .. todas inline y nada de polimorfismo ( rtti esta desactivado ).
};


Se dará cuenta el compilador que solo es un int que quiero tratar diferente?
Hará las optimizaciones necesarias, como por ejemplo alojarlo en un registro para calculos sucesivos o para pasarlo a una función y todas esas cosas?
He visto que los procesadores de hoy en dia trabajan con el stack en la l2 igual de rapido que con los registros.
Ya no deberia preocuparme entonces por esto?

Saludos y gracias

fjfnaranjo

Creo haber leido que una estructura formada por un solo campo int, no ocupa un solo int en la memoria, aunque hace mucho tiempo y no me acuerdo donde. No obstante es algo muy concreto, quizá podrías preguntarlo en los foros del compilador que estés utilizando...
fjfnaranjo.com - Creating entertainment - Creando entretenimiento
fjfnaranjo [4t] gm4il [d0t] c0m (mail y msn)

Tei

Lo unico garantizable es que sizeof (MyIntEspecial) == el tamaño de la estructura.  Que ese tamaño sea igual que su contenido, pues no creo que los compiladores lo garanticen. Yo no he escrito ningun compilador, pero si escribiera uno, no respeteria eso, aunque sea solo por joder. Por por ejemplo para alinear los datos a factores multiplos de dos, entonces tu estructura tiene un "hueco" sin usar. O cosas asi.  De todos modos quizas esto es raro, y todos los compiladores respeten el tamaño del contenido.

Pero que mas da.. primero, no bases tu codigo en estas especulaciones, que esto no esta en los specs (bueno, no lo se, pero no lo creo).
Segundo  sizeof( MyIntEspecial) == sizeof(int)  => este simple codigo te dira si es o no del mismo tamaño :D

Pogacha

Mas que nada no es por el tamaño, sino por la optimización que generan los compiladores. Al llamar a una función pasandolo por valor o como devolución de una función, se dará cuenta que es solo un registro? me lo pasará en eax? o hara toda la maraña? De que dependerá esto?

davur

No puedes garantizar que sizeof(MyIntEspecial) == sizeof(int).

El compilador es libre de insertar el padding que considere oportuno después de una variable miembro.

Aunque los compiladores definen extensiones específicas (no estándares) para personalizar las reglas de alineamiento (#pragma pack en el caso de VS).

swapd0

Cita de: "Pogacha"Mas que nada no es por el tamaño, sino por la optimización que generan los compiladores. Al llamar a una función pasandolo por valor o como devolución de una función, se dará cuenta que es solo un registro? me lo pasará en eax? o hara toda la maraña? De que dependerá esto?
No merece la pena calentarse la cabeza por eso, ¿que vas a ahorrar uno o dos ciclos de un procesador a 2Ghz? En la epoca de los 8 bits tenia sentido pero ahora...

Por lo general los compiladores si una funcion tiene pocos parametros, los copia a los registros, siempre que quepan dentro de un registro.

Pogacha

No son uno o dos ciclos ... de eso estoy hablando, necesita optimizarse ya que es para un inner loop.

swapd0

Cita de: "Pogacha"No son uno o dos ciclos ... de eso estoy hablando, necesita optimizarse ya que es para un inner loop.

Pues ponle "inline" delante de la declaracion, asi insertara el codigo y te ahorraras el salto y la vuelta de la rutina, que es mas costoso para los pipelines.

Loover

CitarNo puedes garantizar que sizeof(MyIntEspecial) == sizeof(int).

El compilador es libre de insertar el padding que considere oportuno después de una variable miembro.

Aunque los compiladores definen extensiones específicas (no estándares) para personalizar las reglas de alineamiento (#pragma pack en el caso de VS).

Es así como dice Davur.

Si quieres que tus estrucutras no presenten padding y ocupen lo mínimo posible puedes utilizar "union". Ahora bien, es más dificil acceder a ellas. Aquí tienes ejemplos en castellano: http://c.conclase.net/curso/index.php?cap=016

Y esto algunos ejemplos de un pdf de mis apuntes del año pasado:

● Syntactically similar to structures
● However, all member variables occupy the same location in
memory
● You are responsible for accessing the right members at the
right time
● Union size is size of the largest member

Ejemplo 1
union UBlah {
char x;
int y;
char z;
};


Ejemplo 2
union {
char x;
int y;
char* z;
} utype;

utype.x = 'c';
printf("%c\n", utype.x);
utype.z = "Hello";
printf("%s\n", utype.z);
printf("%d\n", utype.y); /* Undefined! */
IndieLib Libreria 2.5d utilizando aceleración por hardware para la programación de juegos 2d.
Indie Rover The monkeys are reading!

Pogacha

OK,
Voy a poner un ejemplo para que se entienda un poco mas.


typedef unsigned char byte;
typedef unsigned int dword;

// pragma empaquetar a 4 bytes ...
struct I32RGBAColor {
private:
  static inline int saturate(int a) { return a>255 ? 255: a<0?0:a; }
public:
union {
 struct {  byte r,g,b,a; };
 dword composed;
};

 I32RGBAColor(byte _r, byte _g, byte _b, byte _a) : r(_r),g(_g),b(_b),a(_a) { }

 I32RGBAColor operator+=(const I32RGBAColor& a, const I32RGBAColor &b) {
     return I32RGBAColor( sat(a.r+b.r), sat(a.g+b.g), sat(a.b+b.b), sat(a.a+b.a));
 }
};

void blend_add(I32RGBAColor* a, I32RGBAColor *b, I32RGBAColor* r, int l)
{
  while(l--) *r++= (*a++) + (*b++);
}


Que tan bien compilaria esto?

contra:

inline int saturate(int a) { return a>255 ? 255: a<0?0:a; }

void blend_add(int* a, int *b, int* r, int l)
{
  byte* u = reinterpret_cast<byte*> a;
  byte* v = reinterpret_cast<byte*> b;
  byte* w = reinterpret_cast<byte*> r;
  l*=4;
  while(l--) *w++ = saturate( static_cast<int>(*u++) + static_cast<int>(*v++) );
}


En realidad ambos codigos no tienen sentido, hay mejores formas de hacer lo mismo y no seria la mejor forma de organizar el codigo, pero para el ejemplo creo que sirven.

Tei


Loover

¿Porque no haces una prueba de rendimiento? Haz unos cuantos miles de operaciones con una y con otra y guarda los tiempos.
IndieLib Libreria 2.5d utilizando aceleración por hardware para la programación de juegos 2d.
Indie Rover The monkeys are reading!

Pogacha

¿Porque no haces una prueba de rendimiento?

Pues por lo que dije!
Casi que no tengo dudas de que esos dos codigos compilarian muy parecidos, pero el caso está en que puedo hacer una prueba y en ella todo va a andar bien, pero luego cuando arme toda la estructura del codigo con muchos templates y demas, habrá casos en que podría no compilar de la forma que quiero. No tengo garantias de que esa estructura sencilla se va a reconocer como un int especial nada mas en un codigo mas complejo donde llamadas a funciones no puedan hacerse inline y cosas así pero igual sea critico el factor de optimización.

Igual puedo buscar algunos workarounds como pasar la estructura como un int y luego volver a castearla y cosas así pero dentro de templates eso ya se complica ...

Igual gracias a todos, seguiré precindiendo de estas estructuras y buscaré otra forma de flexibilizar el codigo optimizado.

swapd0

A mi me parece mejor la version con I32RGBAColor, solo asegurate de sobrecargar el operator= y en el haces la copia usando el campo composed y no los campos r,g,b,a. Asi estaras copiando un entero.

Pablo Zurita

Cita de: "Pogacha"Se dará cuenta el compilador que solo es un int que quiero tratar diferente?
Hará las optimizaciones necesarias, como por ejemplo alojarlo en un registro para calculos sucesivos o para pasarlo a una función y todas esas cosas?
Depende del compilador, vas a tener que mirar el assembler generado por tu compilador para determinar eso.

Cita de: "Pogacha"He visto que los procesadores de hoy en dia trabajan con el stack en la l2 igual de rapido que con los registros.
Ya no deberia preocuparme entonces por esto?

Saludos y gracias
Hmm, no se a que te réferis con esto. Ningún tipo de arquitectura que yo conozca permite trabajar con el L2 directamente (por lo menos no es el caso en los x86, el Xenon o el Cell) y el costo de subir información del L2 al L1 depende de la arquitectura pero generalmente ronda los 40 ciclos. La realidad es que en general no te tendrías que preocupar por eso, en todo caso antes de preocuparte hace profiling y determina si realmente es un problema o no. Yo personalmente me canso de ver programadores hacerse problema por el assembler generado por el compilador cuando los problemas de performance que tienen 99% de las veces están relacionados con los algoritmos en sí.






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.