Massive Slow Down when Converting Presentations Using Aspose.Slides for Java

I have a reasonably complicated program that takes an input file and converts it using different layouts. amazon lambda functions timeout after 15 minutes and I’m no longer able to run the latest code due to an over 10 fold decrease in running time.

I’ve just run some timing tests on various 2022 versions of aspose.
These are the timings to convert a 54 slide PowerPoint deck onto 6 different layouts (with various bits of code to move frames around)

aspose version	    time 
22.1             	2m17.254s
22.2             	2m27.812s
22.3             	2m31.069s
22.4             	5m58.469s
22.5             	6m4.615s
22.7             	25m17.604s
22.8             	25m25.207s

N.B. my code is entirely unchanged - I just recompiled using different versions of the aspose library.
Do you run any timing tests on your library and have you seen a similar degradation in speed?

@JamesDriscoll,
Thank you for contacting support.

Our development team is constantly working to improve the performance of Aspose.Slides. Some problems may appear with a particular presentation. For example, WPS Office creates presentations differently than Microsoft PowerPoint. That’s why it is important for us to get samples. Please share the following data and information:

  • input presentation file
  • code example that reproduces the problem
  • OS version on which the code was executed
  • JDK version in your app
  • any additional information that will help us solve the issue

Sadly, the reason I asked if you run any timing tests is that I’m unable to share company confidential data.

This is a general problem not related to one specific document, (the input files are a mixture of ones generated in Microsoft PowerPoint and ones generated by Aspose) The code is over 130Kb and would require too much time to explain but I stress again there has been over a 10 fold decrease in speed of the aspose library using identical code on my side. It’s using JDK 1.8 (a limitation of the amazon lambda layer we have to use)

OS are Amazon Linux 2 and Ubuntu 20.04.5 LTS

I’m reverting my set up to Aspose 22.5 (or maybe 22.3) today and will test it doesn’t time out. If I find time I’ll see if I can do some profiling to see what Aspose functions it’s spending most time in.
Along with general walking through collections I do quite a bit of
ISlide addClone(ISlide sourceSlide, ILayoutSlide destLayout)
and some shape collection reordering. (I’ve usually got 3 PowerPoints in memory - one I’m creating , a source file and one that contains a ‘master layout’)

Hope this helps

I hope this helps and you can duplicate the issue at your end with a simpler code set

@JamesDriscoll,
Thank you for the additional information. We perform timing tests periodically. Unfortunately your information is not enough yet. I may add a ticket for the issue to our issue tracking system, but our developers will request a code example and sample presentation. Please try to isolate the problem. Thank you for helping make Aspose.Slides better.

I’ve run some method profiling (using flightRecorder sampling over time)
on 3 different versions. (Identical code at my side just different version of aspose)

Note the Count below isn’t the number of method calls (that will be the same for all 3 tests) it’s the number of time slices that happend during the running of the program where the stack pointer happened to be in this function

Aspose 22.8 (takes over 25 minutes to complete)
Method Count Percentage
com.aspose.slides.internal.h0.for.do(while, throw, case) 9,571 61.7 %

Aspose 22.5 (takes over 6 minutes to compete)
Method Count Percentage
com.aspose.slides.internal.h0.for.do(while, throw, case) 1,437 23.3 %

Aspose 22.3 (around 2.5 minutes to complete)
Method Count Percentage
com.aspose.slides.internal.h0.for.do(while, throw, case) 24 0.551 %

So it’s looking very much like the bottle neck is whatever is inside
com.aspose.slides.internal.h0.for.do

@JamesDriscoll,
I’ve added a ticket with ID SLIDESJAVA-38908 to our issue tracking system. We apologize for any inconvenience. Our development team will investigate the case. We will inform you of any progress.

1 Like

@JamesDriscoll,
Our developers have investigated the case as much as possible. They found this method to be related to loading images, but we haven’t made any changes to this code in the last six months. Our developers assume that the problem with the com.aspose.slides.internal.h0.for.do(while, throw, case) method in this case is due to a lack of available RAM. I hope this information helps you.

Hi Andrey - I have a 16Gb test machine and no amount of adjusting -Xmx values has any effect on the problem.
version 22.8 is much slower the 22.5 which is slower than 22.3

If the function hasn’t been changed in the last 6 months is it being called in places it didn’t used to get called?

I strongly suspect the problem is in internal code somewhere inside
boolean com.aspose.slides.BaseSlide.equals(IBaseSlide)

Is it allocating and deallocating images rather than keeping hold of them?

