2009-10-06 17 views
19

¿Hay alguna forma portátil para determinar cuál es la alineación máxima posible para tipo?Determinación de la alineación máxima posible en C++

Por ejemplo en x86, las instrucciones SSE requieren alineación de 16 bytes, pero hasta donde yo sé, ninguna instrucción requiere más que eso, por lo que cualquier tipo se puede almacenar de forma segura en un búfer alineado de 16 bytes.

Necesito crear un búfer (como una matriz de caracteres) donde pueda escribir objetos de tipos arbitrarios, por lo que necesito poder confiar en que el principio del búfer se alineará.

Si todo lo demás falla, sé que la asignación de una matriz de caracteres con new está garantizado para tener la máxima alineación, pero con la TR1/C++ 0x plantillas y alignment_ofaligned_storage, me pregunto si sería posible crear el búfer in situ en mi clase de búfer, en lugar de requerir la indirección de puntero adicional de una matriz asignada dinámicamente.

Ideas?

Me doy cuenta de que hay muchas opciones para determinar la alineación máxima para un conjunto limitado de tipos: una unión, o simplemente alignment_of de TR1, pero mi problema es que el conjunto de tipos no está limitado. No sé de antemano qué objetos deben almacenarse en el búfer.

+0

portátil en lo que respecta, exactamente? para cada compilador? para cada sistema operativo? para cada arquitectura? –

+0

Simplemente portátil como en "garantizado por el estándar C++ para trabajar". Por supuesto, podría confiar fácilmente en mi propio conocimiento de la arquitectura objetivo y codificar la alineación máxima, pero sería bueno que el lenguaje en sí proporcionara las herramientas para responder a esto. – jalf

+3

Tenga en cuenta que el parámetro de plantilla 'Align' de' std :: aligned_storage 'tiene un argumento predeterminado de" default-alignment ", que se define como" El valor de default-alignment debe ser el requisito de alineación más estricto para cualquier objeto C++ tipo cuyo tamaño no es mayor que 'Len'." No sé si los tipos SSE se consideran "tipos de objetos C++" y la Biblioteca estándar VC10 no tiene el argumento predeterminado, por lo que no sé cuál es el valor previsto (no tengo ninguna otra biblioteca estándar) implementaciones en esta máquina). –

Respuesta

9

En C++ 0x, el parámetro Align plantilla de std::aligned_storage<Len, Align> tiene un argumento predeterminado de "default-alineación", que se define como (N3225 §20.7.6.6 Tabla 56):

El valor de la alineación por defecto será el requisito de alineación más estricto para cualquier tipo de objeto C++ cuyo tamaño no sea mayor que Len.

No está claro si los tipos SSE se considerarían "tipos de objeto C++".

El argumento predeterminado no era parte del TR1 aligned_storage; fue agregado para C++ 0x.

5

Corto de algunos maximally_aligned_t tipo que todos los compiladores prometieron fielmente para soportar todas las arquitecturas en todas partes, no veo cómo esto podría resolverse en tiempo de compilación. Como dices, el conjunto de tipos de potencial no tiene límites. ¿Es la indirección del puntero extra realmente tan importante?

+0

Puede que no lo sea, pero tengo curiosidad si hay una solución. C++ 0x agrega un par de otras funciones relacionadas con la alineación, y la implementación ya tiene que determinar la alineación máxima posible en otros casos (cuando se asigna dinámicamente una matriz de caracteres) por lo que pensé que podría haber alguna plantilla de biblioteca estándar oscura que expone esto valor. – jalf

+0

Sí. Es una pregunta interesante, y desearía tener una mejor respuesta para usted, pero no creo que haya ninguna forma conforme a las normas. maximally_aligned_t (o mejor, maximal_alignment) no sería difícil de implementar, sin embargo; quizás deberías proponerlo para C++ 1x :) –

1

la asignación de memoria alineada es más complicado de lo que parece - véase, por ejemplo Implementation of aligned memory allocation

+0

Sé que es complicado. Esa no era mi pregunta. ;) Pero la norma proporciona algunas garantías, y especialmente cuando se tiene en cuenta C++ 0x, se necesitan un par de herramientas * estándar * para ayudar. – jalf

+1

El truco no se aplica a Jalf porque no está haciendo un asignador general. Todo lo que necesita es tener espacio adicional en su memoria intermedia, y redondear el puntero dentro del buffer al siguiente bloque de alineación deseado. – Potatoswatter

5

asegurar la alineación Desafortunadamente máximo es mucho más difícil de lo que debería ser, y no existen soluciones garantizadas yo sepa. Desde el blog de GotW (Fast Pimpl article):

union max_align { 
    short  dummy0; 
    long  dummy1; 
    double  dummy2; 
    long double dummy3; 
    void*  dummy4; 
    /*...and pointers to functions, pointers to 
     member functions, pointers to member data, 
     pointers to classes, eye of newt, ...*/ 
}; 

union { 
    max_align m; 
    char x_[sizeofx]; 
}; 

Esto no se garantiza que sea totalmente portátil, pero en la práctica es lo suficientemente estrecha porque hay pocos o ningún sistemas en los que esto no funcionará como esperado.

Ese es el "hack" más cercano que conozco para esto.

