Q about ctype.narrow

Martin Sebor sebor@roguewave.com
Wed Nov 26 17:34:00 GMT 2003


Jerry Quinn wrote:

...
> I think optimizing the array version will be harder.  The catch of
> course is the need to test whether we have a cached value - again
> defeating some of the speed advantage of the loop.  So the candidate
> function looks like:
> 
> char* ctype<char>::narrow(char* lo, char* hi, char default, char* dest)
> {
>   for (; lo != hi; ++dest,++lo) {
>     *dest = table[*lo] ? table[*lo] : do_narrow(*lo, default);
>   }
>   return hi;
> }
> 
> This assumes we prefill the table rather than fill on demand.  Now
> compare to the current version of array do_narrow():
> 
> char* ctype<char>::do_narrow(char* lo, char* hi, char default, char* dest)
> {
>   memcpy(dest, lo, hi-lo);
>   return hi;
> }
> 
> So the question is whether a virtual call followed by call to memcpy
> is faster or slower than the first loop doing a test every iteration.
> I'm not considering the case where do_narrow gets called in the first
> function, since that would only happen in a derived class.
> 
> I'll have to benchmark it...

I suspect this will be a No Contest :) (I.e., the virtual call will
be a lot faster than the loop with the conditional in the general
case).

But I think there is quite a bit of room for improvement. In the
common case, the cache will be fully populated (i.e., no unfilled
slots) with consecutive values from 0 through UCHAR_MAX, so the
loop can be replaced with a memcpy:

inline char*
ctype<char>::narrow(char* lo, char* hi, char dfault, char* dest)
{
   if (cache_consecutive)
       memcpy (dest, lo, hi - lo);
   else
       __narrow (lo, hi, dfault, dest);
   return hi;
}

/* out-of-line */ char*
ctype<char>::__narrow(char* lo, char* hi, char dfault, char* dest)
{
   if (cache_empty) {
        const char s[] = { 0, ..., UCHAR_MAX };
        do_narrow (s, s + sizeof s, 0, cache);
        cache_consecutive = !memchr (cache + 1, 0, sizeof cache - 1);
        cache_empty = false;
   }

   if (cache_consecutive)
       memcpy (dest, lo, hi - lo);
   else {
       for (; lo != hi; ++lo, ++dest)
           *dest = table [*lo] ? table [*lo] : dfault;
   }

   return hi;
}

Martin




More information about the Libstdc++ mailing list