优化解析器函数c程序

时间:2012-03-30 06:20:17

标签: c optimization

gcc (GCC) 4.6.3 20120306 (Red Hat 4.6.3-2)
c89

您好,

我想知道我是否可以再优化此代码。由于这是在一个快速的事务服务器中,它每秒会有很多调用。所以解析器必须非常快速和优化。

我想知道我能做出什么改进。

包含测试用例的完整代码。函数g_get_dnis_user_part是我想要优化的。

我希望这是发布到的正确论坛。

#include <stdio.h>
#include <ctype.h>
#include <string.h>

#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif

static int g_get_dnis_user_part(const char *dnis, char *user_part, size_t size);

int main(void)
{
    /* Test cases */
    const char *dnis_test1 = "0846372573@10.1.8.34";
    const char *dnis_test2 = "084637257310.1.8.34";
    const char *dnis_test3 = "084e672573@10.1.8.34";
    const char *dnis_test4 = "";
    const char *dnis_test5 = "084637257310.1.8.34@";
    size_t passes = 0;
    size_t failures = 0;

#define MAX_ADDRESS_LEN 32

    char user_part[MAX_ADDRESS_LEN];

    memset(user_part, 0, sizeof user_part);
    if(g_get_dnis_user_part(dnis_test1, user_part, MAX_ADDRESS_LEN) == TRUE) {
        printf("TEST 1 PASSED [ %s ] [ %s ]\n", dnis_test1, user_part);
        passes++;
    }
    else {
        printf("TEST 1 FAILED [ %s ] [ %s ]\n", dnis_test1, user_part);
        failures++;
    }

    memset(user_part, 0, sizeof user_part);
    if(g_get_dnis_user_part(dnis_test2, user_part, MAX_ADDRESS_LEN) == TRUE) {
        printf("TEST 2 PASSED [ %s ] [ %s ]\n", dnis_test2, user_part);
        passes++;
    }
    else {
        printf("TEST 2 FAILED [ %s ] [ %s ]\n", dnis_test2, user_part);
        failures++;
    }

    memset(user_part, 0, sizeof user_part);
    if(g_get_dnis_user_part(dnis_test3, user_part, MAX_ADDRESS_LEN) == TRUE) {
        printf("TEST 3 PASSED [ %s ] [ %s ]\n", dnis_test3, user_part);
        passes++;
    }
    else {
        printf("TEST 3 FAILED [ %s ] [ %s ]\n", dnis_test3, user_part);
        failures++;
    }

    memset(user_part, 0, sizeof user_part);
    if(g_get_dnis_user_part(dnis_test4, user_part, MAX_ADDRESS_LEN) == TRUE) {
        printf("TEST 4 PASSED [ %s ] [ %s ]\n", dnis_test4, user_part);
        passes++;
    }
    else {
        printf("TEST 4 FAILED [ %s ] [ %s ]\n", dnis_test4, user_part);
        failures++;
    }

    memset(user_part, 0, sizeof user_part);
    if(g_get_dnis_user_part(dnis_test5, user_part, MAX_ADDRESS_LEN) == TRUE) {
        printf("TEST 5 PASSED [ %s ] [ %s ]\n", dnis_test5, user_part);
        passes++;
    }
    else {
        printf("TEST 5 FAILED [ %s ] [ %s ]\n", dnis_test5, user_part);
        failures++;
    }

    printf("ALL TEST COMPLETED PASSES [ %ld ] FAILURES [ %ld ]\n", passes, failures);

    return 0;
}

/* Get the user part from the complete dnis number
   0846372573@10.1.8.34 -> 0846372573 nul terminated */
static int g_get_dnis_user_part(const char *dnis, char *user_part, size_t size)
{
    size_t i = 0;
    int status = FALSE;

    /* Make room for the nul terminator */
    if(size > 1) {
        size--;
    }
    else {
        return status;
    }

    for(i = 0; i < size; i++) {
        /* Check for valid digit */
        if(isdigit(*dnis) != 0) {
            user_part[i] = *dnis;
        }
        else {
            if(*dnis == '@') {
                /* We are at the end */
                status = TRUE;
                break;
            }
            else {
                /* Not a digit or @ - corrupted dnis string */
                status = FALSE;
                break;
            }
        }

        /* Next character */
        dnis++;
    }

    /* nul terminate the string */
    user_part[i++] = '\0';

    /* Status FALSE indicates that the @ was not found or possible corruption with dnis string */
    return status;
}

