Subject: Re: Waving the lambda flag again (was: Constants and DEFCONSTANT) From: Erik Naggum <erik@naggum.no> Date: 1999/04/02 Newsgroups: comp.lang.lisp Message-ID: <3132042770550791@naggum.no> * hs@inferno.nirvananet (Hartmann Schaffer) | Actually, several steps in the evolution might be even more interesting OK, I'll do the steps thing. at issue was hacking up and printing a time representation. the naïve solution is to use a bignum of seconds since some human-inspired epoch, use FORMAT with ~D to print it. however, I also needed proper timezone support (CL's support is suitable only if you deal with times in a single timezone), and milliseconds (since a second is a really long time to modern computers, and even milliseconds are). the zone was represented as the number of hours west of Greenwich. the time representation is now split up into day, time, and milliseconds. the zone is changed to seconds eas of Greenwich. day 0 is 2000-03-01, because that's when a new 400-year leap year period starts, which means that the leap day is at the end of the four years, the centennium, and the quatercentennium, and thus no expensive computations are needed to check for leap years. however, the main culprit was division. it is an expensive operation, table lookup not, so I precomputed tables for the 146097 days of a quatercentennium and the 86400 seconds of a day, and did a nifty data representation hack so the values fit in a single fixnum. the full time representation was changed from a class to a structure, and proper declarations meant that the access functions were inlined and the type (fixnum) propagated properly. time comparison functions now compared three fixnums instead of bignums and a fixnum (milliseconds) and that meant the timezone tables (extracted from the Unix timezone database that the C library uses (but also gives you exactly one handle on)), were searched much faster. in addition, the interval of the timezone was made available to the caller upon demand (it affects the length of a local day, which can be 23, 24, or 25 hours!) and also cached, since most times decoded were in the same timezone. the time zone adjustment function also did simple addition and subtraction instead of new divisions into day and time. what really made a difference once I had figured out how to store the values in a fixnum (I'll leave that as an exercise for the reader -- since it's so far from obvious how to do it, some people will get a real kick out of how simple it is, and I don't want to take that kind of joy away form anyone), was to pass a preallocated vector for the decoding functions to store its values into instead of returning them as multiple values. finally, printing turned out to be very division-intensive, too, so I precomputed a pair of tables of high and low digits for 100 numbers. streams were also very expensive, so I deposited characters directly into a preallocated string and returned a copy or wrote it out (depending on whether the stream argument was NIL or a stream, like FORMAT). centuries were easy to change into numbers in the 16 to 99 range instead of 1600 to 9900. (granted, I have built myself a system with a serious Y10K problem, but I'll print a warning in the year 9900 if it makes anyone less worried. rumor has it that Y1K brought us the Dark Ages because it took 400 years to update all the software to four instead of three digits in the year.) the only real problem I had with tuning this stuff was that a 400 MHz CPU is damn hard to get useful statistics from every 10 ms. it manages to do a tremendous lot of work in 10 ms, so I had to let profiling run for many minutes to collect useful data as I got closer to the optimum. #:Erik