Slide.GetThumbnail Method Is Locked in Multi Threads while Converting Slides from PPTX File to Images in C# and Java

Looks like there is a lock for get thumbnail for different presentations, my logic is simple, just receive pptx file, and convert to thumbnails, then return to website. I use write a multiprocessing in python to requests this api, it cannot be handled in multi-threading mode.

Profile:
image.png (237.9 KB)

Sample code and demo presentation:
ConsoleAppCore.zip (1001.6 KB)

Download dll for .NET core usage, and unzip into $(MSBuildProjectDirectory)\Aspose.Slides\net6.0\crossplatform

Hardware:
i9 13900K + 128G Memory + 1T SSD.

Test results:

1
thread: 1, time: 2498 ms
total: 2504 ms

5
thread: 1, time: 4219 ms
thread: 2, time: 4228 ms
thread: 3, time: 4243 ms
thread: 4, time: 4243 ms
thread: 5, time: 4244 ms
total: 4249 ms

10
thread: 1, time: 6163 ms
thread: 2, time: 6165 ms
thread: 3, time: 6169 ms
thread: 4, time: 6173 ms
thread: 5, time: 6182 ms
thread: 6, time: 6194 ms
thread: 7, time: 6204 ms
thread: 8, time: 6202 ms
thread: 9, time: 6205 ms
thread: 10, time: 6206 ms

total: 6214 ms

I have test same logic in java, same results as C#.

package org.example;

import com.aspose.slides.*;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
import java.util.concurrent.ThreadPoolExecutor;

import static java.util.concurrent.Executors.*;

public class App {
    public static void main(String[] args) throws IOException {
        License license = new License();
        InputStream licenseStream = App.class.getResourceAsStream("/license.txt");
        license.setLicense(licenseStream);
        System.out.println("License set successfully.");

        InputStream fileStream = App.class.getResourceAsStream("/test.pptx");
        byte[] bytes = fileStream.readAllBytes();


        Scanner scanner = new Scanner(System.in);
        System.out.println("Enter the count:");
        int count = scanner.nextInt();

        long startTime = System.currentTimeMillis();
        ThreadPoolExecutor executor = (ThreadPoolExecutor) newFixedThreadPool(count);
        for (int i = 0; i < count; i++) {
            int index = i;
            executor.execute(() -> {
                try {
                    exportThumbnails(bytes, index);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        }
        executor.shutdown();
        try {
            executor.awaitTermination(Long.MAX_VALUE, java.util.concurrent.TimeUnit.NANOSECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("total time: " + (endTime - startTime) + " ms");
    }

    static void exportThumbnails(byte[] bytes, int threadNumber) throws IOException {
        System.out.println("thread: " + threadNumber + " started");
        long startTime = System.currentTimeMillis();
        InputStream inputStream = new ByteArrayInputStream(bytes);
        Presentation presentation = new Presentation(inputStream);
        for (ISlide slide : presentation.getSlides()) {
            BufferedImage image = slide.getThumbnail(1f, 1f);
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ImageIO.write(image, "jpeg", outputStream);
        }
        long endTime = System.currentTimeMillis();
        System.out.printf("thread: %d, total: %d ms\n", threadNumber, endTime - startTime);
    }
}
···

In multi-threading export presentations to thumbnails, it will not execute in parallel.

@islide,
Thank you for contacting support.

Could you please describe in more detail or share instructions on how to see the problem in the .NET project?

Please also note that the Presentation class is disposable and you should use the using statement or the Dispose method for Presentation objects.

@andrey.potapov

When I export same presentation in memorystream from bytes to thumbnails, in multi threads, it will not render each presentation in parallels.

eg,
1 thread, each presentation takes 2.5s
2 thread, each presentation takes 4.2s
10 thread, each presentation takes 6.2s

Why each presentation not same time, it should be ~2.5s, because I run them in parallels.

Even I have 32 core CPU, it will not render thumbnail in each threads separately, when deep look into Timeline Profile, I noticed there is a lock when get thumbnail from Slide. I guess, maybe GetThumbnail is locked in code, even if I run in threads.

@islide,
Thank you for the details. Could you please also indicate which Aspose.Slides for .NET version you used?

@andrey.potapov
Any version, I have tried from 23.3 to 23.12.

@islide,
Thank you for the information. I’ve reproduced the problem you described.

We have opened the following new ticket(s) in our internal issue tracking system and will deliver their fixes according to the terms mentioned in Free Support Policies.

Issue ID(s): SLIDESNET-44389

You can obtain Paid Support Services if you need support on a priority basis, along with the direct access to our Paid Support management team.

Please also share the following additional information if it is possible:

  • OS version on which your tests were performed

@andrey.potapov

Both in Windows 11 and Docker Container.

@andrey.potapov

After deep profile with sample code, I found two long time logic in your code when called Slide.GetThumbnail

  1. static lock

CleanShot 2024-01-11 at 08.43.56@2x.png (354.5 KB)

  1. gc

CleanShot 2024-01-11 at 08.45.21@2x.jpg (200.2 KB)

@islide,
Thank you for the additional information. I’ve added it to the issue ticket.