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