Re: Formatting a date

Endre Stølsvik <[email protected]> Thu, 30 Mar 2006 13:16:55 +0200 (CEST)
Newsgroups gmane.comp.java.webmacro.user
Message-ID <[email protected]>
  This message is in MIME format.  The first part should be readable text,
  while the remaining parts are likely unreadable without MIME-aware tools.

--0-1212892834-1143703651=:43658
Content-Type: TEXT/PLAIN; CHARSET=iso-8859-15
Content-ID: <[email protected]>
Content-Transfer-Encoding: quoted-printable

On Wed, 29 Mar 2006, Alex Twisleton-Wykeham-Fiennes wrote:

| On Tue 28 March 2006 10:06, Endre St=F8lsvik wrote:
| > With how many threads did you test this? 1000? 100? 10? 1? And how
| > "real-life" was the scenario: how many date-formattings will one
| > typically do within one rendering?
|=20
| Much as I hate to rise to the occasion, I'll just attempt to put this o=
ne to=20
| bed.

....

| This is the same as the original test, just repeated 10 times to give t=
he jit=20
| time to kick in.  In addition, the tests have been changed as follows:-
|=20
| Creator: new SimpleDateFormat for each invocation
| HashMap: using an unsynchronized HashMap for access
| ConcurrentHashMap: using an unsynchronized ConcurrentHashMap for access=
 from=20
| the concurrent.jar shipped with webmacro (does anyone know what version=
 this=20
| is?)
|=20
| So that deals with the JIT issues.

Excellent, then the JIT-point was rather moot. (You could do System.gc()=20
inbetween each test, so that gc doesn't happen within the timed loop, and=
=20
start the VM with -Xms512M -Xmx512M, so that the heap doesn't have to gro=
w=20
and stabilize during the test).

Then what about the multi-threading issues, and "impact"-"analysis", whic=
h=20
was the main point here. Lets say that we have a template with 40 date=20
formattings in (some list?), and 75 threads are rendering this constantly=
=20
100 times each - how many seconds would one _theoretically_ gain by using=
=20
the cache lookuped DateFormat (from the single-threaded access numbers),=20
and how much will this really be when we count the multi-threading in?

This is 7500 page renders, with 40 dates each: 300.000.

(I took the 8th run, it seemed somewhat fair:)
1 "created formatting" takes 0.03882 ms
1 "cached formatting" takes  0.01305 ms

300.000 "created": 11.646 sec
300.000 "cached":   3.915 sec

diff: 7.731 seconds for 7500 page renders with 40 date formattings on eac=
h=20
(which is a rather large "average" number of date-renderings?)

For one page, we have 40*0.03882=3D1.5528 ms, vs 0.5220 (or 7731/7500),=20
which is a ~1 ms diff.

The above is _given_ that there is _no_ impact of multi-thread-access to=20
the map - which I doubt is the fact.

And a sub-point here is: IF you had 40 date renderings on one page,=20
shouldn't you have created the DateFormat and put it in the context=20
anyway? Or maybe the date-tool could do that for you (return an instance)=
?=20
That would have been the fastest at any rate.


Okay, but now, I'll introduce - "The Bomb" (maybe):

This suddenly hit me:

Is (Simple)DateFormat itself completely thread-safe, by contract?

I read javadoc (1.3), and it doesn't say at all.


In the code, SimpleDateFormat:

    public StringBuffer format(Date date, StringBuffer toAppendTo,
                               FieldPosition pos)
    {
        // Initialize
        pos.beginIndex =3D pos.endIndex =3D 0;

        // Convert input date to time field list
        calendar.setTime(date);

...

The last line there looks very un-promising. But the javadoc..?

I then tried google, "simpledateformat thread safe":

first hit:
  http://www-128.ibm.com/developerworks/java/library/j-jtp09263.html

  " How many times have you looked at the Javadoc for a class, and=20
wondered, "Is this class thread-safe?" In the absence of clear=20
documentation, readers may make bad assumptions about a class's thread=20
safety. "

  " As an example of this pitfall, the class java.text.SimpleDateFormat i=
s=20
not thread-safe, but it wasn't until the 1.4 JDK that this was documented=
=20
in the Javadoc. "


And yes, javadoc 1.4:

" Synchronization

Date formats are not synchronized. It is recommended to create separate=20
format instances for each thread. If multiple threads access a format=20
concurrently, it must be synchronized externally. "


NB: The rest of the mail was written before the above hit me! Most is=20
still valid, though - but the above should hopefully get the cache=20
removed, at any rate?

|=20
| > This illustrates the point about synchs that I'm trying to make: sync=
hs
| > are pretty much not noticeable at all if you access them with one thr=
ead
| > (but there was a difference, and it was in the bad direction, right?)=
.
| > However, _contended_ synchs are the bad stuff. A conteded synch force=
s a
| > complete flush of your memory-caches (read the java spec on synch if =
you
| > haven't yet). On "small systems", this isn't that big a deal, as they=
 tend
| > to a) have one CPU (thus really only one cache), and b) have coherent
| > caches if they are more. On larger systems, where CPUs might be more
| > independent ("NUMA"), this can be a huge overhead.
|=20
| Hence the usage of ConcurrentHashMap in the updated test.

