我必须以相同的方式处理数组的每个元素,然后必须通过不可预测的模式修改每个元素。
这些片段之间的性能是否存在差异?如果有,为什么会这样做?
std::vector<int> nums;
//fill nums array
for(unsigned int i = 0; i < nums.size(); ++i){
nums[i] *= nums[i];
if(nums[i] < 10){
nums[i] = 0;
}
}
std::vector<int> nums;
//fill nums array
for(unsigned int i = 0; i < nums.size(); ++i){
nums[i] *= nums[i];
}
for(unsigned int i = 0; i < nums.size(); ++i){
if(nums[i] < 10){
nums[i] = 0;
}
}
这样的不同方法会改善什么吗?
std::vector<int> nums;
//fill nums array
std::vector<int> flags;
flags.resize(nums.size());
for(unsigned int i = 0; i < nums.size(); ++i){
nums[i] *= nums[i];
flags[i] = nums[i] < 10;
}
for(unsigned int i = 0; i < flags.size(); ++i){
nums[i] = (!flags[i]) * nums[i]; //!flags[i] is 0 if nums[i] < 10
}
答案 0 :(得分:4)
第一个语句较少(这总是一个很好的粗略指导:较少的类似语句通常意味着更好的表现),但高于其他一切,它更具可读性。
不要进行微优化:将其留给编译器。
如果您有任何疑问,请检查生成的装配/配置文件的性能。
最后(道歉,因为我无法抗拒),如果nums
是std::vector<unsigned>
,那么你可以撰写
for (std::size_t i = 0; i < nums.size(); ++i){
nums[i] *= (nums[i] <= 3 ? 0 : nums[i]);
}
可以帮助分支预测器。
答案 1 :(得分:2)
我机器上的测试结果(阵列中有1亿个):
checkpoint
starting test: approach1
test took: 84ms
starting test: approach2
test took: 190ms
starting test: approach3
test took: 529ms
starting test: Bathsheba's idea
test took: 61ms
顺便说一句,编写惯用代码通常是最有效的。 clang,gcc et。人。优化者很棒:
void approach5(std::vector<int> &nums) {
auto filter = [](auto x) { return (x < 10) ? 0 : x; };
auto grow = [filter](auto x) { return filter(x * x); };
std::transform(begin(nums), end(nums), begin(nums), grow);
}
这是代码。
#include <iostream>
#include <chrono>
#include <random>
#include <array>
#include <vector>
#include <algorithm>
auto constexpr test_size = std::size_t(100'000'000);
using namespace std::literals;
void approach1(std::vector<int> &nums) {
for (unsigned int i = 0; i < nums.size(); ++i) {
nums[i] *= nums[i];
if (nums[i] < 10) {
nums[i] = 0;
}
}
}
void approach2(std::vector<int> &nums) {
for (unsigned int i = 0; i < nums.size(); ++i) {
nums[i] *= nums[i];
}
for (unsigned int i = 0; i < nums.size(); ++i) {
if (nums[i] < 10) {
nums[i] = 0;
}
}
}
void approach3(std::vector<int> &nums) {
std::vector<int> flags;
flags.resize(nums.size());
for (unsigned int i = 0; i < nums.size(); ++i) {
nums[i] *= nums[i];
flags[i] = nums[i] < 10;
}
for (unsigned int i = 0; i < flags.size(); ++i) {
nums[i] = (!flags[i]) * nums[i]; //!flags[i] is 0 if nums[i] < 10
}
}
void approach4(std::vector<int> &nums) {
for (std::size_t i = 0; i < nums.size(); ++i) {
nums[i] *= (nums[i] <= 3 ? 0 : nums[i]);
}
}
auto test = [](auto &&name, auto &&approach, auto &&data) {
std::cout << "starting test: " << name << std::endl;
auto my_data = std::vector<int>(data.begin(), data.end());
auto now = std::chrono::high_resolution_clock::now();
approach(my_data);
auto then = std::chrono::high_resolution_clock::now();
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(then - now);
std::cout << "test took: " << diff.count() << "ms" << std::endl;
};
std::array<int, test_size> test_data;
int main() {
std::random_device rd;
std::default_random_engine eng(rd());
std::uniform_int_distribution<> dist(0, 100);
std::generate(test_data.begin(), test_data.end(), [&]() { return dist(eng); });
std::cout << "checkpoint" << std::endl;
test("approach1", approach1, test_data);
test("approach2", approach2, test_data);
test("approach3", approach3, test_data);
test("Bathsheba's idea", approach4, test_data);
}
答案 2 :(得分:0)
在算法分析中 第一种模式:时间复杂度是数组O(n)的大小 第二种模式:时间复杂度在2个循环之间最大 O(n)和O(n)所以它也是O(n)。
第一和第二模式是相同的。
第三种模式没有改善任何事情。
结论:3种模式不是技术。