I am trying to sum all elements in position n
in a set of std::array
s. The value of the sum is stored in a std::array
passed to my function add_rows
. The summation is done with recursively "calling" the templated class method to sum the columns with decreasing index for column, until I hit column 0, and the the same for the next row until I hit row 0.
I also have a loop version which does the same and I compare the time it takes to execute both ways of computing the sum. I was expecting to see the templated version to perform better, but it output says is ~25 times slower. Is there anything wrong with the templated version which makes it slower?
Before starting this I got inspired by this article "Using Metaprograms to Unroll Loops"
Output from the program is:
Templated version took: 23 ns.
Loop version took: 0 ns.
Code:
#include <iostream>
#include <array>
#include <numeric>
#include <chrono>
template<size_t num_rows, size_t row_index, size_t num_columns, size_t column_index>
class sumRow;
template<size_t num_rows, size_t row_index, size_t num_columns>
class sumRow<num_rows, row_index, num_columns, 0>
{
public:
static inline int result(const std::array<std::array<int, num_rows>, num_columns>& arrays) noexcept
{
return arrays[0][row_index];
}
};
template<size_t num_rows, size_t row_index, size_t num_columns, size_t column_index>
class sumRow
{
public:
static inline int result(const std::array<std::array<int, num_rows>, num_columns>& arrays) noexcept
{
return arrays[column_index][row_index] + sumRow<num_rows, row_index, num_columns, column_index - 1>::result(arrays);
}
};
// Array of arrays
template<size_t num_rows, size_t row_index, size_t num_columns>
class sumRows;
template<size_t num_rows, size_t num_columns>
class sumRows<num_rows, 0, num_columns>
{
public:
static inline void result(const std::array<std::array<int, num_rows>, num_columns>& arrays, std::array<int, num_rows>& result) noexcept
{
result[0] = sumRow<num_rows, 0, num_columns, num_columns - 1>::result(arrays);
}
};
template<size_t num_rows, size_t row_index, size_t num_columns>
class sumRows
{
public:
static inline void result(const std::array<std::array<int, num_rows>, num_columns>& arrays, std::array<int, num_rows>& result) noexcept
{
result[row_index - 1] = sumRow<num_rows, row_index - 1, num_columns, num_columns - 1>::result(arrays);
sumRows<num_rows, row_index - 1, num_columns>::result(arrays, result);
}
};
template<size_t num_rows, size_t num_columns>
inline void sum_rows(const std::array<std::array<int, num_rows>, num_columns>& arrays, std::array<int, num_rows>& result)
{
sumRows<num_rows, num_rows, num_columns>::result(arrays, result);
};
template<size_t num_channels, size_t channel_size>
inline void loop_sum(const std::array<std::array<int, channel_size>, num_channels>& channels, std::array<int, channel_size>& results) noexcept
{
for (size_t sample_index = 0; sample_index < channel_size; ++sample_index)
{
int result = 0;
for (size_t channel_index = 0; channel_index < num_channels; ++channel_index)
{
result += channels[channel_index][sample_index];
}
results[sample_index] = result;
}
};
// Inspired by from https://stackoverflow.com/a/21995693/2996272
struct measure_cpu_clock
{
template<typename F, typename ...Args>
static clock_t execution(F&& func, Args&&... args)
{
auto start = std::clock();
std::forward<decltype(func)>(func)(std::forward<Args>(args)...);
return std::clock() - start;
}
};
const int num_channels = 850;
const int num_samples = 32;
using channel = std::array<int, num_samples>;
int main()
{
std::array<channel, num_channels> channels{};
for (auto&& item : channels)
{
std::iota(item.begin(), item.end(), 1);
}
// Templated version
channel results = {};
auto execution_time = measure_cpu_clock::execution(sum_rows<num_samples, num_channels>, channels, results);
std::cout << "Templated version took: " << execution_time << " ns." << std::endl;
// Loop version
channel results2 = {};
execution_time = measure_cpu_clock::execution(loop_sum<num_channels, num_samples>, channels, results2);
std::cout << "Loop version took: " << execution_time << " ns." << std::endl;
}