gnuc - Pre-cálculo en gcc-4.8(C++ 11)
gcc ultima version (4)
¿Se omite esta optimización en 4.8.1?
Parece que se ha ido Todavía está presente en 4.7.2 aunque.
¿Por qué? [De uno de sus comentarios:] Creo que la optimización fue excelente y no hace daño a nada.
Es muy probable que sea accidental y los desarrolladores de gcc no lo saben.
Se me ocurre una buena razón por la que querría al menos proporcionar un límite superior en esta optimización. MSVC me mordió en 2009: cuando le di un código C generado por la máquina, estaba tratando de optimizarlo y el compilador luchó durante unos minutos. Obviamente, estaba tratando desesperadamente de hacer una optimización que debería haber sido limitada de alguna manera para que el compilador no luchara por minutos sobre un archivo fuente de 7KB. Mi punto es: es posible que desee limitar las optimizaciones que potencialmente pueden aumentar demasiado los tiempos de compilación.
Sin embargo, no parece ser el caso aquí. Lo he intentado con N
bastante pequeñas y esta optimización tampoco se realiza.
Si se omite, ¿cómo puedo forzar al compilador a hacer este cálculo previo?
Nota (2): Forzar, me refiero a comandos e interruptores del compilador
No pude engañar a gcc 4.8.1 para que hiciera esta optimización. Enviaré un informe de errores si nadie dice pronto que se trata de un problema conocido o se puede habilitar con alguna marca de compilador.
Al probar un código en gcc-4.4.0
y gcc-4.6.4
, el compilador pudo aplicar una optimización inteligente y pre-calcular el resultado para las entradas const
:
#include <iostream>
#include <chrono>
using namespace std;
const auto N = 1000000000ULL; // constexptr is tested, no effect
unsigned long long s(unsigned long long n)
{
auto s = 0ULL;
for (auto i = 0ULL; i < n; i++)
s += i;
return s;
}
int main()
{
auto t1 = std::chrono::high_resolution_clock::now();
auto x = s(N);
auto t2 = std::chrono::high_resolution_clock::now();
auto t = std::chrono::duration_cast<std::chrono::nanoseconds>(t2-t1).count();
cout << "Result: " << x << " -- time (ms):" << t/0.1e7 << endl;
}
N
es un valor constante, luego el compilador puede ejecutar la función s
en tiempo de compilación y asignar el resultado a x
. (No es necesario calcular el tiempo de ejecución para N
)
Resultados en diferentes versiones de gcc (y también una versión de clang):
- El resultado de la última versión ( clang-3.4 ) (precalculado):
0.001532 ms
. - El resultado de la versión anterior ( gcc-4.4.0 ) (precalculado):
0.013517 ms
. - result versión anterior ( gcc-4.6.4 ) (precalculado):
0.001 ms
. - La versión más reciente ( gcc-4.8.0 + ) no se calcula en tiempo de compilación, result :
1313.78 ms
Pregunta:
- ¿Se omite esta optimización en 4.8.1? ¿Por qué?
- ¿Hay algún comando / interruptor del compilador para habilitarlo (si está deshabilitado por defecto)?
-
Si se omite, ¿cómo puedo forzar al compilador a hacer este cálculo previo?
Nota (1): -O2
conmutadores -O2
y -O2
, sin efecto.
Nota (2): Forzar, me refiero a comandos e interruptores del compilador.
Nota (3): La función s
es solo un ejemplo, puede ser reemplazada por funciones más complicadas.
La forma en C ++ 11 de lidiar con los cálculos en tiempo de compilación es mediante el uso de constexpr
. Lamentablemente, constexpr
funciones constexpr
son algo limitadas en lo que se puede hacer. En C ++ 11, una función constexpr
puede contener declaraciones vacías, static_assert()
declaraciones, typedef
s, y using
declaraciones / directivas, y exactamente una return
-representación (me confundí temporalmente porque estaba mirando el C ++ 14 proyecto que tiene las reglas relajadas). Es decir, necesitarías formular tu función recursivamente. En el lado positivo, si se llama a una función constexpr
con una expresión constante, se evaluará en tiempo de compilación.
Aparte de eso, es posible que desee ayudar al compilador con su plegamiento constante. Por ejemplo, podría ayudar a
- Haz la función
s()
una función eninline
. - declara
N
comoconstexpr unsigned long long N = 1000000000ULL;
- Asegúrese de utilizar un nivel de optimización adecuado.
Lo he enviado como un error. Sí, es una Regresión en la versión 4.8 que se corrigió en las revisiones más recientes hace 5 semanas. Síguelo aquí:
Puede FORZARLO para que se ejecute en tiempo de compilación utilizando la nueva palabra clave constexpr
C ++ 11.
Primero debe transformar la iteración en recursión (este requisito se elimina en C ++ 1y), por ejemplo:
constexpr unsigned long long s(unsigned long long n)
{
return n? n + s(n-1): 0;
}
O con recursión de cola (aún funciona bien para el cálculo en tiempo de ejecución cuando la entrada es variable):
constexpr unsigned long long s_impl( unsigned long long accum, unsigned long long n, unsigned long long n_max )
{
return (n < n_max)? s_impl(accum + n + 1, n + 1, n_max): accum;
}
constexpr unsigned long long s(unsigned long long n)
{
return s_impl(0, 0, n);
}
(En C ++ 1y, todo lo que debe hacer es agregar la palabra clave constexpr
a la implementación existente)
Entonces invocalo con
constexpr auto x = s(N);