Я пробовал свой мозг на неделю, пытаясь выполнить это задание, и я надеюсь, что кто-то здесь может привести меня к правильному пути. Позвольте мне начать с инструкций инструктора:
Ваше задание является противоположностью нашего первого лабораторного задания, которое должно было оптимизировать программу простых чисел. Ваша цель в этом назначении - пессимизировать программу, то есть заставить ее работать медленнее. Обе из них - программы с интенсивным использованием процессора. Они занимают несколько секунд, чтобы работать на наших лабораторных компьютерах. Вы не можете изменять алгоритм.
Чтобы обезвредить программу, используйте свои знания о том, как работает конвейер Intel i7. Представьте себе способы переупорядочения инструкций для введения WAR, RAW и других опасностей. Подумайте о способах минимизации эффективности кеша. Будьте дьявольски некомпетентны.
Назначение дало выбор программ Whetstone или Monte-Carlo. Комментарии к эффективности кэша в основном применимы только к Whetstone, но я выбрал программу моделирования Монте-Карло:
// 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;
}
Изменения, которые я сделал, по-видимому, увеличили время выполнения кода на секунду, но я не совсем уверен, что я могу изменить, чтобы остановить работу конвейера без добавления кода. Точка в правильном направлении была бы потрясающей, я бы оценил любые ответы.
Обновление: профессор, который дал это задание, разместил некоторые детали
Основные моменты:
- Это второй класс архитектуры семестра в колледже (используя учебник Hennessy и Patterson).
- Лабораторные компьютеры имеют процессоры Haswell
-  Студенты были ознакомлены с инструкцией CPUIDи как определить размер кеша, а также встроенные функции и инструкциюCLFLUSH.
- допустимы любые параметры компилятора, а также встроенный asm.
- Написание собственного алгоритма с квадратным корнем объявлено как находящееся за пределами бледного
Комментарии Cowmoogun в мета-потоке указывают, что не было ясно, что оптимизация компилятора может быть частью этого и предполагаемого -O0, и что увеличение на 17% во время выполнения было разумным.
Итак, похоже, что цель задания заключалась в том, чтобы заставить учащихся переупорядочить существующую работу, чтобы уменьшить уровень на уровне инструкций parallelism или что-то в этом роде, но это не плохо, что люди углубились и узнали больше.
Имейте в виду, что это вопрос компьютерной архитектуры, а не вопрос о том, как сделать С++ медленным в целом.
