Quantcast
Channel: English – Reiner Saddey's Place
Viewing all articles
Browse latest Browse all 15

Java DateFormat: Do use FastDateFormat or clone() to achieve Thread-Safety

$
0
0

Which pattern should you use to cope with DateFormat (and all of its siblings deriving from java.text.Format) not being thread-safe?

TL;DR: Use Apache Commons Lang FastDateFormat (“a fast and thread-safe version of SimpleDateFormat). Use DateFormat.clone() if you can’t use FastDateFormat.

  1. Use synchronized singleton – thread contention!
  2. Always create a new instance – expensive!
  3. Use clone of singleton – does it perform?
  4. Use pre-fabricated pool of instances – cumbersome to use and scale!
  5. Use Apache FastDateFormat –  does it live up to its name?
  6. Your idea here…

7 years ago, Software Monkey’s Tech Site – Cyclic Pool for Shared Objects has done performance tests comparing the first 4, then available, choices. As the title may suggest, pool turned out to be the winner, albeit by a narrow margin.

But as Java VMs get smarter and common hardware now uses more and even more CPUs, the very same performance test now reveals quite a different picture.

And there is a new kid on the block as well: Apache Commons FastDateFormat.

The new winner is: FastDateFormat. It’s really fast and does away with all threading issues as well.

Test run on my MacBook Pro (Early 2011, 4 CPUs, Java 8u144 with G1GC enabled):

2018-04-15 20:35:33.411 : Threads=10, Iterations=1,000,000
2018-04-15 20:36:33.661 : Test 3:
2018-04-15 20:36:43.956 : Sync : 10,295 ms
2018-04-15 20:36:50.186 : Create : 5,161 ms
2018-04-15 20:36:54.327 : Clone : 2,993 ms
2018-04-15 20:36:58.323 : Pool : 2,811 ms
2018-04-15 20:37:01.070 : Fast : 1,618 ms

2018-04-15 20:38:24.188 : Threads=4, Iterations=1,000,000
2018-04-15 20:38:57.665 : Test 3:
2018-04-15 20:39:01.809 : Sync : 4,143 ms
2018-04-15 20:39:05.287 : Create : 2,407 ms
2018-04-15 20:39:07.789 : Clone : 1,349 ms
2018-04-15 20:39:10.366 : Pool : 1,487 ms
2018-04-15 20:39:12.265 : Fast : 806 ms

And if you cannot use FastDateFormat, e.g. your format is not a SimpleDateFormat or uses certain time zone / year patterns: Use DateFormat.clone().

Unfortunately the author of Software Monkey’s Tech Site chose to delete my findings, but here they are:

Thank you for examining the (wildly ignored) clone pattern!

I may have a clue regarding the “why does a single thread test behave that differently?”. Well, Java VMs may be smart enough to account for the situation, that there is just a single runnable thread and none of the blocked threads (i.e. the main thread) own any monitor (i.e. synchronized), thus effectively temporally turning all synchronizeds to nops. This optimization most likely has been implemented for ages, because the semantics of synchronized are VERY expensive and do propagate down to the hardware level: Though synchronized has just a single Object parameter, it enforces a memory barrier on ALL values within the cache of a CPU, resulting in a complete commit of the CPU-local cache to shared main memory.

Another explanation would be the fact, that, when there’s just a single runnable thread, there are no thread switches at all: It just keeps running. Thread switches may be, IMHO ARE expensive (unless the VM uses “green” threads).

I ran your tests on my MacBook Pro (4 CPUs) using Java 8u144 with G1GC enabled (-XX:+UseG1GC):
2018-04-14 22:29:51.431 : Threads=10, Iterations=1,000,000
2018-04-14 22:29:51.432 : Test 1:
2018-04-14 22:30:03.394 : Sync : 11,961 ms
2018-04-14 22:30:10.216 : Create : 5,751 ms
2018-04-14 22:30:14.232 : Clone : 2,931 ms
2018-04-14 22:30:17.982 : Pool : 2,641 ms
2018-04-14 22:30:19.101 : Test 2:
2018-04-14 22:30:29.067 : Sync : 9,966 ms
2018-04-14 22:30:35.078 : Create : 4,939 ms
2018-04-14 22:30:39.092 : Clone : 2,868 ms
2018-04-14 22:30:42.774 : Pool : 2,583 ms
2018-04-14 22:30:43.904 : Test 3:
2018-04-14 22:30:53.328 : Sync : 9,424 ms
2018-04-14 22:30:59.357 : Create : 4,962 ms
2018-04-14 22:31:03.303 : Clone : 2,809 ms
2018-04-14 22:31:06.892 : Pool : 2,382 ms

Well, my MacBook has less power (aka CPUs / Threads). Let’s repeat the tests using MAXTHREADS = number of “real” CPUs (leaving some CPU power to the garbage collector and other background tasks) in order to better resemble an adequate production environment:
2018-04-14 22:33:06.407 : Threads=4, Iterations=1,000,000
2018-04-14 22:33:06.407 : Test 1:
2018-04-14 22:33:12.530 : Sync : 6,121 ms
2018-04-14 22:33:16.345 : Create : 2,708 ms
2018-04-14 22:33:18.713 : Clone : 1,289 ms
2018-04-14 22:33:21.215 : Pool : 1,338 ms
2018-04-14 22:33:22.306 : Test 2:
2018-04-14 22:33:26.217 : Sync : 3,911 ms
2018-04-14 22:33:29.544 : Create : 2,255 ms
2018-04-14 22:33:31.940 : Clone : 1,204 ms
2018-04-14 22:33:34.639 : Pool : 1,604 ms
2018-04-14 22:33:35.707 : Test 3:
2018-04-14 22:33:39.694 : Sync : 3,986 ms
2018-04-14 22:33:43.061 : Create : 2,294 ms
2018-04-14 22:33:45.462 : Clone : 1,226 ms
2018-04-14 22:33:47.902 : Pool : 1,350 ms

Thus, I have no other verdict, than to recommend using clone at large: It’s simple, easily understood, and may turn out to give most favorable results, assuming actual concurrent usage of DateFormat.format does not exceed hardware resources. i.e. number of usable CPUs.

Regards,
Reiner

My sources (Eclipse stand-alone project), with FastDateFormat added, at: DateFormatTest.zip

Always look forward, never look back!


Viewing all articles
Browse latest Browse all 15

Latest Images

Trending Articles





Latest Images