EncodeDateTime performance - Mailing list pgsql-hackers

From George McCollister
Subject EncodeDateTime performance
Date
Msg-id 48CE9BA8.3090305@novatech-llc.com
Whole thread Raw
Responses Re: EncodeDateTime performance  (Heikki Linnakangas <heikki.linnakangas@enterprisedb.com>)
List pgsql-hackers
I'm trying to optimize postgres performance on a headless solid state
hardware platform (no fans or disks). I have the database stored on a
USB 2.0 flash drive (hdparm benchmarks reads at 10 MB/s). Performance is
limited by the 533Mhz CPU.

Hardware:
IXP425 XScale (big endian) 533Mhz 64MB RAM
USB 2.0 Flash Drive

Software:
Linux 2.6.21.4
postgres 8.2.5

I created a fresh database using initdb, then added one table.

Here is the create table:
CREATE TABLE archivetbl
(
  "DateTime" timestamp without time zone,
  "StationNum" smallint,
  "DeviceDateTime" timestamp without time zone,
  "DeviceNum" smallint,
  "Tagname" character(64),
  "Value" double precision,
  "Online" boolean
)
WITH (OIDS=FALSE);
ALTER TABLE archivetbl OWNER TO novatech;

I populated the table with 38098 rows.

I'm doing this simple query:
select * from archivetbl;

It takes 79 seconds to complete the query (when postgres is compiled
with -O2). I'm running the query from pgadmin3 over TCP/IP.

oprofile is showing that memset (via dopr) is using about 60% of the CPU. I traced back further and noticed most of the
usagewas coming from EncodeDateTime. 

I'm not quite sure why oprofile is showing that memset is hogging so much CPU. Regardless, I found way to eliminate
mostof the sprintf calls that were taking place in my situation. 

I made some modifications to EncodeDateTime and have attached them as a patch. These changes alone reduced the query
timeof the "select *  from archivetbl;" from 79 seconds to just 35 seconds. 

This patch is against 8.2.5. Since I'm cross compiling changing versions is a bit of a pain, but if someone thinks the
performancehas changed much in this area I could probably get the latest version cross compiling. 

Regards,
George McCollister


diff -Naur postgresql-8.2.5/src/backend/utils/adt/datetime.c postgresql-8.2.5.new/src/backend/utils/adt/datetime.c
--- postgresql-8.2.5/src/backend/utils/adt/datetime.c    2007-06-12 10:58:39.000000000 -0500
+++ postgresql-8.2.5.new/src/backend/utils/adt/datetime.c    2008-09-15 12:16:32.000000000 -0500
@@ -3287,6 +3287,53 @@
     return TRUE;
 }    /* EncodeTimeOnly() */

+void ymdhm(char * buf, int year, int mon, int day, int hour, int min)
+{
+    buf[0] = (char)((year / 1000) % 10) + '0';
+    buf[1] = (char)((year / 100) % 10) + '0';
+    buf[2] = (char)((year / 10) % 10) + '0';
+    buf[3] = (char)(year % 10) + '0';
+    buf[4] = '-';
+    buf[5] = (char)((mon / 10) % 10) + '0';
+    buf[6] = (char)(mon % 10) + '0';
+    buf[7] = '-';
+    buf[8] = (char)((day / 10) % 10) + '0';
+    buf[9] = (char)(day % 10) + '0';
+    buf[10] = ' ';
+    buf[11] = (char)((hour / 10) % 10) + '0';
+    buf[12] = (char)(hour % 10) + '0';
+    buf[13] = ':';
+    buf[14] = (char)((min / 10) % 10) + '0';
+    buf[15] = (char)(min % 10) + '0';
+    buf[16] = '\0';
+}
+
+void append_seconds(char * buf, int sec)
+{
+    buf[0] = ':';
+    buf[1] = (char)((sec / 10) % 10) + '0';
+    buf[2] = (char)(sec % 10) + '0';
+    buf[3] = '\0';
+}
+
+#ifdef HAVE_INT64_TIMESTAMP
+void append_seconds_and_fsecs(char * buf, int sec, fsec_t fsec)
+{
+    buf[0] = ':';
+    buf[1] = (char)((sec / 10) % 10) + '0';
+    buf[2] = (char)(sec % 10) + '0';
+    buf[3] = '.';
+    buf[4] = (char)((fsec / 100000) % 10) + '0';
+    buf[5] = (char)((fsec / 10000) % 10) + '0';
+    buf[6] = (char)((fsec / 1000) % 10) + '0';
+    buf[7] = (char)((fsec / 100) % 10) + '0';
+    buf[8] = (char)((fsec / 10) % 10) + '0';
+    buf[9] = (char)(fsec % 10) + '0';
+    buf[10] = '\0';
+}
+#endif
+
+

 /* EncodeDateTime()
  * Encode date and time interpreted as local time.
@@ -3315,9 +3362,8 @@
         case USE_ISO_DATES:
             /* Compatible with ISO-8601 date formats */

-            sprintf(str, "%04d-%02d-%02d %02d:%02d",
-                    (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
-                    tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
+            ymdhm(str, (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
+                tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);

             /*
              * Print fractional seconds if any.  The field widths here should
@@ -3329,7 +3375,7 @@
 #ifdef HAVE_INT64_TIMESTAMP
             if (fsec != 0)
             {
-                sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
+                append_seconds_and_fsecs(str + strlen(str), tm->tm_sec, fsec);
                 TrimTrailingZeros(str);
             }
 #else
@@ -3340,7 +3386,7 @@
             }
 #endif
             else
-                sprintf(str + strlen(str), ":%02d", tm->tm_sec);
+                append_seconds(str + strlen(str), tm->tm_sec);

             /*
              * tzp == NULL indicates that we don't want *any* time zone info

pgsql-hackers by date:

Previous
From: Robert Treat
Date:
Subject: Re: Transaction Snapshots and Hot Standby
Next
From: Magnus Hagander
Date:
Subject: Re: Parsing of pg_hba.conf and authentication inconsistencies