非常感谢任何建议,

5 个答案:

答案 0 :(得分:4)

不要过度优化。这是一个非常简单的函数,可以在足够小的数据集上运行,以适应缓存。它的运行速度几乎可以达到它的速度(假设优化的编译器标志等)。但更重要的是,这只是整个计划的一小部分。不要花费你所有的努力在汇编程序中重写它并仔细研究x86架构手册,以确保CPU管道保持完全填满,或者当你确定在其他地方有更多悬而未决的成果时。首先描述,然后优化分析器说你太慢的位置。

答案 1 :(得分:1)

替换

if(isdigit(*dnis) != 0)

if ( *dns>='0' && *dns<='9' )

如果您只关心十进制数字而不关心locales

虽然不重要,但会保存函数调用开销。 (你需要确认这是否会影响显着)除此之外我没有看到任何重大改变。

答案 2 :(得分:1)

我想我会更像这样编写解析器:

static int g_get_dnis_user_part2(const char *dnis, char *user_part, size_t size)
{
    if (size == 0)
        return FALSE;

    size_t i;

    for (i=0; i<size-1 && isdigit(dnis[i]); i++)
        user_part[i] = dnis[i];
    user_part[i] = '\0';
    return (dnis[i] == '@') ? TRUE : FALSE;
}

如果你真的想要,你也可以将isdigit的呼叫更改为类似my_isdigit的呼叫,你可以实现这样的目的:

int my_isdigit(int input) {
    static char table[UCHAR_MAX+1];
    bool inited;

    if (!inited) 
        for (int i='0'; i<'9'; i++)
            table[i] = 1;

    return table[input];
}

我试图保持干净,但为了使它更快一点,明确(和单独)进行初始化,这样你就不会检查它是否已经初始化每个字符。 (但是有了不错的分支预测,这不会有太大的收获)。

除此之外,正如其他人已经提到过的那样,我会更改TRUEFALSE的定义 - 您使用的定义会让我觉得非常糟糕。通常情况下,FALSE = 0且TRUE = 1,并且看不到改变它们的地方已经获得了任何有用的东西。

答案 3 :(得分:1)

我基本上同意bdonlan,如果你有疑问,不要过度优化和衡量。我甚至会比这更进一步。如果我正确理解您的算法,您要做的是检查字符串的初始段是否为十进制字符,然后检查以下字符是否为@

  • strspn可以随时查看整个角色,只需使用它。
  • 检查'@'
  • 的以下字符

就是这样。

gcc内置了strspn,我不认为你可以做得更好,因为你的运行瓶颈就是从内存中汲取所有字符串。一旦他们进入缓存,你就不会感到太大的不同。

答案 4 :(得分:-1)

也许您可以从展开循环中受益。它不会太漂亮,但像这样的东西应该可以工作(未经测试):

#define CHECKDIGIT(d, user_part, status) \
do {\
    if(isdigit(*(d)) != 0) {    \
        *(user_part)++ = *(d)++;\
    }                           \
    else {                      \
        if(*(d) == '@') {       \
            (status) = TRUE;    \
            goto finish;        \
        }                       \
        else {                  \
            (status) = FALSE;   \
            goto finish;        \
        }                       \
    }                           \
} while(0)

static int g_get_dnis_user_part(const char *dnis, char *user_part, size_t size)
{
    size_t i = 0;
    int status = FALSE;
    int chunks, rem;

    /* Make room for the nul terminator */
    if(size > 1) {
        size--;
    }
    else {
        return status;
    }

    // Divide size in chunks of 8
    chunks = size >> 3;
    rem = size & 0x7;

    for(i = 0; i < chunks; i++) {
        /* Check for valid digit */
        CHECKDIGIT(dnis, user_part);
        CHECKDIGIT(dnis, user_part);
        CHECKDIGIT(dnis, user_part);
        CHECKDIGIT(dnis, user_part);
        CHECKDIGIT(dnis, user_part);
        CHECKDIGIT(dnis, user_part);
        CHECKDIGIT(dnis, user_part);
        CHECKDIGIT(dnis, user_part);
    }
    for(i = 0; i < rem; i++) {
        CHECKDIGIT(dnis, user_part);
    }

    finish:
    /* nul terminate the string */
    user_part[i++] = '\0';

    /* Status FALSE indicates that the @ was not found or possible corruption with     dnis string */
    return status;
}