Okay.

But a cache lookup is a cache lookup. ConcurrencHashMap is excellent=20
stuff, admittedly. But still, what about contending threads?

You stated in another mail that the hitting on the ConcurrentHashMap was=20
the one thing that used most time..? How can that be?

But at any rate, couldn't you hook this stuff up with the WM-internals of=
=20
caching, at least? There is already lots of things cached within WM, and =
I=20
fail to see the reason for not letting this thing be a part of that=20
caching-community - enabling one to configure the sizes, type of cache,=20
and all that.
  WM.shutdown()/clean()/whatever() must clean these caches!

|=20
| Finally, if we are going to be planning on boycotting all "hidden cache=
s" of=20
| code, have you actually looked at the source code for SimpleDateFormat?

Yes. I know. I don't like it, but this is in "java proper", down in the=20
core libraries, and will never be GC'ed anyway. Vector, Hashtable and=20
StringBuffer is also synchronized. They've done some weird things=20
throughout java's life. That doesn't excuse making new such things.

One even more fantastic thing is the JDBC DriverManager: the=20
getConnection(...) is synchronized, and drivers actually "make contact"=20
with the database at this step (log in). And e.g. Oracle's driver in=20
certain circumstances _hangs_ till that happens - thus you whole database=
=20
connectivity is _utterly blocked_ until that piece of junk releases is.

Driver.connect() javadocs:

 * Attempts to make a database connection to the given URL.
 * The driver should return "null" if it realizes it is the wrong kind
 * of driver to connect to the given URL.  This will be common, as when
 * the JDBC driver manager is asked to connect to a given URL it passes
 * the URL to each loaded driver in turn.

So, _within a synchronzied call_, potentially _all_ drivers will be=20
requested to try to connect to their database, where at least on,=20
potentially several, of these driver actually will make some connection,=20
usually through TCP, to the database - where timouts and whatnot might=20
happen.

This is _so amazingly braindead_ that it really can't be described in=20
harsh enough words.

And this is within "java proper".

But that doesn't legitimize doing new such stuff with caches and=20
synch-blocks.

This just to illustrate a point.

|=20
| The final thing to notice about SimpleDateFormat is that it really isn'=
t a=20
| nice thing to do to your garbage collector.  It really does generate a=20
| *large* number of temporary items during the initialisation of=20
| SimpleDateFormat, all of which will need garbage collecting (~40 Object=
s=20
| taking up ~1k per init) .

Then _don't do it_.

Execute your formatting "inside", using a single-instantiated formatter,=20
or make a selection of formatters available to the thread yourself, from=20
some context-up-looked map (e.g. you fetch everything you need in _one_=20
synch block, e.g. when looking up your user-object or similar, or put it=20
in the HttpSession, or _whatever_).

My point is about "innocent-looking _tools_", of which sadly the entire W=
M=20
is one. But I don't see the reason to still clutter it up with even more=20
"magic stuff" that does even more magic caching and synching and whatnot.

1 _k_ per unit?! and _40_ units?! Where?

But still, java is by now the cheapest language in the world to do object=
=20
_creations_ in. Using the generational garbage collector (default from=20
1.3, I think, and tuned i 1.4 and each genereation up), objects that=20
immediately die, are _very_ cheap to "collect" too - the point is that=20
they _aren't_ collected (only live objects are collected by the=20
copy-collector in the first stages of GC: eden -> survivor 0 and 1)

http://www-128.ibm.com/developerworks/java/library/j-jtp09275.html?ca=3Dd=
gr-lnxw01JavaUrbanLegends

Check out the "escape analysis" part - now that'll be cool.

..


|  Looking at the initialiser makes it pretty obvious=20
| that it is designed to be initialised once and then run multiple times =
as the=20
| parse() and format() methods have been designed to minimise object crea=
tion=20
| and are quite streamlined whereas the creator is a bit of a beast.

It even says so in the javadoc, yes.

----------
If you are formatting multiple numbers, it is more efficient to get the=20
format and use it multiple times so that the system doesn't have to fetch=
=20
the information about the local language and country conventions multiple=
=20
times.

  DateFormat df =3D DateFormat.getDateInstance();
  for (int i =3D 0; i < a.length; ++i) {
    output.println(df.format(myDate[i]) + "; ");
  }=20

----------=20

(javadoc 1.3 - Text obviously copied verbatim from NumberFormat, which=20
says the exact same thing..)


Regards,
Endre
--0-1212892834-1143703651=:43658--


-------------------------------------------------------
This SF.Net email is sponsored by xPML, a groundbreaking scripting language
that extends applications into web and mobile media. Attend the live webcast
and join the prime developer group breaking into this new coding territory!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=110944&bid=241720&dat=121642