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