使用 callgrind 或程序集修改优化嵌套的 if 和 switch 语句
Optimizations of nested if and switch statements using callgrind or assembly modifications
考虑这段代码
| 34 static bool
| 35 _valid_character(char *str, size_t *idx)
| 36 {
| 37 char c = str[*idx];
| 38
| 39 if (c != '\' && c != '"') {
| 40 (*idx) += 1;
| 41 return true;
| 42 } else if (c == '"') {
| 43 return false;
| 44 } else {
| 45 char b = str[(*idx) + 1];
| 46 switch (b) {
| 47 case '"':
| 48 case '\':
| 49 case '/':
| 50 case 'b':
| 51 case 'f':
| 52 case 'n':
| 53 case 'r':
| 54 case 't':
| 55 (*idx) += 2;
| 56 return true;
| 57 default:
| 58 pprint_error("%s@%s:%d invalid escape sequnce \%c%c (aborting)",
| 59 __FILE_NAME__, __func__, __LINE__, c, b);
| 60 abort();
| 61 }
| 62 }
| 63 }
这个函数是我的代码完全变慢的根本原因。我曾尝试仅使用 if 语句和仅使用 switch 语句,但这是我能想到的最佳优化,其中 callgrind 可获得最佳性能。此函数约占运行时间的 25%,因此根据可疑的(抱歉)80/20 规则,使此代码更快对我最有利。
下面是在这个函数上使用 kcachegrind 可视化的 callgrind 输出。
似乎 callgrind 说我的第一次跳跃是最差的跳跃,但我已经尝试了这个 if 语句的所有组合以尽量减少跳跃和每次第一次跳跃
是最差的一跳。
此代码是用 clang 编译的
clang ... -Weverything -Werror -Wpedantic -m64 -O0 -g
所以我的问题是什么是优化此代码的最佳方法和替代技术(包括程序集修改)以优化这段简单但致命的代码。
我想继续使用 -O0
,因为我发现它对调试和寻找优化最有用。 -O1,2,3,fast
倾向于抽象到很远的地方,以便更好地理解正在发生的事情。
-- 编辑 1
有人要求输入示例。
char cstr[BUF] = "abcdefghijklmnopqrstuvwxyz\"randomgarbageafterthis";
size_t idx = 0;
while (_valid_character(cstr, &idx));
最后,输入只是一个字符串,并且会调用一个循环,直到结束 "
个字符。结束的 idx 值保持 cstr[idx] == '"''
为真。
如果你真的想优化你的代码,用笔和纸创建一个布尔逻辑table并用你自己的想法优化它的代数。之后重新创建您的代码,请在您的 switch
语句中使用 break;
。
例如:
这是您的代码。
if (c != '\' && c != '"')
{
(*idx) += 1;
return true;
}
这个比上面那个快
if (c != '\')
{
if (c != '"')
{
(*idx) += 1;
return true;
}
}
不要与 if (c != '\')
等某些字符进行比较,而是使用已注册的常量。
register const char BACKSLASH = '\';
还有:
register char c = str[*idx];
加载到 CPU 寄存器中的常量和变量在比较时要快得多。
if (c != BACKSLASH)
一般来说,如果你想要快速的代码,只使用 if
和 else
并使用简单的布尔比较参数。
编辑:
代码示例 1:
if (c != '\')
{
if (c == '"')
{
return false;
}
else
{
(*idx) += 1;
return true;
}
}
相当于:
代码示例 2:
if (c != '\')
{
if (c != '"')
{
(*idx) += 1;
return true;
}
}
else if (c == '"')
{
return false;
}
但是第一个代码示例只比较了2次,而第二个代码却比较了3次。
考虑这段代码
| 34 static bool
| 35 _valid_character(char *str, size_t *idx)
| 36 {
| 37 char c = str[*idx];
| 38
| 39 if (c != '\' && c != '"') {
| 40 (*idx) += 1;
| 41 return true;
| 42 } else if (c == '"') {
| 43 return false;
| 44 } else {
| 45 char b = str[(*idx) + 1];
| 46 switch (b) {
| 47 case '"':
| 48 case '\':
| 49 case '/':
| 50 case 'b':
| 51 case 'f':
| 52 case 'n':
| 53 case 'r':
| 54 case 't':
| 55 (*idx) += 2;
| 56 return true;
| 57 default:
| 58 pprint_error("%s@%s:%d invalid escape sequnce \%c%c (aborting)",
| 59 __FILE_NAME__, __func__, __LINE__, c, b);
| 60 abort();
| 61 }
| 62 }
| 63 }
这个函数是我的代码完全变慢的根本原因。我曾尝试仅使用 if 语句和仅使用 switch 语句,但这是我能想到的最佳优化,其中 callgrind 可获得最佳性能。此函数约占运行时间的 25%,因此根据可疑的(抱歉)80/20 规则,使此代码更快对我最有利。
下面是在这个函数上使用 kcachegrind 可视化的 callgrind 输出。
似乎 callgrind 说我的第一次跳跃是最差的跳跃,但我已经尝试了这个 if 语句的所有组合以尽量减少跳跃和每次第一次跳跃 是最差的一跳。
此代码是用 clang 编译的
clang ... -Weverything -Werror -Wpedantic -m64 -O0 -g
所以我的问题是什么是优化此代码的最佳方法和替代技术(包括程序集修改)以优化这段简单但致命的代码。
我想继续使用 -O0
,因为我发现它对调试和寻找优化最有用。 -O1,2,3,fast
倾向于抽象到很远的地方,以便更好地理解正在发生的事情。
-- 编辑 1 有人要求输入示例。
char cstr[BUF] = "abcdefghijklmnopqrstuvwxyz\"randomgarbageafterthis";
size_t idx = 0;
while (_valid_character(cstr, &idx));
最后,输入只是一个字符串,并且会调用一个循环,直到结束 "
个字符。结束的 idx 值保持 cstr[idx] == '"''
为真。
如果你真的想优化你的代码,用笔和纸创建一个布尔逻辑table并用你自己的想法优化它的代数。之后重新创建您的代码,请在您的 switch
语句中使用 break;
。
例如:
这是您的代码。
if (c != '\' && c != '"')
{
(*idx) += 1;
return true;
}
这个比上面那个快
if (c != '\')
{
if (c != '"')
{
(*idx) += 1;
return true;
}
}
不要与 if (c != '\')
等某些字符进行比较,而是使用已注册的常量。
register const char BACKSLASH = '\';
还有:
register char c = str[*idx];
加载到 CPU 寄存器中的常量和变量在比较时要快得多。
if (c != BACKSLASH)
一般来说,如果你想要快速的代码,只使用 if
和 else
并使用简单的布尔比较参数。
编辑:
代码示例 1:
if (c != '\')
{
if (c == '"')
{
return false;
}
else
{
(*idx) += 1;
return true;
}
}
相当于:
代码示例 2:
if (c != '\')
{
if (c != '"')
{
(*idx) += 1;
return true;
}
}
else if (c == '"')
{
return false;
}
但是第一个代码示例只比较了2次,而第二个代码却比较了3次。