Smarty Forum Index Smarty
WARNING: All discussion is moving to https://reddit.com/r/smarty, please go there! This forum will be closing soon.

smarty_make_timestamp can be faser
Goto page 1, 2  Next
 
This forum is locked: you cannot post, reply to, or edit topics.   This topic is locked: you cannot edit posts or make replies.    Smarty Forum Index -> Smarty Development
View previous topic :: View next topic  
Author Message
Loki
Smarty Rookie


Joined: 13 Jan 2011
Posts: 30

PostPosted: Fri Jan 14, 2011 9:37 pm    Post subject: smarty_make_timestamp can be faser Reply with quote

In file smarty/plugins/shared.make_timestamp.php following function:

Code:

function smarty_make_timestamp($string)
{
    if(empty($string)) {
        // use "now":
        return time();
    } elseif ($string instanceof DateTime) {
        return $string->getTimestamp();
    } elseif (preg_match('/^\d{14}$/', $string)) {
        // it is mysql timestamp format of YYYYMMDDHHMMSS?           
        return mktime(substr($string, 8, 2),substr($string, 10, 2),substr($string, 12, 2),
                       substr($string, 4, 2),substr($string, 6, 2),substr($string, 0, 4));
    } elseif (is_numeric($string)) {
        // it is a numeric string, we handle it as timestamp
        return (int)$string;
    } else {
        // strtotime should handle it
        $time = strtotime($string);
        if ($time == -1 || $time === false) {
            // strtotime() was not able to parse $string, use "now":
            return time();
        }
        return $time;
    }
}


For each call is executed a slow function preg_match (even for timestamp).
I propose to change it as follows

Code:

function smarty_make_timestamp($string)
{
    if(empty($string)) {
        // use "now":
        return time();
    } elseif ($string instanceof DateTime) {
        return $string->getTimestamp();
    } elseif (is_numeric($string)) {
      if (strlen($string)==14) {
         // it is mysql timestamp format of YYYYMMDDHHMMSS?           
         return mktime(substr($string, 8, 2),substr($string, 10, 2),substr($string, 12, 2),
                     substr($string, 4, 2),substr($string, 6, 2),substr($string, 0, 4));
      } else {
         // it is a numeric string, we handle it as timestamp
         return (int)$string;
      }
    } else {
        // strtotime should handle it
        $time = strtotime($string);
        if ($time == -1 || $time === false) {
            // strtotime() was not able to parse $string, use "now":
            return time();
        }
        return $time;
    }
}
Back to top
View user's profile Send private message
mohrt
Administrator


Joined: 16 Apr 2003
Posts: 7368
Location: Lincoln Nebraska, USA

PostPosted: Sun Jan 16, 2011 4:35 pm    Post subject: Reply with quote

From the PHP manual for is_numeric:

Quote:
Finds whether the given variable is numeric. Numeric strings consist of optional sign, any number of digits, optional decimal part and optional exponential part. Thus +0123.45e6 is a valid numeric value. Hexadecimal notation (0xFF) is allowed too but only without sign, decimal and exponential part.


So your suggested code could possibly get a really messed up "mysql" datestamp. The preg guarantees exactly 14 digit chars.
Back to top
View user's profile Send private message Visit poster's website
Loki
Smarty Rookie


Joined: 13 Jan 2011
Posts: 30

PostPosted: Sun Jan 16, 2011 5:46 pm    Post subject: Reply with quote

