Geeks With Blogs
Greg Young Greg.ToString()

Alois has put up a contest for getting the fastest Time format function http://geekswithblogs.net/akraus1/archive/2006/04/23/76146.aspx

Submitted by Bruce Dunwiddie

private static string FormatFast(DateTime time)
{
char[] dateData = new char[12];
dateData[0] = (char)(time.Hour / 10 + '0');
dateData[1] = (char)(time.Hour % 10 + '0');
dateData[2] = ':';
dateData[3] = (char)(time.Minute / 10 + '0');
dateData[4] = (char)(time.Minute % 10 + '0');
dateData[5] = ':';
dateData[6] = (char)(time.Second / 10 + '0');
dateData[7] = (char)(time.Second % 10 + '0');
dateData[8] = '.';
dateData[9] = (char)(time.Millisecond / 100 + '0');
dateData[10] = (char)(time.Millisecond / 10 % 10 + '0');
dateData[11] = (char)(time.Millisecond % 10 + '0');
return new string(dateData);
}

This is a good start but can be optimized alot further ...

        private unsafe static string FormatFast3(DateTime time)
        {
           char[] dateData = new char[12]; 
            fixed (char* p = dateData)
            {
                long ticks = time.Ticks;
                char* a = p;
                int hour = (int)((ticks / 0x861c46800L)) % 24;
                int minute = (int)((ticks / 0x23c34600L)) % 60;
                int second = (int)(((ticks / 0x989680L)) % 60L);
                int ms = (int)(((ticks / 0x2710L)) % 0x3e8L);
                *a = (char)(hour / 10 + '0');
                a++;
                *a = (char)(hour % 10 + '0');
                a++;
                *a = ':';
                a++;
                *a = (char)(minute / 10 + '0');
                a++;
                *a = (char)(minute % 10 + '0');
                a++;
                *a = ':';
                a++;
                *a = (char)(second / 10 + '0');
                a++;
                *a = (char)(second % 10 + '0');
                a++;
                *a = '.';
                a++;
                *a = (char)(ms / 100 + '0');
                a++;
                *a = (char)(ms / 10 % 10 + '0');
                a++;
                *a = (char)(ms % 10 + '0');
            }
            return new String(dateData);
        }

The huge time saver was only getting ticks as a property then calculating the rest myself (in particular since these are calculated properties and the above code calls into them multiple times). The other major change was to use unsafe array access which turned out to be a little bit faster (the aray access comes out to be arraybase + (index * typesize) where as the unsafe is location + constant. Also I used a little bit of trickery on the hours and minutes calculations by converting to an int before the modulus thus running the modulus on 32 bit values instead of on 64 bit.When all is said and done here are my results for 10 million operations (averaged over 10 runs).

With StringFormat 00:00:29.0796462
With StringFormat 00:00:27.7783141
With FastFormat 00:00:13.2886650
With FastFormat3 00:00:05.8365522

        private unsafe static string FormatFast4(DateTime time)
        {
            char[] dateData = { ' ', ' ', ':', ' ', ' ', ':', ' ', ' ', '.', ' ', ' ', ' ' };
            fixed (char* p = dateData)
            {
                long ticks = time.Ticks;
                char* a = p;
                int hour = (int)((ticks / 0x861c46800L)) % 24;
                int minute = (int)((ticks / 0x23c34600L)) % 60;
                int second = (int)(((ticks / 0x989680L)) % 60L);
                int ms = (int)(((ticks / 0x2710L)) % 0x3e8L);
                *a = (char)(hour / 10 + '0');
                a++;
                *a = (char)(hour % 10 + '0');
                a+=2;
                *a = (char)(minute / 10 + '0');
                a++;
                *a = (char)(minute % 10 + '0');
                a+=2;
                *a = (char)(second / 10 + '0');
                a++;
                *a = (char)(second % 10 + '0');
                a+=2;
                *a = (char)(ms / 100 + '0');
                a++;
                *a = (char)(ms / 10 % 10 + '0');
                a++;
                *a = (char)(ms % 10 + '0');
            }
            return new String(dateData);
        }

Appears to be a little bit faster even!! down to .6 seconds for 1 million on my machine WITH recreating the char array for every call (other metrics were with a static array).

Posted on Monday, April 24, 2006 2:41 AM Under the covers | Back to top


Comments on this post: Fun a'la Alois

# re: Fun contest by Alois
Requesting Gravatar...
Very good idea Greg I have checked the results and must say that they are really impressive. Until somebody other has an even faster function I declare you as the winner for finding the fastest string function known to C# programmers.
Left by Alois Kraus on Apr 24, 2006 12:15 PM

# re: Fun a'la Alois
Requesting Gravatar...
Can you explain the reasoning behind the double referencing of p to a?
Left by Bruce Dunwiddie on Apr 24, 2006 2:50 PM

# re: Fun a'la Alois
Requesting Gravatar...
Yes ...

The double referencing is required when you use a "fixed" statement which pins the memory (prevents garbage collector from moving it) it references it as a readonly variable ... since I am writing to the variable (pointer math) I have to use a secondary read/write variable.
Left by Greg Young on Apr 24, 2006 3:22 PM

# re: Fun a'la Alois
Requesting Gravatar...
Hi Greg there is still somebody out there who was smarter. He did beat your function by a factor 2.6! I have just updated the article. This time it is really tough numerical number crunching.

http://geekswithblogs.net/akraus1/archive/2006/04/23/76146.aspx

Yours,
Alois Kraus
Left by Alois Kraus on Apr 26, 2006 3:18 PM

Your comment:
 (will show your gravatar)


Copyright © Greg Young | Powered by: GeeksWithBlogs.net