Я надеялся улучшить производительность, передав указатель функции или объект функции вызову функции внутри вложенного цикла, чтобы избежать ветвления цикла. Ниже приведены три кода: один с объектом функции, с указателем функции и с ветвлением. Для любого варианта оптимизации компилятора или для любого размера задачи и указатель на функцию, и версия объекта работают меньше всего. Это удивительно для меня; почему накладные расходы из-за указателя функции или масштаба объекта с размером проблемы? Второй вопрос. Почему объект функции работает хуже, чем указатель на функцию?
Обновлять
В конце я также добавляю версию лямбда-выражения того же кода. Опять побеждает грубая сила. Версия лямбда-выражения занимает более чем в два раза больше времени с оптимизацией или без нее по сравнению с соответствующим кодом грубой силы и для другого размера задачи.
Коды ниже. Выполнить с помощью ./a.out [SIZE] [function choice]
Объект функции:
#include <iostream>
#include <chrono>
class Interpolator
{
public:
Interpolator(){};
virtual double operator()(double left, double right) = 0;
};
class FirstOrder : public Interpolator
{
public:
FirstOrder(){};
virtual double operator()(double left, double right) { return 2.0 * left * left * left + 3.0 * right; }
};
class SecondOrder : public Interpolator
{
public:
SecondOrder(){};
virtual double operator()(double left, double right) { return 2.0 * left * left + 3.0 * right * right; }
};
double kernel(double left, double right, Interpolator *int_func) { return (*int_func)(left, right); }
int main(int argc, char *argv[])
{
double *a;
int SIZE = atoi(argv[1]);
int it = atoi(argv[2]);
//initialize
a = new double[SIZE];
for (int i = 0; i < SIZE; i++)
a[i] = (double)i;
std::cout << "Initialized" << std::endl;
Interpolator *first;
switch (it)
{
case 1:
first = new FirstOrder();
break;
case 2:
first = new SecondOrder();
break;
}
std::cout << "function" << std::endl;
auto start = std::chrono::high_resolution_clock::now();
//loop
double g;
for (int i = 0; i < SIZE; i++)
{
g = 0.0;
for (int j = 0; j < SIZE; j++)
{
g += kernel(a[i], a[j], first);
}
a[i] += g;
}
auto stop = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
std::cout << "Finalized in " << duration.count() << " ms" << std::endl;
return 0;
}
Указатель функции:
#include <iostream>
#include <chrono>
double firstOrder(double left, double right) { return 2.0 * left * left * left + 3.0 * right; }
double secondOrder(double left, double right) { return 2.0 * left * left + 3.0 * right * right; }
double kernel(double left, double right, double (*f)(double, double))
{
return (*f)(left, right);
}
int main(int argc, char *argv[])
{
double *a;
int SIZE = atoi(argv[1]);
int it = atoi(argv[2]);
a = new double[SIZE];
for (int i = 0; i < SIZE; i++)
a[i] = (double)i; // initialization
std::cout << "Initialized" << std::endl;
//Func func(it);
double (*func)(double, double);
switch (it)
{
case 1:
func = &firstOrder;
break;
case 2:
func = &secondOrder;
break;
}
std::cout << "function" << std::endl;
auto start = std::chrono::high_resolution_clock::now();
//loop
double g;
for (int i = 0; i < SIZE; i++)
{
g = 0.0;
for (int j = 0; j < SIZE; j++)
{
g += kernel(a[i], a[j], func);
}
a[i] += g;
}
auto stop = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
std::cout << "Finalized in " << duration.count() << " ms" << std::endl;
return 0;
}
Ветвление:
#include <iostream>
#include <chrono>
double firstOrder(double left, double right) { return 2.0 * left * left * left + 3.0 * right; }
double secondOrder(double left, double right) { return 2.0 * left * left + 3.0 * right * right; }
int main(int argc, char *argv[])
{
double *a;
int SIZE = atoi(argv[1]); // array size
int it = atoi(argv[2]); // function choice
//initialize
a = new double[SIZE];
double g;
for (int i = 0; i < SIZE; i++)
a[i] = (double)i; // initialization
std::cout << "Initialized" << std::endl;
auto start = std::chrono::high_resolution_clock::now();
//loop
for (int i = 0; i < SIZE; i++)
{
g = 0.0;
for (int j = 0; j < SIZE; j++)
{
if (it == 1)
{
g += firstOrder(a[i], a[j]);
}
else if (it == 2)
{
g += secondOrder(a[i], a[j]);
}
}
a[i] += g;
}
auto stop = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
std::cout << "Finalized in " << duration.count() << " ms" << std::endl;
return 0;
}
Лямбда-выражение
#include <iostream>
#include <chrono>
#include<functional>
std::function<double(double, double)> makeLambda(int kind){
return [kind] (double left, double right){
if(kind == 0) return 2.0 * left * left * left + 3.0 * right;
else if (kind ==1) return 2.0 * left * left + 3.0 * right * right;
};
}
int main(int argc, char *argv[])
{
double *a;
int SIZE = atoi(argv[1]);
int it = atoi(argv[2]);
//initialize
a = new double[SIZE];
for (int i = 0; i < SIZE; i++)
a[i] = (double)i;
std::cout << "Initialized" << std::endl;
std::function<double(double,double)> interp ;
switch (it)
{
case 1:
interp = makeLambda(0);
break;
case 2:
interp = makeLambda(1);
break;
}
std::cout << "function" << std::endl;
auto start = std::chrono::high_resolution_clock::now();
//loop
double g;
for (int i = 0; i < SIZE; i++)
{
g = 0.0;
for (int j = 0; j < SIZE; j++)
{
g += interp(a[i], a[j]);
}
a[i] += g;
}
auto stop = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
std::cout << "Finalized in " << duration.count() << " ms" << std::endl;
return 0;
}
if
вне цикла (хотя разницы не будет, так как компилятор сделает это за вас). - person bipll   schedule 01.11.2020