2009-08-20 18 views
5

Así que estoy optimizando algunos códigos desenrollando algunos loops (sí, sé que debo confiar en mi compilador para hacer esto por mí, pero no estoy trabajando con mi elección de compiladores) y Quería hacerlo con elegancia, de modo que, en caso de que el tamaño de mis datos cambie debido a algunas ediciones en el futuro, el código se degradará elegantemente.C sizeof equivalent for macros

Algo así como:

typedef struct { 
    uint32_t alpha; 
    uint32_t two; 
    uint32_t iii; 
} Entry; 

/*...*/ 

uint8_t * bytes = (uint8_t *) entry; 
#define PROCESS_ENTRY(i) bytes[i] ^= 1; /*...etc, etc, */ 
#if (sizeof(Entry) == 12) 
    PROCESS_ENTRY(0);PROCESS_ENTRY(1);PROCESS_ENTRY(2); 
    PROCESS_ENTRY(3);PROCESS_ENTRY(4);PROCESS_ENTRY(5); 
    PROCESS_ENTRY(6);PROCESS_ENTRY(7);PROCESS_ENTRY(8); 
    PROCESS_ENTRY(9);PROCESS_ENTRY(10);PROCESS_ENTRY(11); 
#else 
# warning Using non-optimized code 
    size_t i; 
    for (i = 0; i < sizeof(Entry); i++) 
    { 
     PROCESS_ENTRY(i); 
    } 
#endif 
#undef PROCESS_ENTRY 

Esto no funciona, por supuesto, ya sizeof no está disponible para el pre-procesador (al menos, eso es lo que parecía indicar this answer).

¿Existe una solución fácil que pueda usar para obtener sizeof una estructura de datos para usar con una macro C, o solo soy SOL?

+0

Bueno, sizeof() ** es ** una macro. Una macro incorporada, al menos. – Havenard

+5

sizeof no es una macro, en cualquier forma o forma –

+2

sizeof no es una macro, aunque offsetof es. sizeof es más un operador. –

Respuesta

17

No se puede hazlo en el preprocesador, pero no es necesario. Sólo generar una llanura if en su macro:

#define PROCESS_ENTRY(i) bytes[i] ^= 1; /*...etc, etc, */ 
if (sizeof(Entry) == 12) { 
    PROCESS_ENTRY(0);PROCESS_ENTRY(1);PROCESS_ENTRY(2); 
    PROCESS_ENTRY(3);PROCESS_ENTRY(4);PROCESS_ENTRY(5); 
    PROCESS_ENTRY(6);PROCESS_ENTRY(7);PROCESS_ENTRY(8); 
    PROCESS_ENTRY(9);PROCESS_ENTRY(10);PROCESS_ENTRY(11); 
} else { 
    size_t i; 
    for (i = 0; i < sizeof(Entry); i++) { 
     PROCESS_ENTRY(i); 
    } 
} 

sizeof es una expresión constante, y la comparación de una constante contra la constante también es constante. Cualquier compilador C sano optimizará la ramificación que siempre es falsa en tiempo de compilación. El plegado constante es una de las optimizaciones más básicas. Sin embargo, pierdes el #warning.

+0

Siempre es bueno ver el problema desde un ángulo ligeramente diferente. +1 y felicitaciones! – qrdl

9

