multi-character constant优化比较

有这样一段代码,比较字符串,然后根据key是否相同,给对应变量赋值。

版本1

最常规的写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int Record::set1(int itemIdx, const char* key, const char* value) {
uint64_t num = *(uint64_t*)value;
if (0 == strcmp(key, "record_type")) {
this->record_type = num;
} else if (0 == strcmp(key, "record_time")) {
this->record_time_start = num;
} else if (0 == strcmp(key, "rule_major_type")) {
this->rule_major_type = num;
} else if (0 == strcmp(key, "rule_minor_type")) {
this->rule_minor_type = num;
} else if (0 == strcmp(key, "src_group")) {
this->src_group = num;
} else if (0 == strcmp(key, "src_type")) {
this->src_type = num;
} else if (0 == strcmp(key, "src_branch_id")) {
this->src_branch_id = num;
} else if (0 == strcmp(key, "src_ip")) {
strncpy(this->src_ip, value, sizeof(this->src_ip));
} else if (0 == strcmp(key, "dst_group")) {
this->dst_group = num;
} else if (0 == strcmp(key, "dst_type")) {
this->dst_type = num;
} else if (0 == strcmp(key, "dst_branch_id")) {
this->dst_branch_id = num;
} else if (0 == strcmp(key, "dst_ip")) {
strncpy(this->dst_ip, value, sizeof(this->dst_ip));
}
return 0;
}

版本2

感觉前面比较第一个字符,就能排除很多无效功,于是做了第二个版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
int Record::set2(int itemIdx, const char* key, const char* value) {
uint64_t num = *(uint64_t*)value;
switch (key[0]) {
case 'r':
if (0 == strcmp(key, "record_type")) {
this->record_type = num;
} else if (0 == strcmp(key, "record_time")) {
this->record_time_start = num;
} else if (0 == strcmp(key, "rule_major_type")) {
this->rule_major_type = num;
} else if (0 == strcmp(key, "rule_minor_type")) {
this->rule_minor_type = num;
}
break;
case 's':
if (0 == strcmp(key, "src_group")) {
this->src_group = num;
} else if (0 == strcmp(key, "src_type")) {
this->src_type = num;
} else if (0 == strcmp(key, "src_branch_id")) {
this->src_branch_id = num;
} else if (0 == strcmp(key, "src_ip")) {
strncpy(this->src_ip, value, sizeof(this->src_ip));
}
break;
case 'd':
if (0 == strcmp(key, "dst_group")) {
this->dst_group = num;
} else if (0 == strcmp(key, "dst_type")) {
this->dst_type = num;
} else if (0 == strcmp(key, "dst_branch_id")) {
this->dst_branch_id = num;
} else if (0 == strcmp(key, "dst_ip")) {
strncpy(this->dst_ip, value, sizeof(this->dst_ip));
}
break;
}
return 0;
}

版本3

N年前看公司的一个模块,里面用到了multi-character constant的写法(这种写法编译器会报Warning),可以将小于等于4个的字符常量转换成一个整数表示,小端平台,rule不能写成rule,需要反过来,变成elur,这样,multi-character constant当成一个整数后,可以直接整数比较。论上是比调用strcmp调用要高效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
int Record::set3(int itemIdx, const char* key, const char* value) {
uint64_t num = *(uint64_t*)value;
int isSrc = 0;
switch (*(int32_t*)(key)) {
case 'ocer':
if ('t_dr' == *(int32_t*)(key+4) && 'e' == key[10]) {
if ('py' == *(int16_t*)(key+8)) {
this->record_type = num;
} else if ('mi' == *(int16_t*)(key+8)) {
this->record_time_start = num;
}
}
break;
case 'elur':
if ('t_ro' == *(int32_t*)(key+8) && 'py' == *(int16_t*)(key+12) && 'e' == key[14]) {
if ('jam_' == *(int32_t*)(key+4)) {
this->rule_major_type = num;
} else if ('nim_' == *(int32_t*)(key+4)) {
this->rule_minor_type = num;
}
}
break;
case '_crs':
isSrc = 1;
case '_tsd':
if ('narb' == *(int32_t*)(key+4) && 'i_hc' == *(int32_t*)(key+8) && 'd' == key[12]) {
if (isSrc) {
this->src_branch_id = num;
} else {
this->dst_branch_id = num;
}
}else if ('uorp' == *(int32_t*)(key+4) && 'p' == key[8]) {
if (isSrc) {
this->src_group = num;
} else {
this->dst_group = num;
}
} else if ('epyt' == *(int32_t*)(key+4)) {
if (isSrc) {
this->src_type = num;
} else {
this->dst_type = num;
}
} else if ('pi' == *(int16_t*)(key+4)) {
if (isSrc) {
strncpy(this->src_ip, value, sizeof(this->src_ip));
} else {
strncpy(this->dst_ip, value, sizeof(this->dst_ip));
}
}
break;
}
return 0;
}

验证一下2亿条数据,执行第一个版本耗时2.3s,执行第二个版本耗时2.0s,第三个版本耗时1.9s,性能稳定:

1
2
3
4
5
6
7
8
9
10
6152 D kvdreader.cpp:161(readBlock) | set1 cost 2671ms, set2 cost 2162ms, set3 cost 1961ms, total count 209712500
6152 D kvdreader.cpp:161(readBlock) | set1 cost 2307ms, set2 cost 2017ms, set3 cost 1960ms, total count 209693500
6152 D kvdreader.cpp:161(readBlock) | set1 cost 2290ms, set2 cost 2013ms, set3 cost 1948ms, total count 209705800
6152 D kvdreader.cpp:161(readBlock) | set1 cost 2293ms, set2 cost 2015ms, set3 cost 1946ms, total count 209712400
6152 D kvdreader.cpp:161(readBlock) | set1 cost 2298ms, set2 cost 2011ms, set3 cost 1945ms, total count 209708800
6152 D kvdreader.cpp:161(readBlock) | set1 cost 2298ms, set2 cost 2008ms, set3 cost 1950ms, total count 209688100
6152 D kvdreader.cpp:161(readBlock) | set1 cost 2285ms, set2 cost 2005ms, set3 cost 1942ms, total count 209554400
6152 D kvdreader.cpp:161(readBlock) | set1 cost 2294ms, set2 cost 2006ms, set3 cost 1945ms, total count 209563300
6152 D kvdreader.cpp:161(readBlock) | set1 cost 2294ms, set2 cost 2041ms, set3 cost 1951ms, total count 209686400
6152 D kvdreader.cpp:161(readBlock) | set1 cost 2285ms, set2 cost 2008ms, set3 cost 1949ms, total count 209688200

注:首次执行的时候,数据还不在cache中,导致set1的时间有偏差,看第一条为2671ms,理论上应该在2307ms左右,若先调用set3,set3的第一次为2622ms,而不是1961ms。

可以看出,效果意义并不大,我一天最多才处理2亿条,一天节约0.5s的时间,代码却比较难维护,还是,不要太计较CPU性能,不要太在意细节,不要提前优化。