I was hanging out in my profiler for a while trying to figure out how to speed up a common log parser which was bottlenecked around the date parsing, and I tried various algorithms to speed things up.
The thing I tried that was fastest for me was also by far the most readable, but potentially non-standard C.
This worked quite well in GCC, icc, and my really old and picky SGI compiler. As it's a quite readable optimization, where doesn't it do what I want?
static int parseMonth(const char *input) { int rv=-1; int inputInt=0; int i=0; for(i=0; i<4 && input[i]; i++) { inputInt = (inputInt << 8) | input[i]; } switch(inputInt) { case 'Jan/': rv=0; break; case 'Feb/': rv=1; break; case 'Mar/': rv=2; break; case 'Apr/': rv=3; break; case 'May/': rv=4; break; case 'Jun/': rv=5; break; case 'Jul/': rv=6; break; case 'Aug/': rv=7; break; case 'Sep/': rv=8; break; case 'Oct/': rv=9; break; case 'Nov/': rv=10; break; case 'Dec/': rv=11; break; } return rv; }
This usually happens when you don't have C/GCC compiler installed on your server. All you got to do is to install gcc to resolve this. If using CentOS 8 then you can use the DNF command. Once installed, you can verify as below.
Compilation error refers to a state when a compiler fails to compile a piece of computer program source code, either due to errors in the code, or, more unusually, due to errors in the compiler itself. A compilation error message often helps programmers debugging the source code.
If the C++ compiler provides its own versions of the C headers, the versions of those headers used by the C compiler must be compatible. Oracle Developer Studio C and C++ compilers use compatible headers, and use the same C runtime library. They are fully compatible.
C is a mid-level language and it needs a compiler to convert it into an executable code so that the program can be run on our machine.
Solaris 10 - SPARC - SUN Compiler.
Test code:
#include <stdio.h> static int parseMonth(const char *input) { int rv=-1; int inputInt=0; int i=0; for(i=0; i<4 && input[i]; i++) { inputInt = (inputInt << 8) | input[i]; } switch(inputInt) { case 'Jan/': rv=0; break; case 'Feb/': rv=1; break; case 'Mar/': rv=2; break; case 'Apr/': rv=3; break; case 'May/': rv=4; break; case 'Jun/': rv=5; break; case 'Jul/': rv=6; break; case 'Aug/': rv=7; break; case 'Sep/': rv=8; break; case 'Oct/': rv=9; break; case 'Nov/': rv=10; break; case 'Dec/': rv=11; break; } return rv; } static const struct { char *data; int result; } test_case[] = { { "Jan/", 0 }, { "Feb/", 1 }, { "Mar/", 2 }, { "Apr/", 3 }, { "May/", 4 }, { "Jun/", 5 }, { "Jul/", 6 }, { "Aug/", 7 }, { "Sep/", 8 }, { "Oct/", 9 }, { "Nov/", 10 }, { "Dec/", 11 }, { "aJ/n", -1 }, }; #define DIM(x) (sizeof(x)/sizeof(*(x))) int main(void) { size_t i; int result; for (i = 0; i < DIM(test_case); i++) { result = parseMonth(test_case[i].data); if (result != test_case[i].result) printf("!! FAIL !! %s (got %d, wanted %d)\n", test_case[i].data, result, test_case[i].result); } return(0); }
Results (GCC 3.4.2 and Sun):
$ gcc -O xx.c -o xx xx.c:14:14: warning: multi-character character constant xx.c:15:14: warning: multi-character character constant xx.c:16:14: warning: multi-character character constant xx.c:17:14: warning: multi-character character constant xx.c:18:14: warning: multi-character character constant xx.c:19:14: warning: multi-character character constant xx.c:20:14: warning: multi-character character constant xx.c:21:14: warning: multi-character character constant xx.c:22:14: warning: multi-character character constant xx.c:23:14: warning: multi-character character constant xx.c:24:14: warning: multi-character character constant xx.c:25:14: warning: multi-character character constant $ ./xx $ cc -o xx xx.c $ ./xx !! FAIL !! Jan/ (got -1, wanted 0) !! FAIL !! Feb/ (got -1, wanted 1) !! FAIL !! Mar/ (got -1, wanted 2) !! FAIL !! Apr/ (got -1, wanted 3) !! FAIL !! May/ (got -1, wanted 4) !! FAIL !! Jun/ (got -1, wanted 5) !! FAIL !! Jul/ (got -1, wanted 6) !! FAIL !! Aug/ (got -1, wanted 7) !! FAIL !! Sep/ (got -1, wanted 8) !! FAIL !! Oct/ (got -1, wanted 9) !! FAIL !! Nov/ (got -1, wanted 10) !! FAIL !! Dec/ (got -1, wanted 11) $
Note that the last test case still passed - that is, it generated a -1.
Here's a revised - more verbose - version of parseMonth() which does work the same under both GCC and Sun C compiler:
#include <stdio.h> /* MONTH_CODE("Jan/") does not reduce to an integer constant */ #define MONTH_CODE(x) ((((((x[0]<<8)|x[1])<<8)|x[2])<<8)|x[3]) #define MONTH_JAN (((((('J'<<8)|'a')<<8)|'n')<<8)|'/') #define MONTH_FEB (((((('F'<<8)|'e')<<8)|'b')<<8)|'/') #define MONTH_MAR (((((('M'<<8)|'a')<<8)|'r')<<8)|'/') #define MONTH_APR (((((('A'<<8)|'p')<<8)|'r')<<8)|'/') #define MONTH_MAY (((((('M'<<8)|'a')<<8)|'y')<<8)|'/') #define MONTH_JUN (((((('J'<<8)|'u')<<8)|'n')<<8)|'/') #define MONTH_JUL (((((('J'<<8)|'u')<<8)|'l')<<8)|'/') #define MONTH_AUG (((((('A'<<8)|'u')<<8)|'g')<<8)|'/') #define MONTH_SEP (((((('S'<<8)|'e')<<8)|'p')<<8)|'/') #define MONTH_OCT (((((('O'<<8)|'c')<<8)|'t')<<8)|'/') #define MONTH_NOV (((((('N'<<8)|'o')<<8)|'v')<<8)|'/') #define MONTH_DEC (((((('D'<<8)|'e')<<8)|'c')<<8)|'/') static int parseMonth(const char *input) { int rv=-1; int inputInt=0; int i=0; for(i=0; i<4 && input[i]; i++) { inputInt = (inputInt << 8) | input[i]; } switch(inputInt) { case MONTH_JAN: rv=0; break; case MONTH_FEB: rv=1; break; case MONTH_MAR: rv=2; break; case MONTH_APR: rv=3; break; case MONTH_MAY: rv=4; break; case MONTH_JUN: rv=5; break; case MONTH_JUL: rv=6; break; case MONTH_AUG: rv=7; break; case MONTH_SEP: rv=8; break; case MONTH_OCT: rv=9; break; case MONTH_NOV: rv=10; break; case MONTH_DEC: rv=11; break; } return rv; } static const struct { char *data; int result; } test_case[] = { { "Jan/", 0 }, { "Feb/", 1 }, { "Mar/", 2 }, { "Apr/", 3 }, { "May/", 4 }, { "Jun/", 5 }, { "Jul/", 6 }, { "Aug/", 7 }, { "Sep/", 8 }, { "Oct/", 9 }, { "Nov/", 10 }, { "Dec/", 11 }, { "aJ/n", -1 }, { "/naJ", -1 }, }; #define DIM(x) (sizeof(x)/sizeof(*(x))) int main(void) { size_t i; int result; for (i = 0; i < DIM(test_case); i++) { result = parseMonth(test_case[i].data); if (result != test_case[i].result) printf("!! FAIL !! %s (got %d, wanted %d)\n", test_case[i].data, result, test_case[i].result); } return(0); }
I wanted to use MONTH_CODE() but the compilers did not cooperate.
if ( !input[0] || !input[1] || !input[2] || input[3] != '/' ) return -1; switch ( input[0] ) { case 'F': return 1; // Feb case 'S': return 8; // Sep case 'O': return 9; // Oct case 'N': return 10; // Nov case 'D': return 11; // Dec; case 'A': return input[1] == 'p' ? 3 : 7; // Apr, Aug case 'M': return input[2] == 'r' ? 2 : 4; // Mar, May default: return input[1] == 'a' ? 0 : (input[2] == 'n' ? 5 : 6); // Jan, Jun, Jul }
Slightly less readable and not so much validating, but perhaps even faster, no?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With