Skip to content

Commit

Permalink
Small code optimisations.
Browse files Browse the repository at this point in the history
  • Loading branch information
maxim2266 committed Sep 27, 2015
1 parent 2ba3365 commit 30184f7
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 93 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ _OS:_ Linux Mint 17.2 64bit

FIX message type | FIX specification | Validation | Average time to parse one message
----------------------------------|------------------------------------------|------------|----------------------------------
NewOrderSingle('D') | Hand-coded spec. for this message only | No | 0.326 µs/msg
NewOrderSingle('D') | Hand-coded spec. for this message only | Yes | 0.547 µs/msg
NewOrderSingle('D') | Compiled full spec. for FIX.4.4 | Yes | 0.739 µs/msg
MarketDataIncrementalRefresh('X') | Hand-coded spec. for this message only | Yes | 1.263 µs/msg
MarketDataIncrementalRefresh('X') | Compiled full spec. for FIX.4.4 | Yes | 1.443 µs/msg
NewOrderSingle('D') | Hand-coded spec. for this message only | No | 0.317 µs/msg
NewOrderSingle('D') | Hand-coded spec. for this message only | Yes | 0.554 µs/msg
NewOrderSingle('D') | Compiled full spec. for FIX.4.4 | Yes | 0.757 µs/msg
MarketDataIncrementalRefresh('X') | Hand-coded spec. for this message only | Yes | 1.223 µs/msg
MarketDataIncrementalRefresh('X') | Compiled full spec. for FIX.4.4 | Yes | 1.386 µs/msg

For more details see `doc/` directory of the project.
165 changes: 108 additions & 57 deletions src/converters.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,47 @@ fix_error copy_fix_tag_as_string(const fix_group* const group, unsigned tag, cha
return FE_OK;
}

// ascii digits to long converters
static
const char* convert_significant_digits(const char* s, long* const result)
{
long res = 0;
unsigned c = CHAR_TO_INT(*s) - '0';

if(c == 0)
return NULL;

if(c <= 9)
{
res = c;

for(c = CHAR_TO_INT(*++s) - '0'; c <= 9; c = CHAR_TO_INT(*++s) - '0')
{
const long t = res * 10 + c;

if(t < res) // overflow
return NULL;

res = t;
}
}

// done
*result = res;
return s;
}

static
const char* convert_digits(const char* s, long* const result)
{
// skip leading zeroes
while(*s == '0')
++s;

// convert digits
return convert_significant_digits(s, result);
}

