Я планирую некоторые простые числовые операции, чтобы решить, каким образом (то есть, с помощью каких инструментов из какой библиотеки) я собираюсь реализовать вычислительно интенсивное моделирование. Приведенный ниже код вычисляет сумму двух внутренних произведений векторов, используя (1) MTL4 версии 4.0.9486 как есть (т.е. без BLAS
), (2) std::vector
с и std::inner_product
и (3) std::valarray
s. Я выбрал игрушечный пример этой конкретной формы, потому что она казалась идеальной площадкой для шаблонов выражений MTL4.
Чтобы сузить все до одного вопроса, справедливо ли следующее сравнение или оно ставит (непреднамеренно) любой из трех подходов в невыгодное положение? Я был немного удивлен, что (2) быстрее, чем (1). Конечно, будет ли общая симуляция быстрее или нет — это отдельная история.
Если у кого-нибудь есть какие-либо предложения по более тщательным испытаниям, которые могли бы выявить сильные или слабые стороны каждого подхода, я с удовольствием их опробую.
Простите макросы в коде; они просто std::cout<<
заявления и призывы к <chrono>
коммунальные услуги.
Заранее спасибо.
Код C ++:
#include <iostream>
#include <valarray>
#include <vector>
#include <algorithm>
#include <boost/numeric/mtl/mtl.hpp>
int main(int argc, const char * argv[])
{
/* DOT PRODUCTS */
constexpr int trials{15};
std::vector<double> mtl_times(trials, 0.0), stl_times(trials, 0.0), valarray_times(trials, 0.0);
constexpr size_t sz{10000000};
double val = M_PI;
mtl::dense_vector<double> m(sz, val), n(sz, val), p(sz, val), q(sz, val);
std::vector<double> y(sz, val), z(sz, val), v(sz, val), w(sz, val);
std::valarray<double> b(val, sz), c(val, sz), d(val, sz), e(val, sz);
double x{0.0}, k{0.0}, aa{0.0};
auto t0 = NOW
auto t1 = t0;
for (int i = 0; i < trials; ++i) {
// MTL4 vectors
t0 = NOW // call now() from <chrono>
k = dot(m, n) + dot(p, q);
t1 = NOW
mtl_times[i] = DURATIONm // duration cast of (t1-t0).count()
// STL vectors
t0 = NOW
x = std::inner_product(y.begin(), y.end(), z.begin(), 0.0) + std::inner_product(v.begin(), v.end(), w.begin(), 0.0);
t1 = NOW
stl_times[i] = DURATIONm
// valarrays
t0 = NOW
aa = (b*c + d*e).sum();
t1 = NOW
valarray_times[i] = DURATIONm
}
std::cout << "MTL4: average time for dot product = " << std::accumulate(mtl_times.begin(), mtl_times.end(), 0.0)/mtl_times.size() << " msec\n";
PRINTV(mtl_times)
PRINTME(result, k)
std::cout << '\n';
std::cout << "STL vectors + std::inner_product: average time for dot product = " << std::accumulate(stl_times.begin(), stl_times.end(), 0.0)/stl_times.size() << " msec\n";
PRINTV(stl_times)
PRINTME(result, x)
std::cout << '\n';
std::cout << "valarrays: average time for dot product = " << std::accumulate(valarray_times.begin(), valarray_times.end(), 0.0)/valarray_times.size() << " msec\n";
PRINTV(valarray_times)
PRINTME(result, aa)
return 0;
}
Выход C ++:
MTL4: среднее время для точечного продукта = 180,333 мсек
mtl_times =
177 175 174 174 175 178 176 185 184 174 175 179 175 216 188результат: 1.97392e + 08
STL-векторы + std :: inner_product: среднее время для точечного произведения = 58,6 мсек
stl_times = 56 55 56 57 57 56 57 56 57 55 55 58 56 58 90
результат: 1.97392e + 08
valarrays: среднее время для точечного произведения = 64,4 мсек
valarray_times = 63 64 63 64 65 63 63 63 64 63 63 63 64 64 77
результат: 1.97392e + 08
Для записи, MatLab хорошо работает:
Код MatLab:
trials = 15;
times_ms = zeros(1, trials);
sz = 1e7;
val = pi;
x(sz) = val;
x(1:end-1) = val;
y(sz) = val;
y(1:end-1) = val;
v(sz) = val;
v(1:end-1) = val;
w(sz) = val;
w(1:end-1) = val;
z = 0;
for i = 1:trials
tic
z = x*y' + v*w';
times_ms(i) = toc*1e3;
end
avg_time = sum(times_ms)/length(times_ms)
times_ms
z
Выход MatLab:
avg_time = 56,0687 мсек
times_ms = 56,8919 57,2052 55,3179 55,5126 55,7660 55,3982 55,1044 55,4809 57,7229 56,1902 57,3888 56,5263 55,2830 55,4926 55,7501
z = 1,9739e + 08
Это неудивительно, поскольку встроенные операции оптимизированы, однако существуют другие препятствия, связанные с использованием MatLab в симуляции.
Вычисление точечных продуктов снова и снова, вероятно, будет связано с памятью. Было бы лучше сравнить что-то вроде умножения матриц, если вы пытаетесь получить точное представление о разнице в скорости, которую вы можете ожидать. Продукты Dot также достаточно просты, так что вы можете просто проверить код сборки, чтобы увидеть, что происходит; Я бы посоветовал вам сделать это.
Ваше сравнение немного несправедливо по отношению к valarrays; Вы делаете два массива, затем складываете их вместе, затем берете их сумму. Вероятно, лучше вычислить две суммы и сложить их. (Я не знаю, как избежать дополнительного сканирования всего массива с помощью интерфейса valarray.)
Других решений пока нет …