// ============================================================================
// This is a Servlet sample for the G-WAN Web Server (http://www.trustleap.com)
// ----------------------------------------------------------------------------
// bench.c: How much time should it take to format a RFC-1123 HTTP date string
//          like "Tue, 06 Jan 2009 06:12:20 GMT"?
//
// We were wondering how G-WAN C servlets are doing in comparison to the system
// Wininet InternetTimeFromSystemTime() Windows API call:
//
//   function	    max   average   min
// ------------  ------  -------  -----
//  WinInet API   9,426    9,292  9,194  (here, on average, Windows's WinInet
//   time2rfc()     638      606    578   is 15x slower than a G-WAN servlet)
//
// On this 3GHz CPU, 1 milisecond  = 3,000,000 clock cycles
//                   1 microsecond =     3,000 clock cycles
//                   1 nanosecond  =         3 clock cycles: 1 cycle=.3 nanosec
//
// When we zipped a folder during the test we got more interesting results:
// (because concurrent tasks compete for the CPU caches)
//
//   function	    max   average   min
// ------------  ------  -------  -----
//  WinInet API  70,450	  19,723  9,590  (here, on average, Microsoft's code
//   time2rfc()     874      650    554   is 30x slower than a G-WAN servlet)
//
// This simple case illustrates how high G-WAN can leverage Windows computers 
// -and how much your developments will benefit from using G-WAN.
//
// ============================================================================
#include <windows.h>
#include "xbuffer.h" // G-WAN dynamic buffers

// Title of our HTML page
static u8 title[]="Benchmarking function calls (with CPU clock cycles)";

// Top of our HTML page
static u8 top[]=
     "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">"
     "<html lang=\"en\"><head><title>%s</title><meta http-equiv"
     "=\"Content-Type\" content=\"text/html; charset=utf-8\">"
//   "<style=\"margin: 10; padding: 10;\" type=\"text/css\">"
     "<link href=\"imgs/style.css\" rel=\"stylesheet\" type=\"text/css\">"
     "</head><body><h1>%s</h1>";

// ----------------------------------------------------------------------------
// imported functions:
//   get_reply(): get a pointer on the 'reply' dynamic buffer from the server
//   set_reply(): send back the 'reply' dynamic buffer's pointer to the server
//    time2rfc(): like httpdate(), but faster and thread-safe
//    cycles64(): return the number of elapsed CPU clock cycles (64-bit)
//    xbuf_cat(): like strcat(), but in the specified dynamic buffer 
//   xbuf_xcat(): formatted strcat() (a la printf) in a given dynamic buffer 
// ----------------------------------------------------------------------------
int main(int argc, char *argv[])
{
   u8  date[32], *p;
   __int64 start, overh;
   u32 i, rounds=10,
       ms_min=0xffffffff, ms_ave=0, ms_max=0,
       sv_min=0xffffffff, sv_max=0, sv_ave=0,
       t=time(0), z=1;

   // create a dynamic buffer and get a pointer on the server response buffer
   xbuf_ctx reply; get_reply(argv, &reply);

   memset(date, 0, sizeof(date)-1);

   // ---- format the top of our HTML page with a title
   xbuf_xcat(&reply, top, title, title);

   // ---- find the CPU clock cycles count overhead so we can remove it later
   i=rounds; while(i--) start=cycles64(), overh=cycles64()-(start+z), overh--;

   // -------------------------------------------------------------------------
   // benchmark the "Wininet API" function call
   // -------------------------------------------------------------------------
do{  
   char      *p;
   SYSTEMTIME st;
   typedef u32(WINAPI *pHTTPTIME)(const SYSTEMTIME *pst, u32 dwRFC,
                                  char *lpszTime, u32 cbTime);
   pHTTPTIME pHttpTime=0;
   HMODULE   hlib=LoadLibrary("wininet.dll");
   pHttpTime=(pHTTPTIME)GetProcAddress(hlib, "InternetTimeFromSystemTime");
   if(!pHttpTime) 
   {
      xbuf_cat(&reply, "Can't load InternetTimeFromSystemTime<br>");
      break;
   }

   // check that all function calls provide similar results
   GetSystemTime(&st); // GetSystemTime(&st); is *much* slower than time(0);
   pHttpTime(&st, 0, date, sizeof(date));
   xbuf_xcat(&reply, "<br>WinInet API call: %s<br>", date); 

   xbuf_cat(&reply, "<br><table class=\"clean\" width=160px>"
                    "<tr><th>function</th><th>time</th></tr>");

   // warm the cpu cache to get consistent values
   i=rounds*2; while(i--) start=cycles64(), 
                          p=pHttpTime(&st, 0, date, sizeof(date));
   i=rounds; // now time it
   while(i--)
   {
      start=cycles64();
      pHttpTime(&st, 0, date, sizeof(date));
      start=cycles64()-(start+overh);
      if(ms_min>start) ms_min=start;
      if(ms_max<start) ms_max=start;
      ms_ave+=start;
      xbuf_xcat(&reply, 
                "<tr class=\"d%u\"><td>WinInet API call</td><td>%u</td></tr>",
                !(i&1), start);
   }
   ms_ave/=rounds; xbuf_cat(&reply, "</table>");
   FreeLibrary(hlib);
}while(0);

   // -------------------------------------------------------------------------
   // benchmark the "time2rfc(t, date);" function call
   // -------------------------------------------------------------------------
   // check that all function calls provide similar results
   xbuf_xcat(&reply, "<br>C servlet time2rfc(): %s<br>", time2rfc(t, date)); 

   xbuf_cat(&reply, "<br><table class=\"clean\" width=160px>"
                    "<tr><th>function</th><th>time</th></tr>");

   // warm the cpu cache to get consistent values
   i=rounds*2; while(i--) start=cycles64(), p=time2rfc(t, date);

   // 32-bit time stamp counter overflow period on a 1GHz CPU:
   // 2^32 cycles * (1 second/1,000,000,000 cycles) = 4.29 seconds
   i=rounds;
   while(i--)
   {
      start=cycles64();
      time2rfc(t, date);
      start=cycles64()-(start+overh);
      if(sv_min>start) sv_min=start;
      if(sv_max<start) sv_max=start;
      sv_ave+=start;
      xbuf_xcat(&reply, 
                "<tr class=\"d%u\"><td>time2rfc()</td><td>%u</td></tr>",
                !(i&1), start);
   }
   sv_ave/=rounds; xbuf_cat(&reply, "</table>");

   // ---- write how many CPU cycles these operations took
   xbuf_xcat(&reply, "<br><table class=\"clean\" width=440px>"
   "<tr><th>function</th><th>max</th><th>average</th><th>min</th></tr>"
   "<tr class=\"d1\"><td>WinInet API call</td><td>%u</td><td>%u</td><td>%u</td>"
   "<tr class=\"d0\"><td>time2rfc()</td><td>%u</td><td>%u</td><td>%u</td></tr>"
   "</tr></table><table class=\"clean\" width=440px>"
   "<tr><th>TrustLeap's code scaled <font color=#f0f000>%.02f</font> times "
   "better</th></tr></table>",
   ms_max, ms_ave, ms_min, 
   sv_max, sv_ave, sv_min, (double)ms_ave/(double)sv_ave);

   // ---- close our HTML page
   xbuf_cat(&reply, "<br></body></html>");

   // confirm the reply's dynamic buffer address and size to the server
   // (they have changed when more memory is allocated during formatting)
   set_reply(argv, &reply); return(200); // return an HTTP code (200:'OK')
}
// ============================================================================
// End of Source Code
// ============================================================================
