fperez-RatedPower opened a new issue, #2302:
URL: https://github.com/apache/fory/issues/2302

   ### Question
   
   Hi,
   
   We are developing an application in which we need to deserialize thousands 
of files as quickly as possible. Learning about Fury has been great, in our 
internal benchmarks it was more than 90% faster than normal Java serialization, 
with minimal code!
   
   However, at some point we started running the deserialization process in 
parallel, using about 10 threads. We found that Fury does not scale well in 
parallel, there is some kind of bottleneck. I think that the bottleneck is 
apparent in this chart:
   
   
![Image](https://github.com/user-attachments/assets/85363947-fa48-4911-b3b5-a583f93f8470)
   
   We are running this test in a PC with a core 9 ultra, and 64 GB of RAM. I do 
not think my PC is bottlenecking with this test, even with 20 threads. We have 
also observed the same issue in our cloud cluster.
   
   To elaborate the chart, we used the test below. The vertical axis shows the 
ratio of parallel_time / serial_time. I also added the ideal performance as a 
comparison. We are aware of Amdahl's law, so we know that the ideal speed up is 
not realistic. But we would like to see better performance and scaling. 
   
   Is there any configuration or setting that we can change to improve this 
situation? Is it possible that there is a bug or bottleneck of some kind in 
Fury? Maybe our testing methodology is incorrect?
   
   Thanks in advance for your help!
   
   This is the code we used to measure the performance:
   
   ```
   import org.apache.fury.Fury;
   import org.apache.fury.ThreadSafeFury;
   import org.apache.fury.config.Language;
   
   import java.nio.charset.StandardCharsets;
   import java.util.ArrayList;
   import java.util.List;
   import java.util.Random;
   import java.util.UUID;
   import java.util.concurrent.Callable;
   import java.util.concurrent.ExecutorService;
   import java.util.concurrent.Executors;
   import java.util.concurrent.Future;
   
   class FurySerializationGithubTest {
   
        private static final int MAX_ARRAY = 500;
        private static final int MAX_CHILDREN = 1_000;
        private static final Random r = new Random();
   
        public static void main(String[] args) {
                FuryParentDTO parentDTO = buildDTO();
                SerializeFury serializeFury = new SerializeFury();
                byte[] serialized = serializeFury.serialize(parentDTO);
   
                System.out.println("starting warm up");
                // Warm up
                for(int i = 0; i < 10; i++) {
                        serializeFury.deserialize(serialized);
                }
                System.out.println("finished warm up");
   
                int timesToDeserialize = 20_000;
                long serialStartTime = System.nanoTime();
                serializeFury = new SerializeFury();
                for(int i = 0; i < timesToDeserialize; i++) {
                        serializeFury.deserialize(serialized);
                }
                long serialTime = System.nanoTime() - serialStartTime;
   
                int threads = 10;
                int unitsPerThread = timesToDeserialize / threads;
                long parallelStartTime = System.nanoTime();
                try(ExecutorService executorService = 
Executors.newFixedThreadPool(threads)) {
                        List<Future<FuryParentDTO>> futures = new ArrayList<>();
                        for(int i = 0; i < threads; i++) {
                                Callable<FuryParentDTO> callable = () -> {
                                        SerializeFury sir = new SerializeFury();
                                        for(int j = 0; j < unitsPerThread - 1; 
j++) {
                                                sir.deserialize(serialized);
                                        }
                                        return sir.deserialize(serialized);
                                };
                                Future<FuryParentDTO> future = 
executorService.submit(callable);
                                futures.add(future);
                        }
   
                        for(Future<FuryParentDTO> future : futures) {
                                future.get();
                        }
                } catch(Exception e) {
                        throw new RuntimeException(e);
                }
                long parallelTime = System.nanoTime() - parallelStartTime;
   
                System.out.println("Serial deserialize time: " + serialTime / 
1e9 + "seconds");
                System.out.println("Parallel deserialize time: " + parallelTime 
/ 1e9 + " seconds");
                System.out.println("Parallel time / serial time: " + 1.0 * 
parallelTime / serialTime);
        }
   
        private static class SerializeFury {
   
                private final ThreadSafeFury fury;
   
                public SerializeFury() {
                        this.fury = createFuryInstance();
                }
   
                public byte[] serialize(FuryParentDTO furyDTO) {
                        return fury.serializeJavaObject(furyDTO);
                }
   
                public FuryParentDTO deserialize(byte[] bytes) {
                        return fury.deserializeJavaObject(bytes, 
FuryParentDTO.class);
                }
   
                private static ThreadSafeFury createFuryInstance() {
                        // Disable logging to avoid noise
                        org.apache.fury.logging.LoggerFactory.disableLogging();
   
                        ThreadSafeFury fury = Fury.builder()
                                        .withLanguage(Language.JAVA)
                                        .requireClassRegistration(true)
                                        .withAsyncCompilation(false)
                                        .withIntCompressed(false)
                                        .withLongCompressed(false)
                                        .withNumberCompressed(false)
                                        .withBufferSizeLimitBytes(200 * 1024 * 
1024) // 200 MB
                                        .buildThreadSafeFury();
   
                        registerIntervalResultsClasses(fury);
   
                        return fury;
                }
   
                private static void 
registerIntervalResultsClasses(ThreadSafeFury fury) {
                        fury.register(FuryParentDTO.class);
                        fury.register(FuryChildDTO.class);
                }
        }
   
   
        private static FuryParentDTO buildDTO() {
                FuryParentDTO furyParentDTO = new FuryParentDTO();
                furyParentDTO.setUuid(UUID.randomUUID());
                furyParentDTO.setValue1((float) Math.random());
                furyParentDTO.setValue2((float) Math.random());
   
                furyParentDTO.setDescription(getGenerateRandomString());
                furyParentDTO.setName(getGenerateRandomString());
   
                furyParentDTO.setChildren(generateChildren());
                return furyParentDTO;
        }
   
   
        private static List<FuryChildDTO> generateChildren() {
                List<FuryChildDTO> list = new ArrayList<>(MAX_CHILDREN);
                for(int i = 0; i < MAX_CHILDREN; i++) {
                        FuryChildDTO child = new FuryChildDTO();
                        child.setUuid(UUID.randomUUID());
                        child.setDescription(getGenerateRandomString());
                        child.setDescription(getGenerateRandomString());
                        child.setValue1(generateArray());
                        child.setValue2(generateArray());
   
                        list.add(child);
                }
                return list;
        }
   
        private static float[] generateArray() {
                float[] array = new float[MAX_ARRAY];
                for(int i = 0; i < MAX_ARRAY; i++) {
                        array[i] = r.nextFloat();
                }
                return array;
        }
   
        private static String getGenerateRandomString() {
                byte[] array = new byte[30]; // length is bounded by 7
                r.nextBytes(array);
                return new String(array, StandardCharsets.UTF_8);
        }
   
        private static class FuryParentDTO {
   
                UUID uuid;
                List<FuryChildDTO> children;
                String name;
                String description;
                float value1;
                float value2;
   
                public FuryParentDTO() {
                }
   
                public UUID getUuid() {
                        return uuid;
                }
   
                public void setUuid(UUID uuid) {
                        this.uuid = uuid;
                }
   
                public List<FuryChildDTO> getChildren() {
                        return children;
                }
   
                public void setChildren(List<FuryChildDTO> children) {
                        this.children = children;
                }
   
                public String getName() {
                        return name;
                }
   
                public void setName(String name) {
                        this.name = name;
                }
   
                public String getDescription() {
                        return description;
                }
   
                public void setDescription(String description) {
                        this.description = description;
                }
   
                public float getValue1() {
                        return value1;
                }
   
                public void setValue1(float value1) {
                        this.value1 = value1;
                }
   
                public float getValue2() {
                        return value2;
                }
   
                public void setValue2(float value2) {
                        this.value2 = value2;
                }
        }
   
        private static class FuryChildDTO {
   
                UUID uuid;
                String name;
                String description;
                float[] value1;
                float[] value2;
   
                public FuryChildDTO() {
                }
   
                public UUID getUuid() {
                        return uuid;
                }
   
                public void setUuid(UUID uuid) {
                        this.uuid = uuid;
                }
   
                public String getName() {
                        return name;
                }
   
                public void setName(String name) {
                        this.name = name;
                }
   
                public String getDescription() {
                        return description;
                }
   
                public void setDescription(String description) {
                        this.description = description;
                }
   
                public float[] getValue1() {
                        return value1;
                }
   
                public void setValue1(float[] value1) {
                        this.value1 = value1;
                }
   
                public float[] getValue2() {
                        return value2;
                }
   
                public void setValue2(float[] value2) {
                        this.value2 = value2;
                }
        }
   }
   ```


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to