@JamesDriscoll

There were a few changes to improve the quality of text rendering and rendering of 3d effects, so there may be performance degradation! But to find the exact cause, we need a sample presentation and code snippet that reproduces this problem.

Could you please provide the sample presentation and code sample so we can reproduce the problem on our side?

Thank you!

As explained above I can’t share company confidential documents with you but this is very much a generic problem across the whole suite of documents. I’m not entirely sure why improvements in rendering would effect com.aspose.slides.BaseSlide.equals but I assume I can’t have access to your code to know for sure?

@JamesDriscoll

I tested the performance on several versions of Aspose.Slides using the following code snippet and found no performance issues:

int total = 0;
for (int i=0; i <100; i++) {
    Presentation p1 = new Presentation("welcome-to-powerpoint.pptx");
    ISlide slide = p1.getSlides().get_Item(1);
    try {
        LocalDateTime start = LocalDateTime.now();
        for (ISlide slide1 : p1.getSlides()) {
            boolean result = slide.equals(slide1);
        }
        LocalDateTime end = LocalDateTime.now();
        Duration duration = Duration.between(end, start);
        long diff = Math.abs(duration.toMillis());
        total += diff;
    } finally {
        if (p1 != null) p1.dispose();
    }
}

System.out.println("v22.1: " + total + "ms");

results:

v22.1: 22421ms
v22.4: 20692ms
v22.9: 20040ms

welcome-to-powerpoint.zip (3.6 MB)

I’ve had a crack at simplifying the problem enough so you have something to test…

public final class Test {


/**
 * 
 * @param add   slide to add
 * @param layout layout to use for the slide  (will add it if it's not already in the presentation)
 * @param pres prestation to add it to
 * @return  the new slide
 */
private static ISlide addSlideUsingLayoutToPresentation(ISlide add, ILayoutSlide layout, Presentation pres) {
    // check if layout already in presentation

    IGlobalLayoutSlideCollection presLayouts = pres.getLayoutSlides();
    boolean slideAdded = false;
    ISlide slide = null;
    System.out.println("check if layout already there "+ layout.getName());

    for (ILayoutSlide presLayout : presLayouts) {
        if (presLayout.equals( layout)) {// is this the equals that is taking an increasing amount of time??
            System.out.println("it's already there so use it");
            // using an existing layout
            slide = pres.getSlides().addClone(add, presLayout);
            slideAdded = true;
            break;
        }
    }
    if (!slideAdded) {
        // add the missing layout
        System.out.println("adding missing layout");
        layout = presLayouts.addClone(layout);
        slide = pres.getSlides().addClone(add, layout);
    }

 return slide;
}
public static void main(String[] args) {
    
com.aspose.slides.License license = new com.aspose.slides.License();
license.setLicense("Aspose.Slides.Java.lic");

try {
    int total=0;
    LocalDateTime start = LocalDateTime.now();

    String dataDir = new File(".").getAbsolutePath() + File.separator;

    Presentation in = new Presentation(dataDir+"in.pptx");
    Presentation out = new Presentation("master.potx");

    while(out.getSlides().size()>0){
        out.getSlides().removeAt(0);//remove any presentation slides in master layout
     }

     ISlide all[] = in.getSlides().toArray();
     

     for (int j = 0; j < all.length; j = j + 1) {

             ILayoutSlide layout;
             IGlobalLayoutSlideCollection outLayouts = out.getLayoutSlides();
             layout=null;
            // System.out.println("find layout  "+all[j].getLayoutSlide().getName());
             for(int k=0;k<outLayouts.size() && layout==null;k++){

                if(all[j].getLayoutSlide().getName().equals(outLayouts.get_Item(k).getName())){
              //      System.out.println("found it!");
                    layout=outLayouts.get_Item(k);
                }
             }

             if(layout==null){
                layout=all[j].getLayoutSlide();
                // System.out.println("no layout found in the master so adding the original ("+layout.getName()+")");
             }

             ISlide destinationSlide  = addSlideUsingLayoutToPresentation(all[j], layout, out);

     } 
    
    out.save(dataDir + "testout.pptx", SaveFormat.Pptx);                  

    LocalDateTime end = LocalDateTime.now();


    Duration duration = Duration.between(end, start);

    long diff = Math.abs(duration.toMillis())/1000;

    total += diff;
    System.out.println("aspose version "+License.getVersion()+ " time=" + total + "s");

} catch( Exception e){
System.out.println("Error  Exception in code: "+e.getMessage());
System.exit(1) ; }
} 

}