Hay otro enfoque que he usado personalmente para la asignación súper rápida. Tenga en cuenta que es malo, pero trabajo en campos de trazado de rayos donde la velocidad es una de las mejores medidas de calidad y el código del perfil se realiza a diario. Implica utilizar un asignador de montón con memoria preasignada que funciona como la pila local (simplemente incrementa un puntero en la asignación y disminuye uno en la desasignación).

Lo uso especialmente para Pimpls. Sin embargo, solo tener el asignador no es suficiente; Para que funcione tal asignador, debemos suponer que la memoria para una clase, Foo, se asigna en un constructor, la misma memoria también se desasigna solo en el destructor, y que Foo mismo se crea en la pila. Para que sea seguro, necesitaba una función para ver si el puntero 'this' de una clase está en la pila local para determinar si podemos usar nuestro alocator de pila súper rápido basado en heap.Para eso tuvimos que buscar soluciones específicas del sistema operativo: utilicé TIBs y TEBs para Win32/Win64, y mis compañeros de trabajo encontraron soluciones para Linux y Mac OS X.

El resultado, después de una semana de investigación de sistemas operativos específicos métodos para detectar rango de pila, requisitos de alineación y hacer muchas pruebas y perfiles, era un asignador que podía asignar memoria en 4 ciclos de reloj de acuerdo con nuestros puntos de referencia de contador de ticks en comparación con aproximadamente 400 ciclos para malloc/operator new (nuestra prueba involucrada contención de hilo así que malloc es probable que sea un poco más rápido que esto en casos de subproceso único, quizás un par de cientos de ciclos). Agregamos un montón de subprocesos por subproceso y detectamos qué subproceso se estaba utilizando, lo que aumentó el tiempo a aproximadamente 12 ciclos, aunque el cliente puede realizar un seguimiento del asignador de subprocesos para obtener las asignaciones de 4 ciclos. Eliminó del mapa los puntos de acceso basados ​​en asignación de memoria.

Si bien no tiene que pasar por todos esos problemas, escribir un asignador rápido podría ser más fácil y más aplicable (por ejemplo, permitir que la cantidad de memoria asignar/desasignar se determine en tiempo de ejecución) que algo como max_align aquí. max_align es bastante fácil de usar, pero si busca velocidad para asignaciones de memoria (y suponiendo que ya ha perfilado su código y encontrado hotspots en malloc/free/operator new/delete con los principales contribuyentes en el código que tiene control) , escribir tu propio asignador realmente puede hacer la diferencia.

+0

+1. Vaya, asignación 100 veces más rápida. Gracias por compartir esta información. –

+1

Hmm, eso es muy interesante. Pero realmente no estaba preguntando acerca de la asignación rápida (en mi caso, ese es en realidad un problema bastante fácil de resolver, porque no tengo que manejar los desagradables casos generales como lo hizo - y debo decir, estoy impresionado lo tienes funcionando). Pero mi pregunta era simplemente asegurar que los objetos se asignan a direcciones alineadas correctamente. – jalf

+0

@jalf Ah, disculpas, por lo general, creo que cuando uno comienza a entrar en problemas de alineación y almacena varios tipos de datos en un único búfer, a menudo tiene en cuenta un asignador de memoria y el rendimiento. Me temo que no conozco ninguna forma portátil para garantizar la alineación máxima para un tipo determinado. Por lo general, tuve que ser bastante específico de la plataforma en tales casos. Para tratar de ponerlo a salvo, a menudo he usado enfoques de opt-in (un tipo cuya alineación es desconocida se alineará en los límites de cuádruplas palabras, la alineación máxima posible de la que soy consciente). – stinky472

-2

Esto es lo que estoy usando. Además de esto, si está asignando memoria, entonces una nueva matriz de caracteres con una longitud mayor o igual a max_alignment se alineará con max_alignment para que pueda usar índices en esa matriz para obtener direcciones alineadas.

enum { 
      max_alignment = boost::mpl::deref< 
       boost::mpl::max_element< 
         boost::mpl::vector< 
          boost::mpl::int_<boost::alignment_of<signed char>::value>::type, 
          boost::mpl::int_<boost::alignment_of<short int>::value>::type, 
          boost::mpl::int_<boost::alignment_of<int>::value>::type,        boost::mpl::int_<boost::alignment_of<long int>::value>::type, 
          boost::mpl::int_<boost::alignment_of<float>::value>::type, 
          boost::mpl::int_<boost::alignment_of<double>::value>::type, 
          boost::mpl::int_<boost::alignment_of<long double>::value>::type, 
          boost::mpl::int_<boost::alignment_of<void*>::value>::type 
         >::type 
        >::type 
       >::type::value 
      }; 
     } 
+0

Desafortunadamente, esto no garantiza una alineación adecuada para los tipos de SSE. – jalf

9

En C++ 11 std :: max_align_t definido en cstddef cabecera es un tipo POD cuya alineación requisito es al menos tan estrictas (tan grande) que la de cada tipo escalar.

Usando el nuevo operador alignof sería tan simple como alignof(std::max_align_t)

+0

para compiladores que no admiten alignof (es decir, MSVC11), puede usar std :: alignment_of :: value –

+0

Esto funciona bien hasta que haya [un error en la biblioteca estándar] (https: // github .com/cameron314/concurrentqueue/issues/64): - / – Cameron

Cuestiones relacionadas