On 8/4/03 10:10 AM, John Siracusa wrote:
> On 8/4/03 12:26 AM, Dave Rolsky wrote:
>>> # "..." includes args: year, month, day, hour, minute, second
>>> DateTime->new(...): 16 wallclock secs @ 687.29/s
>>> (14.48 usr + 0.07 sys = 14.55 CPU)
>>
>> This does a lot of work, including calculating both UTC & local times,
>> which involves calculating leap seconds, etc.
>
> Does it need to do that? I mean, sure, eventually it might have to do that
> if I want to do some sort of date manipulation, or even just fetch or print
> the date. But does it have to really do anything at all during object
> construction other than stash the args somewhere?
I played around with DateTime::new() and found that the biggest culprit is
this line:
$self->{locale} = DateTime::Locale->load( $p{locale} );
The removal of which more than doubles the performance of calling
DateTime::new(...) with ymdhms args. The only way to get a comparable
speedup is to remove every line below that one except for these two:
bless $self, $class;
return $self;
And even that only gives a ~90% speedup vs. the 100%+ gained by ditching
DateTime::Locale->load(). (Obviously all of this will hose DateTime's
actual functionality, but bear with me :)
Profiling showed that DateTime::Locale::_load_class_from_id() was being
called N+1 times during N calls to DateTime->new(...), and that it was #3 in
the dprofpp list (2000 iterations shown):
%Time ExclSec CumulS #Calls sec/call Csec/c Name
47.8 0.663 2.135 2000 0.0003 0.0011 DateTime::new
35.2 0.488 0.399 4274 0.0001 0.0001 Params::Validate::_validate
31.6 0.439 0.517 2001 0.0002 0.0003
DateTime::Locale::_load_class_from_id
15.8 0.219 0.313 2020 0.0001 0.0002 DateTime::TimeZone::BEGIN
I found that _load_class_from_id() unconditionally executes this code:
eval "require $real_class";
Skipping that line was good for a 30%+ speed boost, but that got me
thinking...aren't the Locale objects loaded/created by _load_class_from_id()
singletons? Replacing calls to _load_class_from_id() within
DateTime::Locale::load() with some dumb caching like this:
$Cache_By_Id{$id} ||= $class->_load_from_id($id)
Resulted in an easy 50% speed-up for DateTime->new(...), and
_load_class_from_id() dropped completely off the dprofpp output:
Total Elapsed Time = 0.841889 Seconds
User+System Time = 0.501889 Seconds
Exclusive Times
%Time ExclSec CumulS #Calls sec/call Csec/c Name
116. 0.584 1.290 2000 0.0003 0.0006 DateTime::new
79.3 0.398 0.287 4274 0.0001 0.0001 Params::Validate::_validate
41.6 0.209 0.220 2002 0.0001 0.0001 DateTime::_calc_local_rd
37.6 0.189 0.238 2020 0.0001 0.0001 DateTime::TimeZone::BEGIN
31.6 0.159 0.150 2002 0.0001 0.0001 DateTime::_calc_utc_rd
27.8 0.140 0.070 2002 0.0001 0.0000
DateTime::_calc_local_components
25.9 0.130 0.030 10000 0.0000 0.0000 DateTime::__ANON__
17.9 0.090 0.070 2001 0.0000 0.0000 DateTime::DefaultLocale
15.9 0.080 0.040 4004 0.0000 0.0000
DateTime::TimeZone::OffsetOnly::is_utc
15.9 0.080 0.030 2000 0.0000 0.0000 DateTime::_last_day_of_month
15.9 0.080 0.040 2002 0.0000 0.0000 DateTime::_normalize_seconds
13.9 0.070 0.010 6006 0.0000 0.0000
DateTime::TimeZone::Floating::is_floating
13.9 0.070 0.069 2006 0.0000 0.0000
DateTime::TimeZone::Floating::BEGIN
11.3 0.057 0.115 1 0.0573 0.1145 DateTime::Locale::register
7.97 0.040 0.154 6 0.0067 0.0257 DateTime::Locale::BEGIN
(An aside: why is DateTime::DefaultLocale on this list at all?)
To test my theory that this kind of dumb caching is valid, I ran all of
DateTime::Locale's tests, and then ran DateTime's tests while using the
modified DateTime::Locale. Everything passed.
So, assuming I'm not missing a finer point here, I'm thinking that one easy
speed-up for DateTime object creation would be to make the various
DateTime::Locale::* classes into singletons (using whatever the "proper"
method is for this in the DT project) and avoid repeated string evals and
repeated calls to _load_class_from_id().
Going further, if calls to DateTime::Locale->load(...) could be "memoized"
safely, that'd be great too :)
-John