sistemas significado recursos que optimizar optimizando optimizacion definicion celular aplicaciones c++ visual-c++ optimization benchmarking

c++ - significado - que es optimizar recursos



Barrera de optimización para marcas de microbios en MSVC: ¿le dice al optimizador que tiene memoria? (2)

Dada su aproximación de escape() , también debería estar bien con la siguiente aproximación de clobber() (tenga en cuenta que esta es una idea preliminar, aplazando parte de la solución a la implementación de la función nextLocationToClobber() ):

// always returns false, but in an undeducible way bool isClobberingEnabled(); // The challenge is to implement this function in a way, // that will make even the smartest optimizer believe that // it can deliver a valid pointer pointing anywhere in the heap, // stack or the static memory. volatile char* nextLocationToClobber(); const bool clobberingIsEnabled = isClobberingEnabled(); volatile char* clobberingPtr; inline void clobber() { if ( clobberingIsEnabled ) { // This will never be executed, but the compiler // cannot know about it. clobberingPtr = nextLocationToClobber(); *clobberingPtr = *clobberingPtr; } }

ACTUALIZAR

Pregunta : ¿Cómo se aseguraría de que isClobberingEnabled devuelva false "de una manera no deducible"? Ciertamente, sería trivial colocar la definición en otra unidad de traducción, pero en el momento en que habilitas LTCG, esa estrategia se anula. ¿Qué tenías en mente?

Respuesta : Podemos aprovechar una propiedad difícil de demostrar de la teoría de los números, por ejemplo, el último teorema de Fermat :

bool undeducible_false() { // It took mathematicians more than 3 centuries to prove Fermat''s // last theorem in its most general form. Hardly that knowledge // has been put into compilers (or the compiler will try hard // enough to check all one million possible combinations below). // Caveat: avoid integer overflow (Fermat''s theorem // doesn''t hold for modulo arithmetic) std::uint32_t a = std::clock() % 100 + 1; std::uint32_t b = std::rand() % 100 + 1; std::uint32_t c = reinterpret_cast<std::uintptr_t>(&a) % 100 + 1; return a*a*a + b*b*b == c*c*c; }

Chandler Carruth introdujo dos funciones en su charla CppCon2015 que se pueden usar para hacer una inhibición de grano fino del optimizador. Son útiles para escribir micro-puntos de referencia que el optimizador no simplemente utilizará para no tener sentido.

void clobber() { asm volatile("" : : : "memory"); } void escape(void* p) { asm volatile("" : : "g"(p) : "memory"); }

Estos utilizan instrucciones de ensamblaje en línea para cambiar los supuestos del optimizador.

La declaración de ensamblaje en clobber indica que el código de ensamblaje en él puede leer y escribir en cualquier lugar de la memoria. El código de ensamblaje real está vacío, pero el optimizador no lo verá porque es asm volatile . Lo cree cuando le decimos que el código puede leer y escribir en cualquier parte de la memoria. Esto evita efectivamente que el optimizador reordene o descarte las escrituras de la memoria antes de la llamada a clobber , y obliga a las lecturas de la memoria después de la llamada a clobber †.

El que está en escape , además hace que el puntero p visible para el bloque de ensamblaje. Nuevamente, debido a que el optimizador no buscará en el código de ensamblaje en línea real, ese código puede estar vacío, y el optimizador aún asumirá que el bloque usa la dirección señalada por el puntero p . Esto efectivamente hace que los puntos p estén en la memoria y no en un registro, porque el bloque de ensamblaje podría realizar una lectura desde esa dirección.

(Esto es importante porque la función clobber no forzará las lecturas ni escribirá para nada que los compiladores decidan colocar en un registro, ya que la declaración de ensamblado en clobber no establece que todo en particular deba ser visible para el ensamblaje).

Todo esto sucede sin que se genere ningún código adicional directamente por estas "barreras". Son artefactos puramente de tiempo de compilación.

Sin embargo, estos usan extensiones de lenguaje compatibles con GCC y Clang. ¿Hay una manera de tener un comportamiento similar cuando se utiliza MSVC?

† Para entender por qué el optimizador tiene que pensar de esta manera, imagine que el bloque de ensamblaje fuera un bucle que agrega 1 a cada byte en la memoria.


He usado lo siguiente en lugar de escape .

#ifdef _MSC_VER #pragma optimize("", off) template <typename T> inline void escape(T* p) { *reinterpret_cast<char volatile*>(p) = *reinterpret_cast<char const volatile*>(p); // thanks, @milleniumbug } #pragma optimize("", on) #endif

No es perfecto, pero está lo suficientemente cerca, creo.

Lamentablemente, no tengo una manera de emular a clobber .