使用 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)

一般来说,如果你想要快速的代码,只使用 ifelse 并使用简单的布尔比较参数。

编辑:

代码示例 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次。