c++ - 数组的累积乘积:std::accumulate vs loop
问题描述
假设我想计算k
数组第一个元素的累积乘积。
int arr[8] = {3, 5, 7, 4, 8, 9, 1, 2};
int k = 3;
就性能而言,哪一个是最佳选择?
选项1.普通for循环
int result = 1;
for (size_t i = 0; i < k; ++i) {
result *= arr[i];
}
选项 2. 累积
result = std::accumulate(std::begin(arr),
std::begin(arr) + k,
1,
[](const int& x, const int &y) { return x*y; });
我对k
较小的情况特别感兴趣,例如k = 3
或k = 4
. 在这种情况下,花哨的 C++11 做事方式真的值得吗?
解决方案
简短的回答:使用算法而不是手写循环与性能无关!你可以在这里看到一个可能的实现。您会注意到它只是一个循环,您也可以自己编写。
更好的比较是手写循环与:
auto result = std::accumulate(std::begin(arr),
std::begin(arr) + k,
1,
std::multiplies<>{});
这等效于手写循环,不需要 C++11(仅适用于auto
,std::begin
和std::end
, 并且std::multiplies<>
需要 C++14,但在您使用 之前std::multiplies<int>
)。
该算法的优点是表现力和可读性。您不能为循环命名,而算法名称则说明了它的作用。二元运算符和初始值也更容易替换。初始值不一定与元素的类型相同,返回类型accumulate
是从中推导出来的。如果您想将更多值相乘并避免溢出,您只需将 替换1
为1L
。如果您后来决定不想相乘,而是相加,则只需替换std::multiplies
为std::plus
. 对于手写循环,这种重构需要深入细节并考虑整个循环,而不是只更改您想要更改的一个细节。除此之外,哪个“更好”是基于意见的。如果有的话,性能方面不会有太大的差异。
请注意,C++11 和 C++14 带来的变化不仅仅是“花哨”。在 C++11 之前,调用看起来像这样:
int result = std::accumulate(arr, // <- int
arr + k,
1, // <- int
std::multiplies<int>{}); // <- int
这里用于结果的类型出现了 3 次,这使得上面关于轻松替换初始值的类型的观点没有意义。随之而来的变化auto
和带有模板的函子operator()
不是关于花哨的,而是它们显着减少了犯错的机会。
TL;博士
对于像你这样简单的循环,我可能会使用手写循环。它清晰易读,任何花哨的算法都不太可能使其性能更高。