Si usa autoconf u otro sistema de configuración de compilación, puede verificar el tamaño de las estructuras de datos en el momento de la configuración y escribir encabezados (como #define SIZEOF_Entry 12). Por supuesto, esto se vuelve más complicado cuando se compila de forma cruzada y eso, pero asumo que las arquitecturas de compilación y destino son las mismas.

De lo contrario sí, no tiene suerte.

-1

Si desea el tamaño más pequeño posible para la estructura (o para alinearlo con un límite de 4 bytes, o lo que sea), puede usar los atributos empaquetados o alineados.

En Visual C++, puede utilizar #pragma pack, y en GCC puede utilizar __attribute __ ((lleno)) y __ __attribute ((alineado (num-bytes))

+0

Eso no dará el tamaño. Además, mientras que el empaque ahorrará espacio, es probable que le cueste tiempo, y eso es lo que está tratando de ganar. –

+0

@David Thornley: No dará el tamaño, pero hará lo que * quiera * en este bloque de código. Al empaquetar la estructura, sabrá con certeza que el relleno no está en el camino, por lo que la estructura * es * exactamente 12 bytes (o la suma de los tamaños de cada elemento), y por lo tanto * no es necesario * utilizar una macro de preprocesador para encontrar el tamaño de la estructura PROCESS_ENTRY utiliza el acceso de nivel de bytes en una estructura sin relleno, por lo que empaquetar la estructura hace posible usar esta macro sin preocuparse por el relleno. –

+0

Debo añadir que generalmente es mejor no interferir de todos modos y dejar que el optimizador haga su trabajo (especialmente porque es mucho mejor que la mayoría de los programadores). –

5

Usted está de suerte -. Preprocesador ni siquiera sabe qué es una estructura, y mucho menos forma de calcular su tamaño.

En un caso como este, puede definir una constante a lo que sabe el tamaño de la estructura, entonces afirmar estáticamente que en realidad es igual al tamaño usando el truco de matriz de tamaño negativo.

También podría intentar simplemente haciendo if (sizeof(Entry) == 12), y vea si su compilador es capaz de evaluar la condición de la sucursal en tiempo de compilación y eliminar el código muerto. No es tan grande preguntar.

+0

+1 para ver si el optimizador optimiza la sugerencia. –

+0

La idea de usar un assert() en lugar de un #warning también es buena. ¿De verdad quieres una degradación elegante, o quieres estar claramente informado sobre el problema para que puedas arreglarlo? –

+0

Y hay dos razones por las que no puede ser 12. Una es que hay un relleno inesperado (en cuyo caso probablemente desee un error, por lo que puede usar pragmas específicos del compilador para empaquetar la estructura), y uno es que tiene agregó un campo (en cuyo caso desea actualizar #define para que represente el nuevo tamaño, y luego también decidir si desea actualizar el bucle desenrollado para manejar el nuevo tamaño). Entonces, en realidad, tendría la afirmación en el punto de definición de la estructura, y * también * la advertencia en el momento de desenrollar. –

1

Esto probablemente no va a ayudar, pero si usted tiene la capacidad de hacer esto en C++ se puede utilizar una plantilla para que el compilador para despachar al bucle apropiada en tiempo de compilación:

template <std::size_t SizeOfEntry> 
void process_entry_loop(...) 
{ 
    // ... the nonoptimized version of the loop 
} 

template <> 
void process_entry_loop<12>(...) 
{ 
    // ... the optimized version of the loop 
} 

// ... 

process_entry_loop<sizeof(Entry)>(...); 
+2

Buena idea, pero sería bastante más claro hacer sizeof (Entrada) el parámetro de la plantilla, y especialícese para 12. –

+0

+1 para la propina - ¡Me gusta eso! – fbrereto

1

Otros dos enfoques vienen a la mente: escriba una aplicación pequeña para escribir el ciclo desenrollado, o use una variación en Duff's device con el tamaño esperado de la estructura.

+1

Cualquier compilador moderno que genere código que sea más lento que el dispositivo de Duff no es un compilador muy bueno. –

+0

¿Eso no depende de cuán paranoico sea el tamaño del código? Los compiladores no multiplican necesariamente el tamaño de tu código por 12 solo por diversión. Sin la entrada del analizador, el optimizador no tiene forma de saber qué bucles vale la pena pagar el tamaño del código para ganar velocidad. Desenrollar todo da como resultado un código grande, muchos errores de icache y ralentizaciones. Usted, por otro lado, puede seleccionar inteligentemente qué bucles para desenrollar (o, aún más inteligentemente, usar un compilador que pueda optimizar el uso de datos del generador de perfiles). A menos que quiera decir que el Dispositivo de Duff ahora está obsoleto debido a trucos nuevos y mejores que yo no conozco. –