You right. But it's possible to replace
Code:

} elseif (is_numeric($string)) {

by
Code:
} elseif (is_int($string)) {

or
Code:
} elseif (intval($string)) {
Back to top
View user's profile Send private message
mohrt
Administrator


Joined: 16 Apr 2003
Posts: 7368
Location: Lincoln Nebraska, USA

PostPosted: Sun Jan 16, 2011 6:02 pm    Post subject: Reply with quote

afaik is_int() tests if a variable type is int, not if a string value is all number characters. is_numeric() tests if a string type is numeric in format, but accepts other characters than numbers. We need to be sure a string has a format of 14 number characters.
Back to top
View user's profile Send private message Visit poster's website
rodneyrehm
Administrator


Joined: 30 Mar 2007
Posts: 674
Location: Germany, border to Switzerland

PostPosted: Sun Jan 16, 2011 8:07 pm    Post subject: Reply with quote

how about this?

Code:
if (is_numeric($string)) {
  if (strlen($string) == 14) {
    return mktime(
      substr($string, 8, 2),
      substr($string, 10, 2),
      substr($string, 12, 2),
      substr($string, 4, 2),
      substr($string, 6, 2),
      substr($string, 0, 4)
    );
  } else {
    return (int) $string;
  }
}
Back to top
View user's profile Send private message Visit poster's website
mohrt
Administrator


Joined: 16 Apr 2003
Posts: 7368
Location: Lincoln Nebraska, USA

PostPosted: Sun Jan 16, 2011 8:43 pm    Post subject: Reply with quote

already answered that, read up two posts.
Back to top
View user's profile Send private message Visit poster's website
Loki
Smarty Rookie


Joined: 13 Jan 2011
Posts: 30

PostPosted: Mon Jan 17, 2011 8:53 pm    Post subject: Reply with quote

Code:
} elseif (intval($string)>0 && strlen($string)==14) {


i understood that preg it's easiest way but, unfortunately, it's too slow
Back to top
View user's profile Send private message
rodneyrehm
Administrator


Joined: 30 Mar 2007
Posts: 674
Location: Germany, border to Switzerland

PostPosted: Mon Jan 17, 2011 8:59 pm    Post subject: Reply with quote

Loki wrote:
Code:
} elseif (intval($string)>0 && strlen($string)==14) {


i understood that preg it's easiest way but, unfortunately, it's too slow


that test would fail these:

Code:
$string = "3.141500000000";
$string = "3e000000000001";
// …
Back to top
View user's profile Send private message Visit poster's website
mohrt
Administrator


Joined: 16 Apr 2003
Posts: 7368
Location: Lincoln Nebraska, USA

PostPosted: Mon Jan 17, 2011 9:08 pm    Post subject: Reply with quote

this might do the trick:

Code:
if(ctype_digit($string)&&strlen($string)==14)
Back to top
View user's profile Send private message Visit poster's website
rodneyrehm
Administrator


Joined: 30 Mar 2007
Posts: 674
Location: Germany, border to Switzerland

PostPosted: Mon Jan 17, 2011 9:43 pm    Post subject: Reply with quote

After having some confusing test results due to an enabled xdebug, we've now found

Code:
ctype_digit($string) && strlen($string)==14


to be the fastest solution.
Back to top
View user's profile Send private message Visit poster's website
mohrt
Administrator


Joined: 16 Apr 2003
Posts: 7368
Location: Lincoln Nebraska, USA

PostPosted: Mon Jan 17, 2011 9:45 pm    Post subject: Reply with quote

globe wrote:
After having some confusing test results due to an enabled xdebug, we've now found

Code:
ctype_digit($string) && strlen($string)==14


to be the fastest solution.


well fastest, and the only one as accurate as preg_*

committed to svn trunk.
Back to top
View user's profile Send private message Visit poster's website
Loki
Smarty Rookie


Joined: 13 Jan 2011
Posts: 30

PostPosted: Mon Jan 17, 2011 10:07 pm    Post subject: Reply with quote

Code:

<?
function getmicrotime()
{
   list($usec, $sec) = explode(" ", microtime());
   return ((float)$usec + (float)$sec);
}

$time_start = getmicrotime();
for($i=0;$i<1000000;$i++)
{
   $string=str_pad($i, 14, "0", STR_PAD_LEFT);
   if (preg_match('/^\d{14}$/', $string)) null;
}
echo sprintf("%01.3f sec.", getmicrotime()-$time_start)."<br />";

$time_start = getmicrotime();
for($i=0;$i<1000000;$i++)
{
   $string=str_pad($i, 14, "0", STR_PAD_LEFT);
   if(ctype_digit($string)&&strlen($string)==14) null;
}
echo sprintf("%01.3f sec.", getmicrotime()-$time_start)."<br />";


results (on windows):

Code:
10.093 sec.
12.234 sec.


So preg_match is the fastest way to check value. Thanks for your attention!
Back to top
View user's profile Send private message
rodneyrehm
Administrator


Joined: 30 Mar 2007
Posts: 674
Location: Germany, border to Switzerland

PostPosted: Mon Jan 17, 2011 10:21 pm    Post subject: Reply with quote

urrm. although we only tested on osx and linux we did find the ctype solution to be faster.

is it possible you have xdebug (and or similar PECLs) active? they slow down certain function calls for some reason. My development machine has xdebug enabled (for obvious reasons). Only after disabling it did I get the same results I saw on some production systems I ran the test on.

also give microtime() another look. microtime(true) already returns a float, so you don't need to calculate that yourself…

Edit:

Code:
C:\Documents and Settings\rrehm\Desktop>php test.php
done: 1.2822089195251
done: 1.8049728870392
done: 2.0510950088501

C:\Documents and Settings\rrehm\Desktop>php --version
PHP 5.3.2 (cli) (built: Mar  3 2010 19:40:13)
Copyright (c) 1997-2010 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies


ctype is fastest, followed by the inaccurate intval, followed by preg on last place.

your test shows the same result on my box:
Code:
C:\Documents and Settings\rrehm\Desktop>php test.php
4.677 sec.
3.548 sec.
Back to top
View user's profile Send private message Visit poster's website
Loki
Smarty Rookie


Joined: 13 Jan 2011
Posts: 30

PostPosted: Tue Jan 18, 2011 6:51 am    Post subject: Reply with quote

globe wrote:
is it possible you have xdebug (and or similar PECLs) active?

You right - after disabling xdebug ctype variant becomes up to 35% faster than preg_match.
Back to top
View user's profile Send private message
Lemon Juice
Smarty Pro


Joined: 24 May 2006
Posts: 109

PostPosted: Wed Jan 19, 2011 9:44 am    Post subject: Reply with quote

globe wrote:
After having some confusing test results due to an enabled xdebug, we've now found

Code:
ctype_digit($string) && strlen($string)==14


to be the fastest solution.


That's nice but it can be sped up even more - if we are talking about such micro-optimizations. Just reverse the order:

Code:
strlen($string)==14 && ctype_digit($string)


This will 2x faster for a huge majority of cases because:

1. If the string is a timestamp number, then it's certainly less than 14 characters long so ctype_digit will not have to be executed at all - 2x faster.

2. If the string is a descriptive string for strtotime it would be very rare that it's 14 characters long. All date and datetime formats from mysql are not 14 characters long - 2x faster for probably >90% of cases.

3. If the string is mysql timestamp format of YYYYMMDDHHMMSS then the execution time will be the same. The same for empty string (first if) and DateTime object (first elseif).
Back to top
View user's profile Send private message
Display posts from previous:   
This forum is locked: you cannot post, reply to, or edit topics.   This topic is locked: you cannot edit posts or make replies.    Smarty Forum Index -> Smarty Development All times are GMT
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group
Protected by Anti-Spam ACP