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:  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]