testdocs.zip (7.6 MB)
the interesting lines in the results are:
aspose version 22.3 time=62s
aspose version 22.8 time=520s

I’ve marked in the code the equals that appears to get progressively slower

1 Like

not sure what is going on here in the formatting did you get the zip with the test files in?testdocs.zip (7.6 MB)

@JamesDriscoll

Thank you for testing data files and sample code.

I have checked you code and got following results on my end:

aspose version 22.1 time=36s
aspose version 22.8 time=268s
aspose version 22.9 time=38s

Could you please try new version of Aspose.Slides for Java (22.9)?

As far as I can see this problem should not be there. However, I have forwarded your information to the development team for detailed investigation.

1 Like

for my simple example above it’s gone from
aspose version 22.3 time=62s
to
aspose version 22.9 time=89s

However, I’ve just tested my main app with 22.9
for a test file, it’s gone from 2mins 22sec in version 22.3
to 6mins 2sec in version in version 22.9
(so about 2 and half times slower).

There’s no mention of a fix in 22.9 release notes so I’m not sure what has changed to make the difference. It’s clearly an improvement on the 25 minutes it was taking version 22.8 but I hope you can track the issue down.

@JamesDriscoll
We will update you as soon as the development team completes the investigation.
Thank you for your patience.

1 Like

@JamesDriscoll
Our development team suggest to use the following code snippet to clone slides:

com.aspose.slides.License license = new com.aspose.slides.License();
license.setLicense(dataDir + "Product.Family.lic");
System.out.println("Start");
try {
    int total = 0;
    long startTime = System.currentTimeMillis();

    String dataDir = datapath;

    Presentation in = new Presentation(dataDir + "in.pptx");
    Presentation out = new Presentation(dataDir + "master.potx");

    while (out.getSlides().size() > 0) {
        out.getSlides().removeAt(0);//remove any presentation slides in master layout
    }

    ISlide all[] = in.getSlides().toArray();
    //Create list of used MasterSlides
    Map<String, IMasterSlide> usedMasterSlides = new HashMap<String, IMasterSlide>();
    for(int  i = 0; i < all.length; i++)
    {
        boolean masterSlideNotFound = true;
        for (int j = 0; j < out.getMasters().size(); j++) {
            if (usedMasterSlides.containsKey(all[i].getLayoutSlide().getMasterSlide().toString()))
            {
                usedMasterSlides.put(all[i].getLayoutSlide().getMasterSlide().toString(), usedMasterSlides.get(all[i].getLayoutSlide().getMasterSlide().toString()));
                masterSlideNotFound = false;
            }
        }
        if (masterSlideNotFound)
        {
            //Clone MasterSlide
            IMasterSlide clonedMasterSlide = out.getMasters().addClone(all[i].getLayoutSlide().getMasterSlide());
            usedMasterSlides.put(all[i].getLayoutSlide().getMasterSlide().toString(), clonedMasterSlide);
        }
    }

    for (int j = 0; j < all.length; j = j + 1) {
        System.out.println("Clone " + j);
        ISlide slide  = out.getSlides().addClone(all[j], usedMasterSlides.get(all[j].getLayoutSlide().getMasterSlide().toString()), false);
    }

    out.save(dataDir + "testout-"+License.getVersion()+".pptx", SaveFormat.Pptx);

    long endTime = System.currentTimeMillis();

    long diff = Math.abs(endTime - startTime) / 1000;

    total += diff;
    System.out.println("aspose version " + License.getVersion() + " time=" + total + "s");

} catch (Exception e) {
    System.out.println("Error  Exception in code: " + e.getMessage());
    System.exit(1);
}

Could you please try it on your end?

It’s not something I can try at my end, the simplified code I wrote to demonstrate the problem is much simpler than the actual code in the app. I don’t actually use the layout string name for comparisons (I have to deal with many versions of templates that all have same layout names but different content). I suspect you are suggesting I add all new layouts up front rather than adding them at the point I’m processing a presentation slide. I’m not sure why it should matter, and it certainly hasn’t mattered for the last couple of years. It’s there a change in documentation to explain this?

@JamesDriscoll
The code I suggested to use to clone slides is just a workaround until we find and fix the cause.

Thanks but it’s not something I can use (I’m actually processing multiple input decks individually to avoid too much memory burden so will never know what the full list of layouts I’ll be using is until the last input deck is processed). 22.9 had a big enough improvement over 22.8 that I can use that for now but will have to check each new update in case the problem returns