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;
}
非常感谢任何建议,
答案 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];
}
我试图保持干净,但为了使它更快一点,明确(和单独)进行初始化,这样你就不会检查它是否已经初始化每个字符。 (但是有了不错的分支预测,这不会有太大的收获)。
除此之外,正如其他人已经提到过的那样,我会更改TRUE
和FALSE
的定义 - 您使用的定义会让我觉得非常糟糕。通常情况下,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;
}