[#3925] - Thread Contention problems

Category:
Bug report
Priority:
High
Status:
Acknowledged
Project: Severity:
Major
Resolution:
Open
Component: Reproducibility:
Always
Assigned to:

The issue refer to post : http://jasperforge.org/plugins/espforum/view.php?group_id=102&forumid=10...

The following stack traces shows the contention problems when 100's of threads are used to generate the report. Currently in our application we are getting around 2500 reports per minutes ( in 3 servers - 8 core, 16 GB RAM each) but what I am looking for is to get around 10000 reports per minute :-) I know, I am looking for a figure which is achievable in the hardware where I am working on, but I need to remove the contentions completely.

The main problem I am having now is in init of LineBreakMeasurer, Since this is core of report printing, I am really stuck without any solution. I was wondering if any solution is possible to this contention? like maintaining a cache of LineBreakMeasurer with the key of font, size, style etc and then using deleteChar and insertChar of LineBreakMeasurer instance ? I am not sure if that itself will be synchronized !. If I do not get any solution to this, I may not have any solution to scale up ( even I could try Fork-Join in Java 7 ? I am not sure if the synchronization problem will go away with that ? : http://www.ibm.com/developerworks/java/library/j-jtp11137.html )

Another option could be like a much simpler algorithm for non styled text fields where majority of the reporting happens, and more complex solution like LineBreakMeasurer for really complex fonts ?? One link I saw was http://www.freshcookies.org/jtreemap/api/org/freshcookies/awt/LineWrappe... ( I am not sure if this is completely different topic altogether )

When I profiled, I got the first contention in net.sf.jasperreports.engine.util.JRProperties.getProperty(), where the Properties.get() is synchronized and so the below contention. For trial I have made the properties as ThreadLocal, but I think we can have better solution like ConcurrentHashMap or something like that to store the property values.

java.util.Properties.getProperty(java.lang.String)
net.sf.jasperreports.engine.util.JRProperties.getProperty(net.sf.jasperreports.engine.JRPropertiesHolder, java.lang.String)
net.sf.jasperreports.engine.util.JRTextMeasurerUtil.getTextMeasurerFactoryClass(net.sf.jasperreports.engine.JRPropertiesHolder)
net.sf.jasperreports.engine.util.JRTextMeasurerUtil.getTextMeasurerFactory(net.sf.jasperreports.engine.JRPropertiesHolder)
net.sf.jasperreports.engine.util.JRTextMeasurerUtil.createTextMeasurer(net.sf.jasperreports.engine.JRCommonText, net.sf.jasperreports.engine.JRPropertiesHolder)
net.sf.jasperreports.engine.util.JRTextMeasurerUtil.createTextMeasurer(net.sf.jasperreports.engine.JRCommonText)
net.sf.jasperreports.engine.fill.JRFillTextElement.createTextMeasurer()
net.sf.jasperreports.engine.fill.JRFillTextElement.ensureTextMeasurer()
net.sf.jasperreports.engine.fill.JRFillTextElement.chopTextElement(int)
net.sf.jasperreports.engine.fill.JRFillTextField.prepare(int, boolean)
net.sf.jasperreports.engine.fill.JRFillElementContainer.prepareElements(int, boolean)
net.sf.jasperreports.engine.fill.JRFillBand.fill(int, boolean)
net.sf.jasperreports.engine.fill.JRFillBand.fill(int)
net.sf.jasperreports.engine.fill.JRVerticalFiller.fillTitle()
net.sf.jasperreports.engine.fill.JRVerticalFiller.fillReportStart()
net.sf.jasperreports.engine.fill.JRVerticalFiller.fillReport()
net.sf.jasperreports.engine.fill.JRBaseFiller.fill(java.util.Map)
net.sf.jasperreports.engine.fill.JRBaseFiller.fill(java.util.Map, java.sql.Connection)
net.sf.jasperreports.engine.fill.JRFiller.fillReport(net.sf.jasperreports.engine.JasperReport, java.util.Map, java.sql.Connection)
net.sf.jasperreports.engine.JasperFillManager.fillReport(net.sf.jasperreports.engine.JasperReport, java.util.Map, java.sql.Connection)
net.sf.jasperreports.engine.JasperFillManager.fillReport(java.lang.String, java.util.Map, java.sql.Connection)

In the below case, the getRegistries() is synchronized on registryCache ( org.apache.commons.collections.ReferenceMap ) We could use something like http://www.nabble.com/ConcurrentReferenceMap-enhancement-to-166---Feedba... or some other implementation which will avoid the locking.

net.sf.jasperreports.extensions.DefaultExtensionsRegistry.getRegistries()
net.sf.jasperreports.extensions.DefaultExtensionsRegistry.getExtensions(Class)
net.sf.jasperreports.engine.util.JRFontUtil.getFontInfo(String, Locale)
net.sf.jasperreports.engine.util.JRFontUtil.getAwtFontFromBundles(String, int, int, Locale)
net.sf.jasperreports.engine.util.JRFontUtil.getAttributes(Map, JRFont, Locale)
net.sf.jasperreports.engine.JRStyledTextAttributeSelector$2.getStyledTextAttributes(JRPrintText)
net.sf.jasperreports.engine.fill.JRTemplatePrintText.getStyledText(JRStyledTextAttributeSelector)
net.sf.jasperreports.engine.JRAbstractExporter.getStyledText(JRPrintText, boolean)
net.sf.jasperreports.engine.export.JRPdfExporter.exportText(JRPrintText)
net.sf.jasperreports.engine.export.JRPdfExporter.exportElements(Collection)
net.sf.jasperreports.engine.export.JRPdfExporter.exportPage(JRPrintPage)
net.sf.jasperreports.engine.export.JRPdfExporter.exportReportToStream(OutputStream)
net.sf.jasperreports.engine.export.JRPdfExporter.exportReport()
net.sf.jasperreports.engine.JasperExportManager.exportReportToPdfStream(JasperPrint, OutputStream)

The following trace the contention is happening while ThreadLocal.get() call. I have not done any research in this area, but I just put a static ConcurentHashMap to put the registry and to get the value.

net.sf.jasperreports.extensions.ExtensionsEnvironment.getExtensionsRegistry()
net.sf.jasperreports.engine.util.JRFontUtil.getFontInfo(java.lang.String, java.util.Locale)
net.sf.jasperreports.engine.util.JRFontUtil.getAwtFontFromBundles(java.lang.String, int, int, java.util.Locale)
net.sf.jasperreports.engine.util.JRFontUtil.getAttributes(java.util.Map, net.sf.jasperreports.engine.JRFont, java.util.Locale)
net.sf.jasperreports.engine.JRStyledTextAttributeSelector$2.getStyledTextAttributes(net.sf.jasperreports.engine.JRPrintText)
net.sf.jasperreports.engine.fill.JRTemplatePrintText.getStyledText(net.sf.jasperreports.engine.JRStyledTextAttributeSelector)
net.sf.jasperreports.engine.JRAbstractExporter.getStyledText(net.sf.jasperreports.engine.JRPrintText, boolean)
net.sf.jasperreports.engine.export.JRPdfExporter.exportText(net.sf.jasperreports.engine.JRPrintText)
net.sf.jasperreports.engine.export.JRPdfExporter.exportElements(java.util.Collection)
net.sf.jasperreports.engine.export.JRPdfExporter.exportPage(net.sf.jasperreports.engine.JRPrintPage)
net.sf.jasperreports.engine.export.JRPdfExporter.exportReportToStream(java.io.OutputStream)
net.sf.jasperreports.engine.export.JRPdfExporter.exportReport()
net.sf.jasperreports.engine.JasperExportManager.exportReportToPdfStream(net.sf.jasperreports.engine.JasperPrint, java.io.OutputStream)

sanjaymk's picture
Joined: Apr 23 2009 - 12:51am
Last seen: 4 years 7 months ago

4 Comments:

#1

The text measurer has been optimized in SVN revision 4784 to avoid the use of LineBreakMeasurer for simple and short texts (to be more precise, for single style texts that fit on a single line).

Also, JRProperties was changed to use ConcurrentHashMap for the properties in SVN revision 4838.

We're going to consider the remaining problems as well. If you're interested in testing the latest changes, you can get the sources from SVN and build a JasperReports jar.

Regards,
Lucian

#2

Sun Java 1.7 resolved the java.text.Bidi thread contention (which you observed as java.awt.font.LineBreakMeasurer). But it might have introduced another contention in sun.font.T2KFontScaler. Nevertheless, you could give Java 1.7 a try.

As a more general note, having that many threads (an order of magnitude bigger than the number of CPUs) might not yield the best throughput. I would rather try using a thread pool that has the same size as the number of CPUs, or a little higher if the report generation process involves significant I/O (such as DB access).

#3

Lucian,

Thank you so much for looking into this. Unfortunately the project is completed with the achievable performance at that time and delivered to the client. Since we do not work on those projects now, will not be able to test the same.

The reason for more number of threads is because the high network IO and as you said a tuned configuration would be the best approach and we did that and reduced the threads appropriately.

I am sure that these fixes and information will help other users in future. Once again I want to thank you for hosting such a great product as open source !!

Sanjay

#4

Hello Sanjay

Thank you for your words, we appreciate the feedback. And sorry for taking so long to implement some improvements in the direction you suggested.

But the issue is now on our radar and we're going to investigate further solutions for making JR better in that aspect.

Regards,
Lucian

Feedback