C++ 性能反向优化——用哈希表unordered_map消除if else导致性能降低。

从代码整洁的角度考虑,对于不同的值将调用相同参数的不同函数,我们通常可以通过建立从值到对应函数指针的哈希表,从而将if else消除。但实际可能使性能更低,以下是测试例子。

原因在于,if else分支预测不正确虽然可能使指令流水线几条指令执行错误,但通过哈希表的方式,增加了计算哈希值、查询哈希表以及通过函数指针调用的开销,从而可能使得调用过程慢了更多。

以下比较了四种方法的计算性能:

  1. if else

  2. unordered_map

  3. switch case

  4. function pointer array

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 10;
constexpr int COUNT = 10000000;
long long a = 0;
void targetFunc1(){
	for(int i = 0; i < N; i++){
		a++;
	}
}

void targetFunc2(){
	for(int i = 0; i < N; i++){
		a++;
	}
}

void targetFunc3(){
	for(int i = 0; i < N; i++){
		a++;
	}
}

void targetFunc4(){
	for(int i = 0; i < N; i++){
		a++;
	}
}

void targetFunc5(){
	for(int i = 0; i < N; i++){
		a++;
	}
}

void targetFunc6(){
	for(int i = 0; i < N; i++){
		a++;
	}
}

// 1. if else

void test1(int a){
	if(a == 1) targetFunc1();
	if(a == 2) targetFunc2();
	if(a == 3) targetFunc3();
	if(a == 4) targetFunc4();
	if(a == 5) targetFunc5();
	if(a == 6) targetFunc6();
}

std::unordered_map<int, void(*)()> funcs = {
	{1, targetFunc1},
	{2, targetFunc2},
	{3, targetFunc3},
	{4, targetFunc4},
	{5, targetFunc5},
	{6, targetFunc6}
};


// 2. unordered_map

void test2(int a){
	funcs[a]();
}


// 3. switch case

void test3(int a){
	switch(a){
		case 1:
			targetFunc1();
			break;
		case 2:
			targetFunc2();
			break;
		case 3:
			targetFunc3();
			break;
		case 4:
			targetFunc4();
			break;
		case 5:
			targetFunc5();
			break;
		case 6:
			targetFunc6();
			break;
	}
}


// 4. function pointer array
void (*arr[6])();

void test4(int a){
	arr[a-1]();
}

void timeMeasure(void(*f)(int)){
    auto begin = std::chrono::high_resolution_clock::now();
    uint32_t iterations = 100;
    for(uint32_t i = 0; i < iterations; ++i)
    {
		int index = rand() % 6 + 1;
		f(index);
    }
    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(end-begin).count();
    std::cout << duration << "ns total, average : " << duration / iterations << "ns." << std::endl;
}

int main()
{

	arr[0] = targetFunc1;
	arr[1] = targetFunc2;
	arr[2] = targetFunc3;
	arr[3] = targetFunc4;
	arr[4] = targetFunc5;
	arr[5] = targetFunc6;
	timeMeasure(test1);
	std::cout<<a<<std::endl;
	timeMeasure(test2);
	std::cout<<a<<std::endl;
	timeMeasure(test3);
	std::cout<<a<<std::endl;
	timeMeasure(test4);
	std::cout<<a<<std::endl;
	return 0;
}

16859ns total, average : 168ns.
1000
41576ns total, average : 415ns.
2000
13834ns total, average : 138ns.
3000
14368ns total, average : 143ns.
4000

结论:

  1. switch case和函数指针数组查表比if else实现的性能较高

  2. unordered_map查表导致性能降低。

posted on 2023-03-08 14:55  七昂的技术之旅  阅读(212)  评论(0编辑  收藏  举报

导航