// tag as long integer
fix_error get_fix_tag_as_long(const fix_group* const group, unsigned tag, long* const result)
{
Expand All @@ -70,31 +111,33 @@ fix_error get_fix_tag_as_long(const fix_group* const group, unsigned tag, long*
if(err != FE_OK)
return err;

// conversion
long val = 0;
const char* s = value.begin;
const int negative = (*s == '-') ? (++s, 1) : 0;
if(fix_string_length(value) > 20) // ???
RETURN( FE_INVALID_VALUE );

// quickly skip leading zeroes
while(*s == '0')
++s;
// sign
bool neg = false;

// convert all significant digits
while(*s >= '0' && *s <= '9')
if(*value.begin == '-')
{
const long new_val = val * 10L + *s++ - '0';
++value.begin;
neg = true;
}

if(new_val < val) // overflow
RETURN( FE_INVALID_VALUE );
// conversion
long val;

val = new_val;
}
value.begin = convert_digits(value.begin, &val);

if(*s != SOH || s == value.begin + negative) // no SOH terminator or the sign only
// validation
if(!value.begin || (neg && val == 0)) // overflow or '-0'
RETURN( FE_INVALID_VALUE );

if(value.begin < value.end) // unprocessed bytes
RETURN( FE_INCORRECT_VALUE_FORMAT );

// all clear
if(result)
*result = negative ? -val : val;
*result = neg ? -val : val;

return FE_OK;
}
Expand All @@ -118,62 +161,70 @@ fix_error get_fix_tag_as_double(const fix_group* const group, unsigned tag, doub
if(err != FE_OK)
return err;

// conversion
long val = 0, frac = 0;
unsigned ndig = 0; // number of significant digits
const char* s = value.begin;
const double sign = (*s == '-') ? (++s, -1.) : 1.;
const char* mark = s;
// sign
bool neg = false;

if(*value.begin == '-')
{
++value.begin;
neg = true;
}

// skip leading zeroes
while(*s == '0')
++s;
while(*value.begin == '0')
++value.begin;

// integer part
while(*s >= '0' && *s <= '9')
{
if(++ndig > 15)
RETURN( FE_INVALID_VALUE );
long int_part;
const char* s = convert_significant_digits(value.begin, &int_part);

val = val * 10L + *s++ - '0';
}
if(!s)
RETURN( FE_INVALID_VALUE );

if(s == mark) // cannot have empty integer part
RETURN( FE_INCORRECT_VALUE_FORMAT );
unsigned nsig = s - value.begin; // significant digits counter

// fractional part
if(nsig > 15)
RETURN( FE_INVALID_VALUE );

long frac_part = 0;
unsigned nfrac = 0;

if(*s == '.')
if(*s == '.' && *++s != SOH)
{
mark = ++s;
// fractional part
value.begin = s;
s = convert_digits(s, &frac_part);

if(ndig == 0)
while(*s == '0') // skip leading zeroes
++s;
if(!s)
RETURN( FE_INCORRECT_VALUE_FORMAT );

while(*s >= '0' && *s <= '9')
{
if(++ndig > 15)
RETURN( FE_INVALID_VALUE );
nfrac = s - value.begin;

frac = frac * 10L + *s++ - '0';
}

nfrac = s - mark;
if(nsig + nfrac > 15) // counting trailing zeros as significant, contrary to the definition
RETURN( FE_INCORRECT_VALUE_FORMAT );
}

if(*s != SOH)
// final checks
if(s < value.end) // unprocessed bytes
RETURN( FE_INCORRECT_VALUE_FORMAT );

static const double mult[] = { 0., 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, 1e-8, 1e-9, 1e-10, 1e-11, 1e-12, 1e-13, 1e-14, 1e-15 };
if(neg && int_part == 0 && frac_part == 0) // -0.0
RETURN( FE_INVALID_VALUE );

// compose result
static const double factor[] = { 0., 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, 1e-8, 1e-9, 1e-10, 1e-11, 1e-12, 1e-13, 1e-14, 1e-15 };

double res = (double)int_part;

if(frac_part != 0)
res += (double)frac_part * factor[nfrac];

if(neg)
res = -res;

// all done
if(result)
#ifdef FP_FAST_FMA
*result = copysign(fma((double)frac, mult[nfrac], (double)val), sign);
#else
*result = copysign(frac * mult[nfrac] + val, sign);
#endif
*result = res;

return FE_OK;
}
Expand Down Expand Up @@ -228,14 +279,14 @@ fix_error get_fix_tag_as_boolean(const fix_group* const group, unsigned tag, boo

// matchers (unsafe macros!)
#define READ_FIRST_DIGIT(s, r) \
switch(CHAR_TO_INT(*(s))) { \
switch(*(s)) { \
case '0' ... '9': (r) = *(s) - '0'; break; \
default: RETURN( FE_INCORRECT_VALUE_FORMAT ); \
} \
++(s)

#define READ_DIGIT(s, r) \
switch(CHAR_TO_INT(*(s))) { \
switch(*(s)) { \
case '0' ... '9': (r) = (r) * 10 + *(s) - '0'; break; \
default: RETURN( FE_INCORRECT_VALUE_FORMAT ); \
} \
Expand Down Expand Up @@ -309,7 +360,7 @@ fix_error read_time_part(const fix_group* const group, fix_string* const ps, utc
static
fix_error read_time_ms_part(const fix_group* const group, fix_string* const ps, utc_timestamp* const ts)
{
fix_error err = read_time_part(group, ps, ts);
const fix_error err = read_time_part(group, ps, ts);

if(err != FE_OK)
return err;
Expand Down Expand Up @@ -470,7 +521,7 @@ fix_error get_fix_tag_as_LocalMktDate(const fix_group* const group, unsigned tag
// FUNCTION EXPECTS A STRING IN THE "YYYY-MM-DD" FORMAT.

fix_string value;
fix_error err = get_fix_tag_as_string(group, tag, &value);
const fix_error err = get_fix_tag_as_string(group, tag, &value);

if(err != FE_OK)
return err;
Expand Down
29 changes: 10 additions & 19 deletions src/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,9 @@ unsigned read_uint(fix_parser* const parser, const char delim)

// assuming (s < parser->frame.end) and *(parser->frame.end - 1) == SOH

unsigned tag = CHAR_TO_INT(*s++) - '0';
unsigned res = CHAR_TO_INT(*s++) - '0';

if(tag == 0 || tag > 9) // leading zeroes are not allowed
if(res == 0 || res > 9) // leading zeroes are not allowed
goto ERROR_EXIT;

for(unsigned c = CHAR_TO_INT(*s++); c != CHAR_TO_INT(delim); c = CHAR_TO_INT(*s++))
Expand All @@ -128,16 +128,16 @@ unsigned read_uint(fix_parser* const parser, const char delim)
if(c > 9) // not a digit
goto ERROR_EXIT;

c = tag * 10 + c;
c = res * 10 + c;

if(c < tag) // overflow
if(c < res) // overflow
goto ERROR_EXIT;

tag = c;
res = c;
}

parser->result.error.context.end = parser->frame.begin = s;
return tag;
return res;

ERROR_EXIT:
parser->result.error.context.end = s;
Expand All @@ -157,17 +157,14 @@ unsigned next_tag(fix_parser* const parser)
}

// read tag
unsigned tag = read_uint(parser, '=');
const unsigned tag = parser->result.error.tag = read_uint(parser, '=');

if(tag == 0) // invalid tag
{
parser->result.error.code = FE_INVALID_TAG;
parser->result.error.tag = 0;
return 0;
}

parser->result.error.tag = tag;

if(parser->frame.begin == parser->frame.end) // empty tag value
{
parser->result.error.code = FE_EMPTY_VALUE;
Expand Down Expand Up @@ -206,14 +203,8 @@ unsigned read_uint_value(fix_parser* const parser)
{
const unsigned val = read_uint(parser, SOH);

if(val != 0)
{
parser->result.error.code = FE_OK;
return val;
}

parser->result.error.code = FE_INCORRECT_VALUE_FORMAT;
return 0;
parser->result.error.code = (val != 0) ? FE_OK : FE_INCORRECT_VALUE_FORMAT;
return val;
}

// read bytes to the first SOH, i.e., a FIX string
Expand Down Expand Up @@ -623,7 +614,7 @@ const fix_error_details* get_fix_group_error_details(const fix_group* const grou
}

// tag accessors ------------------------------------------------------------------------------------------
static
static inline
fix_error set_group_error(const fix_group* const group, unsigned tag, fix_error err)
{
set_error_ctx(group->error, err, tag, EMPTY_STR);
Expand Down
20 changes: 8 additions & 12 deletions src/scanner.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,24 +102,21 @@ static
unsigned char copy_cs(char* restrict p, const char* restrict s, unsigned n)
{
unsigned char cs = 0;
const char* const end = s + n;

#ifdef USE_SSE
if(n >= 16)
if(end - s >= 16)
{
__m128i cs128 = _mm_loadu_si128((const __m128i*)s);

s += 16;
_mm_storeu_si128((__m128i*)p, cs128);
p += 16;

while((n -= 16) >= 16)
for(s += 16, p += 16; end - s >= 16; s += 16, p += 16)
{
const __m128i tmp = _mm_loadu_si128((const __m128i*)s);

s += 16;
_mm_storeu_si128((__m128i*)p, tmp);
p += 16;
cs128 = _mm_add_epi8(cs128, tmp);
_mm_storeu_si128((__m128i*)p, tmp);
}

cs128 = _mm_add_epi8(cs128, _mm_srli_si128(cs128, 8));
Expand All @@ -129,22 +126,21 @@ unsigned char copy_cs(char* restrict p, const char* restrict s, unsigned n)
cs += _mm_extract_epi16(cs128, 0); // SSE4: _mm_extract_epi8 ?
}

if(n >= 8)
if(end - s >= 8)
{
__m128i cs64 = _mm_loadl_epi64((const __m128i*)s);

_mm_storel_epi64((__m128i*)p, cs64);
s += 8;
p += 8;
cs64 = _mm_add_epi8(cs64, _mm_srli_si128(cs64, 4));
cs64 = _mm_add_epi8(cs64, _mm_srli_si128(cs64, 2));
cs64 = _mm_add_epi8(cs64, _mm_srli_si128(cs64, 1));
cs += _mm_extract_epi16(cs64, 0); // SSE4: _mm_extract_epi8 ?
p += 8;
s += 8;
n -= 8;
}
#endif // #ifdef USE_SSE

while(n-- > 0)
while(s < end)
cs += (*p++ = *s++);

return cs;
Expand Down

0 comments on commit 30184f7

Please sign in to comment.