自定义memstr(strstr)速度优化

时间:2016-07-15 21:15:04

标签: c performance pointers arm strstr

我正在编写一个例程,在嵌入式(ARM Cortex M0 @ 16MHz)应用程序的指定内存块中找到一个字符串,我想知道为什么我编写的两个不同版本以不同的速度运行。

char* memstr(char* mem, uint32_t n, char* str) {

    if( (str[0] == '\0') || (n == 0) )   return NULL;

    uint32_t i = 0;
    char* max_mem;

    max_mem = mem + n;

    while( mem < max_mem ) {
        if( *mem != str[i] ) {
            mem -= i;
            i = 0;
        } else {
            if(str[i+1] == '\0')   return mem - i;
            i++;
        }
        mem++;
    }

    return NULL;
}


char* memstr2(char* mem, uint32_t n, char* str) {

    if( (str[0] == '\0') || (n == 0) )   return NULL;

    uint32_t c = 0;
    uint32_t i = 0;

    while( c < n ) {
        if( mem[c] != str[i] ) {
            c -= i;
            i = 0;
        } else {
            i++;
            if(str[i] == '\0')   return &mem[c - i + 1];
        }
        c++;
    }

    return NULL;
}
当在20到200字节的内存中找到7个字符的字符串时,memstr一直比memstr2快1us。例如,在110字节中找到7个字符的字符串,memstr需要106us,而memstr2需要107us。 1us可能听起来不是什么大问题,但在嵌入式应用程序中,每个蜱都很重要,这是一个缺点。

一种奖励问题:这也促使我编写自己的strstr,它比stock strstr更快(例如,在207字符串中查找7个字符的字符串需要my_strstr 236us和strstr 274us)。这有什么不对,因为strstr必须进行相当优化?

char* my_strstr(char* str1, char* str2) {
    uint32_t i = 0;

    if( str2[0] == '\0' )   return NULL;

    while( *str1 != '\0' ) {
        if( *str1 != str2[i] ) {
            str1 -= i;
            i = 0;
        } else {
            i++;
            if(str2[i] == '\0')   return (str1 - i - 1);
        }
        str1++;
    }

    return NULL;
}

3 个答案:

答案 0 :(得分:0)

首先,如果搜索以两个相等字符开头的字符串,则两个函数都不起作用:如果搜索xxabcde并且字符串包含xxxabcde,那么当您注意到xxabcde与第三个x不匹配时,你已经跳过两个x并且不匹配字符串。

您也不检查是否搜索空字符串,在这种情况下,您的代码会产生未定义的行为。

您将内存与内存进行比较。但是,只需将内存与单个字符进行比较,就可以完成大量的工作。如果您搜索“abcde”,首先必须找到字母a。所以我先检查一个空字符串,然后读取第一个字符。然后首先循环检查该字符。

char first = str2 [0];
if (first == '\0') return mem;

for (; mem < maxmem; ++mem) if (*mem == first) {
    ... check whether there is a match
}

您应该检查您的数据。如果您希望搜索字符串提前出现而您希望它通常根本不存在,那么您可以编写不同的代码。

答案 1 :(得分:0)

在memstr&#39; mem&#39;用作指针。在memstr2&#39; mem&#39;用作数组的名称&#39; mem [c]&#39;。根据优化,编译器可能会乘以1。

例如在声明中:

if( mem[c] != str[i] ) {
每次循环计算

mem [c]为

* ( &mem[0] + c * sizeof(mem[0]) )

一个体面的编译器会发现,对于一个&#39; char&#39; sizeof(mem [0])== 1,可以跳过多个。如果有意禁用优化,就像通常对调试版本所做的那样,我可以想象每个循环有一个额外的乘法运算。即使没有乘法,memstr2版本也会有一些额外的计算时间,但如果可以测量的话会让我感到惊讶。

答案 2 :(得分:-1)

在我看来,差异可能是由于在第二个版本中你从函数返回时取消引用指针(return &mem[c - i - 1];),这可能导致在内存中访问,这是昂贵的,是在你的第一个函数(mem - i)中不会发生的事情 但唯一可以确定的是查看为每个案例创建的程序集 我不认为这是关于C而是关于编译器和平台