Deoptimizing a program for the pipeline in Intel Sandybridge-family CPU
He estado atormentando mi cerebro durante una semana tratando de completar esta tarea y espero que alguien aquí pueda guiarme hacia el camino correcto. Permítanme comenzar con las instrucciones del instructor:
Su tarea es lo opuesto a nuestra primera tarea de laboratorio, que consistía en optimizar un programa de números primos. Su propósito en esta tarea es pesimizar el programa, es decir, hacerlo correr más lento. Ambos son programas intensivos en CPU. Tardan unos segundos en ejecutarse en nuestras PC de laboratorio. No puede cambiar el algoritmo.
Para desoptimizar el programa, use su conocimiento de cómo funciona la tubería Intel i7. Imagine formas de reordenar las rutas de instrucciones para introducir WAR, RAW y otros peligros. Piense en formas de minimizar la efectividad del caché. Ser diabólicamente incompetente.
La asignación dio una opción de programas de Whetstone o Montecarlo. Los comentarios sobre la eficacia de la memoria caché solo se aplican principalmente a Whetstone, pero elegí el programa de simulación Monte-Carlo:
// Un-modified baseline for pessimization, as given in the assignment
#include <algorithm> // Needed for the "max" function
#include <cmath>
#include <iostream>
// A simple implementation of the Box-Muller algorithm, used to generate
// gaussian random numbers - necessary for the Monte Carlo method below
// Note that C++11 actually provides std::normal_distribution<> in
// the <random> library, which can be used instead of this function
double gaussian_box_muller() {
double x = 0.0;
double y = 0.0;
double euclid_sq = 0.0;
// Continue generating two uniform random variables
// until the square of their "euclidean distance"
// is less than unity
do {
x = 2.0 * rand() / static_cast<double>(RAND_MAX)-1;
y = 2.0 * rand() / static_cast<double>(RAND_MAX)-1;
euclid_sq = x*x + y*y;
} while (euclid_sq >= 1.0);
return x*sqrt(-2*log(euclid_sq)/euclid_sq);
}
// Pricing a European vanilla call option with a Monte Carlo method
double monte_carlo_call_price(const int& num_sims, const double& S, const double& K, const double& r, const double& v, const double& T) {
double S_adjust = S * exp(T*(r-0.5*v*v));
double S_cur = 0.0;
double payoff_sum = 0.0;
for (int i=0; i<num_sims; i++) {
double gauss_bm = gaussian_box_muller();
S_cur = S_adjust * exp(sqrt(v*v*T)*gauss_bm);
payoff_sum += std::max(S_cur - K, 0.0);
}
return (payoff_sum / static_cast<double>(num_sims)) * exp(-r*T);
}
// Pricing a European vanilla put option with a Monte Carlo method
double monte_carlo_put_price(const int& num_sims, const double& S, const double& K, const double& r, const double& v, const double& T) {
double S_adjust = S * exp(T*(r-0.5*v*v));
double S_cur = 0.0;
double payoff_sum = 0.0;
for (int i=0; i<num_sims; i++) {
double gauss_bm = gaussian_box_muller();
S_cur = S_adjust * exp(sqrt(v*v*T)*gauss_bm);
payoff_sum += std::max(K - S_cur, 0.0);
}
return (payoff_sum / static_cast<double>(num_sims)) * exp(-r*T);
}
int main(int argc, char **argv) {
// First we create the parameter list
int num_sims = 10000000; // Number of simulated asset paths
double S = 100.0; // Option price
double K = 100.0; // Strike price
double r = 0.05; // Risk-free rate (5%)
double v = 0.2; // Volatility of the underlying (20%)
double T = 1.0; // One year until expiry
// Then we calculate the call/put values via Monte Carlo
double call = monte_carlo_call_price(num_sims, S, K, r, v, T);
double put = monte_carlo_put_price(num_sims, S, K, r, v, T);
// Finally we output the parameters and prices
std::cout << "Number of Paths: " << num_sims << std::endl;
std::cout << "Underlying: " << S << std::endl;
std::cout << "Strike: " << K << std::endl;
std::cout << "Risk-Free Rate: " << r << std::endl;
std::cout << "Volatility: " << v << std::endl;
std::cout << "Maturity: " << T << std::endl;
std::cout << "Call Price: " << call << std::endl;
std::cout << "Put Price: " << put << std::endl;
return 0;
}
Los cambios que he realizado parecen aumentar el tiempo de ejecución del código en un segundo, pero no estoy completamente seguro de qué puedo cambiar para detener la tubería sin agregar código. Un punto en la dirección correcta sería increíble, agradezco cualquier respuesta.
Actualización:el profesor que asignó esta tarea publicó algunos detallesLos aspectos más destacados son:
Es una clase de arquitectura del segundo semestre en un colegio comunitario (usando el libro de texto de Hennessy y Patterson).las computadoras de laboratorio tienen CPU HaswellLos estudiantes han estado expuestos a laCPUID
instrucción y cómo determinar el tamaño de caché, así como los intrínsecos y laCLFLUSH
instruccióne permiten todas las opciones del compilador, y también lo está el asm en líneEscribir su propio algoritmo de raíz cuadrada se anunció como fuera de la pálidaos comentarios de @ Cowmoogun sobre el meta hilo indican queno estaba claro, las optimizaciones del compilador podrían ser parte de esto, y supusimos que-O0
, y que un aumento del 17% en el tiempo de ejecución fue razonable.
or lo tanto, parece que el objetivo de la tarea era lograr que los estudiantes reordenen el trabajo existente para reducir el paralelismo en el nivel de instrucción o cosas por el estilo, pero no es algo malo que la gente haya profundizado y aprendido más.
Recuerde que esta es una pregunta de arquitectura de computadora, no una pregunta sobre cómo hacer que C ++ sea lento en general.