Commit afe1ef5e authored by Vladislav Vaintroub's avatar Vladislav Vaintroub

LP1008334 : Speedup specific datetime queries that got slower with...

LP1008334 : Speedup specific datetime queries that got slower with introduction of microseconds in 5.3

- Item::get_seconds() now skips decimal arithmetic, if decimals is 0. This significantly speeds up from_unixtime() if no fractional part is passed.
- replace sprintfs used to format temporal values  by hand-coded formatting 
  
Query1 (original query in the bug report)
BENCHMARK(10000000,DATE_SUB(FROM_UNIXTIME(RAND() * 2147483648), INTERVAL (FLOOR(1 + RAND() * 365)) DAY)) 
  
Query2 (Variation of query1 that does not use fractional part in FROM_UNIXTIME parameter)
BENCHMARK(10000000,DATE_SUB(FROM_UNIXTIME(FLOOR(RAND() * 2147483648)), INTERVAL (FLOOR(1 + RAND() * 365)) DAY)) 
  
Prior to the patch, the runtimes were (32 bit compilation/AMD machine)
Query1: 41.53 sec 
Query2: 23.90 sec
  
With the patch, the runtimes are
Query1: 32.32 sec (speed up due to removing sprintf)
Query2: 12.06 sec (speed up due to skipping decimal arithmetic)
parent e326a389
...@@ -1043,6 +1043,27 @@ void set_zero_time(MYSQL_TIME *tm, enum enum_mysql_timestamp_type time_type) ...@@ -1043,6 +1043,27 @@ void set_zero_time(MYSQL_TIME *tm, enum enum_mysql_timestamp_type time_type)
} }
/*
Helper function for datetime formatting.
Format number as string, left-padded with 0.
The reason to use own formatting rather than sprintf() is performance - in a
datetime benchmark it helped to reduced the datetime formatting overhead
from ~30% down to ~4%.
*/
static char* fmt_number(uint val, char *out, uint digits)
{
uint i;
for(i= 0; i < digits; i++)
{
out[digits-i-1]= '0' + val%10;
val/=10;
}
return out + digits;
}
/* /*
Functions to convert time/date/datetime value to a string, Functions to convert time/date/datetime value to a string,
using default format. using default format.
...@@ -1056,42 +1077,84 @@ void set_zero_time(MYSQL_TIME *tm, enum enum_mysql_timestamp_type time_type) ...@@ -1056,42 +1077,84 @@ void set_zero_time(MYSQL_TIME *tm, enum enum_mysql_timestamp_type time_type)
int my_time_to_str(const MYSQL_TIME *l_time, char *to, uint digits) int my_time_to_str(const MYSQL_TIME *l_time, char *to, uint digits)
{ {
ulong day= (l_time->year || l_time->month) ? 0 : l_time->day; uint day= (l_time->year || l_time->month) ? 0 : l_time->day;
uint hour= day * 24 + l_time->hour;
char*pos= to;
if (digits == AUTO_SEC_PART_DIGITS) if (digits == AUTO_SEC_PART_DIGITS)
digits= l_time->second_part ? TIME_SECOND_PART_DIGITS : 0; digits= l_time->second_part ? TIME_SECOND_PART_DIGITS : 0;
DBUG_ASSERT(digits <= TIME_SECOND_PART_DIGITS); DBUG_ASSERT(digits <= TIME_SECOND_PART_DIGITS);
return sprintf(to, if(l_time->neg)
digits ? "%s%02lu:%02u:%02u.%0*lu" *pos++= '-';
: "%s%02lu:%02u:%02u",
(l_time->neg ? "-" : ""), if(hour > 99)
day * 24L + l_time->hour, l_time->minute, l_time->second, /* Need more than 2 digits for hours in string representation. */
digits, (ulong)sec_part_shift(l_time->second_part, digits)); pos= longlong10_to_str((longlong)hour, pos, 10);
else
pos= fmt_number(hour, pos, 2);
*pos++= ':';
pos= fmt_number(l_time->minute, pos, 2);
*pos++= ':';
pos= fmt_number(l_time->second, pos, 2);
if (digits)
{
*pos++= '.';
pos= fmt_number((uint)sec_part_shift(l_time->second_part, digits),
pos, digits);
}
*pos= 0;
return (int) (pos-to);
} }
int my_date_to_str(const MYSQL_TIME *l_time, char *to) int my_date_to_str(const MYSQL_TIME *l_time, char *to)
{ {
return my_sprintf(to, (to, "%04u-%02u-%02u", char *pos=to;
l_time->year, pos= fmt_number(l_time->year, pos, 4);
l_time->month, *pos++='-';
l_time->day)); pos= fmt_number(l_time->month, pos, 2);
*pos++='-';
pos= fmt_number(l_time->day, pos, 2);
*pos= 0;
return (int)(pos - to);
} }
int my_datetime_to_str(const MYSQL_TIME *l_time, char *to, uint digits) int my_datetime_to_str(const MYSQL_TIME *l_time, char *to, uint digits)
{ {
char *pos= to;
if (digits == AUTO_SEC_PART_DIGITS) if (digits == AUTO_SEC_PART_DIGITS)
digits= l_time->second_part ? TIME_SECOND_PART_DIGITS : 0; digits= l_time->second_part ? TIME_SECOND_PART_DIGITS : 0;
DBUG_ASSERT(digits <= TIME_SECOND_PART_DIGITS); DBUG_ASSERT(digits <= TIME_SECOND_PART_DIGITS);
return sprintf(to, pos= fmt_number(l_time->year, pos, 4);
digits ? "%04u-%02u-%02u %02u:%02u:%02u.%0*lu" *pos++='-';
: "%04u-%02u-%02u %02u:%02u:%02u", pos= fmt_number(l_time->month, pos, 2);
l_time->year, l_time->month, l_time->day, *pos++='-';
l_time->hour, l_time->minute, l_time->second, pos= fmt_number(l_time->day, pos, 2);
digits, (ulong)sec_part_shift(l_time->second_part, digits)); *pos++=' ';
pos= fmt_number(l_time->hour, pos, 2);
*pos++= ':';
pos= fmt_number(l_time->minute, pos, 2);
*pos++= ':';
pos= fmt_number(l_time->second, pos, 2);
if (digits)
{
*pos++='.';
pos= fmt_number((uint) sec_part_shift(l_time->second_part, digits), pos,
digits);
}
*pos= 0;
return (int)(pos - to);
} }
......
...@@ -1190,7 +1190,7 @@ bool Item::get_date(MYSQL_TIME *ltime,uint fuzzydate) ...@@ -1190,7 +1190,7 @@ bool Item::get_date(MYSQL_TIME *ltime,uint fuzzydate)
bool Item::get_seconds(ulonglong *sec, ulong *sec_part) bool Item::get_seconds(ulonglong *sec, ulong *sec_part)
{ {
if (result_type() == INT_RESULT) if (decimals == 0)
{ // optimize for an important special case { // optimize for an important special case
longlong val= val_int(); longlong val= val_int();
bool neg= val < 0 && !unsigned_flag; bool neg= val < 0 && !unsigned_flag;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment