If you are using the ERES in a multi-user environment, multiple users can be exporting reports and charts at the same time, utilizing multiple CPU cores.
However, if you are using the EC, ER, EDAB or ERES API to export charts locally, the application typically runs in a single thread.
In the following code block, there’s an example of a single-threaded chart export using the EspressChart API.
// do not connect to the EspressManager server QbChart.setEspressManagerUsed(false); // our chart template is parameterized // it uses a single integer as the argument Object queryParams[] = new Object[1]; // set the argument to "1" queryParams[0] = 1; // create an instance of the QbChart object QbChart chart = new QbChart((Applet)null, "chartTemplate.pac", queryParams); // export the chart to a PNG file chart.export(QbChart.PNG, "exportedChart.png");
If you run the previous code in a loop (even with different parameter values), it can export as many charts as you want, however, it will export one chart at a time using a single CPU thread. Most of the CPU will be idle.
This will negatively impact the overall performance of your API implementation on a modern CPU with many threads. On the other hand, if you are exporting a single chart or just a few charts at the same time, the export should be finished within a couple of seconds maximum.
However, it is possible to utilize multiple CPU cores even via the EspressChart API without connecting to the EC, ER, EDAB or ERES server by utilizing Java multi threading. In this example, we are using EspressChart API but similar code can be used for speeding up EspressReport export too. To use this code with EspressReport, replace the QbChart Java class with QbReport. The rest of the code is the same for both EspressChart and Espress Report.
In this example, we want to export 10,000 charts. All charts will be using the same chart template (although it would be also possible for every chart to be different, with some code changes) but different data.
The chart template is parameterized. The chart takes one parameter – a customer ID (integer). The chart is loading its data from a MySQL 8 database.
This is how the chart template looks in Chart Designer.
We want to export 10,000 of these charts, each with a different customer ID in as little time as possible.
We’ll create a multi-threaded Java application using the EspressChart API.
We’ll create two Java classes:
BenchmarkRunner.java
The BenchmarkRunner.java class will be the executable class and it will control the multi-threaded application.
ChartExporter.java
The ChartExporter.java class will be doing the actual exporting.
private List<Integer> paramList, paramListSynchronized; /** * launches the multi-threaded benchmark * * @param args default argument, no arguments needed for this app */ public static void main(String[] args) { new BenchmarkRunner(); } public BenchmarkRunner() { // set the number of application threads that will be used for export // set as many threads as your CPU can handle // using more threads than your CPU has is counter-productive // for instance for AMD 5950X, we'll use 32 threads int threadCount = 32; // fetch the parameter list // a reference of this list will be passed to all threads paramList = new ArrayList<>(); for (int k = 1; k <= 50; k++) { paramList.add(k); } paramListSynchronized = Collections.synchronizedList(paramList); // we're starting the export, save the current system time in milliseconds long starttime = System.currentTimeMillis(); // create a new Fixed Thread Pool in the size of the threadCount ExecutorService executor = Executors.newFixedThreadPool(threadCount); List<Future<?>> futures = new ArrayList<>(); // start all threads one by one for (int i = 1; i <= threadCount; i++) { // create a new thread // initialize the ChartBenchmarkTemplate class using it's only constructor // pass the paramList as a reference Future<?> f = executor .submit(new ChartExporter(i, "templates/StatementChart.pac", paramListSynchronized)); // add to the futures list so we have a list of all threads we launched futures.add(f); } // iterate over the threads for (Future<?> f : futures) { try { // wait for the thread to finish f.get(); } catch (InterruptedException e) { // basic error handling, use something better in your project e.printStackTrace(); } catch (ExecutionException e) { // basic error handling, use something better in your project e.printStackTrace(); } } // all threads have finished, print out the time difference (in milliseconds) // and exit the application System.out.println("--- All threads completed in " + (System.currentTimeMillis() - starttime) + " miliseconds"); // we're done, exit the application System.exit(0); } /** * initialize the chart export thread * * @param threadid the number of the thread. Informative. Only used in a * println * @param templateName the name of the chart template that will be exported * @param params the list of all remaining parameters waiting to be * exported */ public ChartExporter(int threadid, String templateName, List<Integer> params) { // do not use EspressManager for the export QbChart.setEspressManagerUsed(false); this.threadid = threadid; this.template = templateName; this.params = params; } /** * Get a parameter out of the params list and export it */ public void export() { // prepare a variable that will store the parameter Object queryParams[] = new Object[1]; // while the params list still contains parameters to be exported, take one // parameter and export it while (params.size() > 0) { // it's important to handle the params list in a synchronized block to prevent // concurrent modification // synchronize by the params list only, all other commands can run in parallel synchronized (params) { // take and remove the last parameter from the params list and save it in this // thread queryParams[0] = params.remove(params.size() - 1); } // just prints an informative message System.out.println("Thread nr. " + threadid + " exporting chart ID " + queryParams[0]); // load the chart from the template, apply the parameter QbChart chart = new QbChart((Applet) null, template, queryParams); // export the chart chart.export(QbChart.PNG, "dist/StatementChartID_" + queryParams[0] + ".png"); } }
This application can use all of our CPU threads to speed-up the exporting.
Please note that this application makes sense only if you want to export many charts as quickly as possible. Export of a single chart will still be single-threaded.
You can download the full source code, including the chart template file here: https://www.quadbase.com/downloads/QuadbaseMultiThreadedExport.zip