You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jira@kafka.apache.org by GitBox <gi...@apache.org> on 2020/10/26 03:19:54 UTC

[GitHub] [kafka] yuzawa-san opened a new pull request #9499: KAFKA-10470: Zstd upgrade and buffering

yuzawa-san opened a new pull request #9499:
URL: https://github.com/apache/kafka/pull/9499


   Zstd-jni 1.4.5-6 allocates large internal buffers inside of ZstdInputStream and ZstdOutputStream. This caused a lot of allocation and GC activity when creating and closing the streams. It also does not buffer the reads or writes. This causes inefficiency when DefaultRecord.writeTo() does a series of small single bytes reads using various ByteUtils methods. The JNI is more efficient if the writes of uncompressed data were flushed in large pieces rather than for each byte. This is due to the the expense of context switching between the Java code and the native code. This is also the case when reading as well. Per https://github.com/luben/zstd-jni/issues/141 the maintainer of zstd-jni and I agreed to not buffer reads and writes in favor of having the caller do that, so here we are updating the caller. Here is part of a flame graph of  CPU time spent in the single byte writes from Kafka:
   
   ![image](https://user-images.githubusercontent.com/1082334/97130235-3113cd00-1717-11eb-835c-cc145d549604.png)
   
   In this patch, I upgraded to the most recent zstd-jni version with the buffer reuse built-in. This was done in https://github.com/luben/zstd-jni/pull/143 and https://github.com/luben/zstd-jni/pull/146 Since we decided not to add additional buffering of input/output with zstd-jni, I added the BufferedInputStream and BufferedOutputStream to CompressionType.ZSTD just like we currently do for CompressionType.GZIP which also is inefficient for single byte reads and writes. I used the same buffer sizes as that existing implementation.
   
   NOTE: if so desired we could pass a wrapped BufferSupplier into the Zstd*Stream classes to have Kafka decide how the buffer recycling occurs. This functionality was added in the latter PR linked above. I am holding off on this since based on jmh benchmarking the performance gains were not clear and personally I don't know if it worth the complexity of trying to hack around the reflection at this point in time. The zstd-jni uses a very similar default recycler as snappy does currently which seems to provide decent efficiency. While this PR fixes the defect, I feel that using BufferSupplier in both zstd-jni and snappy is outside of the scope of this bugfix and should be considered a separate improvement. I would prefer this change get merged in on its own since the performance gains here are very significant relative to the more incremental and minor optimizations which could be achieved by doing that separate work.
   
   There are some noticeable improvements in the JMH benchmarks (excerpt):
   
   BEFORE:
   ```
   Benchmark                                                                                                                    (bufferSupplierStr)  (bytes)  (compressionType)  (maxBatchSize)  (messageSize)  (messageVersion)   Mode  Cnt       Score     Error   Units
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed                                                CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15   27743.260 ± 673.869   ops/s
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.alloc.rate                                 CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    3399.966 ±  82.608  MB/sec
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.alloc.rate.norm                            CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15  134968.010 ±   0.012    B/op
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.churn.G1_Eden_Space                        CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    3850.985 ±  84.476  MB/sec
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.churn.G1_Eden_Space.norm                   CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15  152881.128 ± 942.189    B/op
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.churn.G1_Survivor_Space                    CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15     174.241 ±   3.486  MB/sec
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.churn.G1_Survivor_Space.norm               CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    6917.758 ±  82.522    B/op
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.count                                      CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    1689.000            counts
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.time                                       CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15   82621.000                ms
   JMH benchmarks done
   
   Benchmark                                                                                                    (bufferSupplierStr)  (bytes)  (compressionType)  (maxBatchSize)  (messageSize)  (messageVersion)   Mode  Cnt       Score       Error   Units
   RecordBatchIterationBenchmark.measureIteratorForBatchWithSingleMessage                                                    CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15   24095.711 ±   895.866   ops/s
   RecordBatchIterationBenchmark.measureIteratorForBatchWithSingleMessage:·gc.alloc.rate                                     CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    2932.289 ±   109.465  MB/sec
   RecordBatchIterationBenchmark.measureIteratorForBatchWithSingleMessage:·gc.alloc.rate.norm                                CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15  134032.012 ±     0.013    B/op
   RecordBatchIterationBenchmark.measureIteratorForBatchWithSingleMessage:·gc.churn.G1_Eden_Space                            CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    3282.912 ±   115.042  MB/sec
   RecordBatchIterationBenchmark.measureIteratorForBatchWithSingleMessage:·gc.churn.G1_Eden_Space.norm                       CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15  150073.914 ±  1342.235    B/op
   RecordBatchIterationBenchmark.measureIteratorForBatchWithSingleMessage:·gc.churn.G1_Survivor_Space                        CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15     149.697 ±     5.786  MB/sec
   RecordBatchIterationBenchmark.measureIteratorForBatchWithSingleMessage:·gc.churn.G1_Survivor_Space.norm                   CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    6842.462 ±    64.515    B/op
   RecordBatchIterationBenchmark.measureIteratorForBatchWithSingleMessage:·gc.count                                          CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    1449.000              counts
   RecordBatchIterationBenchmark.measureIteratorForBatchWithSingleMessage:·gc.time                                           CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15   82518.000                  ms
   RecordBatchIterationBenchmark.measureSkipIteratorForVariableBatchSize                                                     CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    1449.060 ±   230.498   ops/s
   RecordBatchIterationBenchmark.measureSkipIteratorForVariableBatchSize:·gc.alloc.rate                                      CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15     198.051 ±    31.532  MB/sec
   RecordBatchIterationBenchmark.measureSkipIteratorForVariableBatchSize:·gc.alloc.rate.norm                                 CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15  150502.519 ±     0.186    B/op
   RecordBatchIterationBenchmark.measureSkipIteratorForVariableBatchSize:·gc.churn.G1_Eden_Space                             CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15     200.064 ±    31.879  MB/sec
   RecordBatchIterationBenchmark.measureSkipIteratorForVariableBatchSize:·gc.churn.G1_Eden_Space.norm                        CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15  152569.341 ± 13826.686    B/op
   RecordBatchIterationBenchmark.measureSkipIteratorForVariableBatchSize:·gc.count                                           CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15      91.000              counts
   RecordBatchIterationBenchmark.measureSkipIteratorForVariableBatchSize:·gc.time                                            CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15   75869.000                  ms
   RecordBatchIterationBenchmark.measureStreamingIteratorForVariableBatchSize                                                CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    2609.660 ±  1145.160   ops/s
   RecordBatchIterationBenchmark.measureStreamingIteratorForVariableBatchSize:·gc.alloc.rate                                 CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15     815.441 ±   357.818  MB/sec
   RecordBatchIterationBenchmark.measureStreamingIteratorForVariableBatchSize:·gc.alloc.rate.norm                            CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15  344309.097 ±     0.238    B/op
   RecordBatchIterationBenchmark.measureStreamingIteratorForVariableBatchSize:·gc.churn.G1_Eden_Space                        CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15     808.952 ±   354.975  MB/sec
   RecordBatchIterationBenchmark.measureStreamingIteratorForVariableBatchSize:·gc.churn.G1_Eden_Space.norm                   CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15  345712.061 ± 51434.034    B/op
   RecordBatchIterationBenchmark.measureStreamingIteratorForVariableBatchSize:·gc.churn.G1_Old_Gen                           CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15       0.019 ±     0.042  MB/sec
   RecordBatchIterationBenchmark.measureStreamingIteratorForVariableBatchSize:·gc.churn.G1_Old_Gen.norm                      CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15      18.615 ±    42.045    B/op
   RecordBatchIterationBenchmark.measureStreamingIteratorForVariableBatchSize:·gc.churn.G1_Survivor_Space                    CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15      24.132 ±    12.254  MB/sec
   RecordBatchIterationBenchmark.measureStreamingIteratorForVariableBatchSize:·gc.churn.G1_Survivor_Space.norm               CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15   13540.960 ± 14649.192    B/op
   RecordBatchIterationBenchmark.measureStreamingIteratorForVariableBatchSize:·gc.count                                      CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15     148.000              counts
   RecordBatchIterationBenchmark.measureStreamingIteratorForVariableBatchSize:·gc.time                                       CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15   23848.000                  ms
   JMH benchmarks done
   ```
   
   AFTER:
   ```
   Benchmark                                                                                                                (bufferSupplierStr)  (bytes)  (compressionType)  (maxBatchSize)  (messageSize)  (messageVersion)   Mode  Cnt       Score      Error   Units
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed                                            CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15  147792.454 ± 2721.318   ops/s
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.alloc.rate                             CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    2708.481 ±   50.012  MB/sec
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.alloc.rate.norm                        CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15   20184.002 ±    0.002    B/op
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.churn.G1_Eden_Space                    CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    2732.667 ±   59.258  MB/sec
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.churn.G1_Eden_Space.norm               CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15   20363.460 ±  120.585    B/op
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.churn.G1_Old_Gen                       CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15       0.042 ±    0.033  MB/sec
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.churn.G1_Old_Gen.norm                  CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15       0.316 ±    0.249    B/op
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.count                                  CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15     833.000             counts
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.time                                   CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    8390.000                 ms
   JMH benchmarks done
   
   Benchmark                                                                                                (bufferSupplierStr)  (bytes)  (compressionType)  (maxBatchSize)  (messageSize)  (messageVersion)   Mode  Cnt       Score      Error   Units
   RecordBatchIterationBenchmark.measureIteratorForBatchWithSingleMessage                                                CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15  166786.092 ± 3285.702   ops/s
   RecordBatchIterationBenchmark.measureIteratorForBatchWithSingleMessage:·gc.alloc.rate                                 CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    2926.914 ±   57.464  MB/sec
   RecordBatchIterationBenchmark.measureIteratorForBatchWithSingleMessage:·gc.alloc.rate.norm                            CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15   19328.002 ±    0.002    B/op
   RecordBatchIterationBenchmark.measureIteratorForBatchWithSingleMessage:·gc.churn.G1_Eden_Space                        CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    2938.541 ±   66.850  MB/sec
   RecordBatchIterationBenchmark.measureIteratorForBatchWithSingleMessage:·gc.churn.G1_Eden_Space.norm                   CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15   19404.357 ±  177.485    B/op
   RecordBatchIterationBenchmark.measureIteratorForBatchWithSingleMessage:·gc.churn.G1_Old_Gen                           CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15       0.516 ±    0.100  MB/sec
   RecordBatchIterationBenchmark.measureIteratorForBatchWithSingleMessage:·gc.churn.G1_Old_Gen.norm                      CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15       3.409 ±    0.657    B/op
   RecordBatchIterationBenchmark.measureIteratorForBatchWithSingleMessage:·gc.churn.G1_Survivor_Space                    CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15       0.032 ±    0.131  MB/sec
   RecordBatchIterationBenchmark.measureIteratorForBatchWithSingleMessage:·gc.churn.G1_Survivor_Space.norm               CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15       0.207 ±    0.858    B/op
   RecordBatchIterationBenchmark.measureIteratorForBatchWithSingleMessage:·gc.count                                      CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15     834.000             counts
   RecordBatchIterationBenchmark.measureIteratorForBatchWithSingleMessage:·gc.time                                       CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    9370.000                 ms
   RecordBatchIterationBenchmark.measureSkipIteratorForVariableBatchSize                                                 CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15   15988.116 ±  137.427   ops/s
   RecordBatchIterationBenchmark.measureSkipIteratorForVariableBatchSize:·gc.alloc.rate                                  CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15     448.636 ±    3.851  MB/sec
   RecordBatchIterationBenchmark.measureSkipIteratorForVariableBatchSize:·gc.alloc.rate.norm                             CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15   30907.698 ±    0.020    B/op
   RecordBatchIterationBenchmark.measureSkipIteratorForVariableBatchSize:·gc.churn.G1_Eden_Space                         CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15     450.905 ±    5.587  MB/sec
   RecordBatchIterationBenchmark.measureSkipIteratorForVariableBatchSize:·gc.churn.G1_Eden_Space.norm                    CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15   31064.113 ±  291.190    B/op
   RecordBatchIterationBenchmark.measureSkipIteratorForVariableBatchSize:·gc.churn.G1_Old_Gen                            CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15       0.043 ±    0.007  MB/sec
   RecordBatchIterationBenchmark.measureSkipIteratorForVariableBatchSize:·gc.churn.G1_Old_Gen.norm                       CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15       2.931 ±    0.493    B/op
   RecordBatchIterationBenchmark.measureSkipIteratorForVariableBatchSize:·gc.count                                       CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15     790.000             counts
   RecordBatchIterationBenchmark.measureSkipIteratorForVariableBatchSize:·gc.time                                        CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15     999.000                 ms
   RecordBatchIterationBenchmark.measureStreamingIteratorForVariableBatchSize                                            CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15   11345.169 ±  206.528   ops/s
   RecordBatchIterationBenchmark.measureStreamingIteratorForVariableBatchSize:·gc.alloc.rate                             CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    2314.800 ±   42.094  MB/sec
   RecordBatchIterationBenchmark.measureStreamingIteratorForVariableBatchSize:·gc.alloc.rate.norm                        CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15  224714.266 ±    0.028    B/op
   RecordBatchIterationBenchmark.measureStreamingIteratorForVariableBatchSize:·gc.churn.G1_Eden_Space                    CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    2320.213 ±   45.521  MB/sec
   RecordBatchIterationBenchmark.measureStreamingIteratorForVariableBatchSize:·gc.churn.G1_Eden_Space.norm               CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15  225235.965 ±  803.309    B/op
   RecordBatchIterationBenchmark.measureStreamingIteratorForVariableBatchSize:·gc.churn.G1_Old_Gen                       CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15       0.026 ±    0.005  MB/sec
   RecordBatchIterationBenchmark.measureStreamingIteratorForVariableBatchSize:·gc.churn.G1_Old_Gen.norm                  CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15       2.551 ±    0.455    B/op
   RecordBatchIterationBenchmark.measureStreamingIteratorForVariableBatchSize:·gc.count                                  CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15     994.000             counts
   RecordBatchIterationBenchmark.measureStreamingIteratorForVariableBatchSize:·gc.time                                   CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    1189.000                 ms
   JMH benchmarks done
   ```
   
   ### Committer Checklist (excluded from commit message)
   - [ ] Verify design and implementation 
   - [ ] Verify test coverage and CI build status
   - [ ] Verify documentation (including upgrade notes)
   


----------------------------------------------------------------
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.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [kafka] yuzawa-san commented on pull request #9499: KAFKA-10470: Zstd upgrade and buffering

Posted by GitBox <gi...@apache.org>.
yuzawa-san commented on pull request #9499:
URL: https://github.com/apache/kafka/pull/9499#issuecomment-724718123


   There is a newer zstd-jni version, so I'm going to push the newer one shortly after my local checks pass.


----------------------------------------------------------------
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.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [kafka] yuzawa-san commented on pull request #9499: KAFKA-10470: Zstd upgrade and buffering

Posted by GitBox <gi...@apache.org>.
yuzawa-san commented on pull request #9499:
URL: https://github.com/apache/kafka/pull/9499#issuecomment-724882680


   ok most recent version is pushed and checks pass now.


----------------------------------------------------------------
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.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [kafka] chia7712 commented on pull request #9499: KAFKA-10470: Zstd upgrade and buffering

Posted by GitBox <gi...@apache.org>.
chia7712 commented on pull request #9499:
URL: https://github.com/apache/kafka/pull/9499#issuecomment-716935467


   @yuzawa-san Thanks for great profile and explanation. I'm eager (too) to see this patch to be merged since it not only improves the memory usage but also brings better performance (especially for small message). The following story is just my two cents. The reason to use kafka pool instead of (zstd) recycling buffers is not only about performance but also other improvements. For example, synchronization issue (that was discussed in https://github.com/luben/zstd-jni/issues/131). Also, on our kafka (private branch), we introduces some sensors to (kafka) pool to get oom/duration metrics to trace compression status. In short, it would be great to introduce kafka pool to compression. (We don't use snappy since it is not best compression on our use cases)
   
   At any rate, I'm +1 to merge this PR as it does bring significant improvement. We can address kafka pool in next PR.
   
   @yuzawa-san thank you (again) that your effort on both zstd and kafka!


----------------------------------------------------------------
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.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [kafka] chia7712 merged pull request #9499: KAFKA-10470: Zstd upgrade and buffering

Posted by GitBox <gi...@apache.org>.
chia7712 merged pull request #9499:
URL: https://github.com/apache/kafka/pull/9499


   


----------------------------------------------------------------
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.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [kafka] chia7712 edited a comment on pull request #9499: KAFKA-10470: Zstd upgrade and buffering

Posted by GitBox <gi...@apache.org>.
chia7712 edited a comment on pull request #9499:
URL: https://github.com/apache/kafka/pull/9499#issuecomment-716935467






----------------------------------------------------------------
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.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [kafka] yuzawa-san commented on a change in pull request #9499: KAFKA-10470: Zstd upgrade and buffering

Posted by GitBox <gi...@apache.org>.
yuzawa-san commented on a change in pull request #9499:
URL: https://github.com/apache/kafka/pull/9499#discussion_r512045768



##########
File path: clients/src/main/java/org/apache/kafka/common/record/CompressionType.java
##########
@@ -119,7 +119,9 @@ public InputStream wrapForInput(ByteBuffer inputBuffer, byte messageVersion, Buf
         @Override
         public OutputStream wrapForOutput(ByteBufferOutputStream buffer, byte messageVersion) {
             try {
-                return (OutputStream) ZstdConstructors.OUTPUT.invoke(buffer);
+                // Set input buffer (uncompressed) to 16 KB (none by default) to ensure reasonable performance
+                // in cases where the caller passes a small number of bytes to write (potentially a single byte)
+                return new BufferedOutputStream((OutputStream) ZstdConstructors.OUTPUT.invoke(buffer), 16 * 1024);

Review comment:
       This should be safe. It is used already in this file to wrap the GZIPOutputStream which also does not buffer uncompressed data prior to compression.




----------------------------------------------------------------
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.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [kafka] yuzawa-san commented on pull request #9499: KAFKA-10470: Zstd upgrade and buffering

Posted by GitBox <gi...@apache.org>.
yuzawa-san commented on pull request #9499:
URL: https://github.com/apache/kafka/pull/9499#issuecomment-716620712


   @chia7712  @ijuma  The main reason I'm holding off using the BufferSupplier is because of part of the  "after" benchmark looks like "before" benchmark:
   ```
   Benchmark                                                                                                                    (bufferSupplierStr)  (bytes)  (compressionType)  (maxBatchSize)  (messageSize)  (messageVersion)   Mode  Cnt       Score      Error   Units
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed                                                CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15   26302.815 ± 2849.585   ops/s
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.alloc.rate                                 CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    3613.977 ±  391.633  MB/sec
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.alloc.rate.norm                            CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15  151328.008 ±    0.012    B/op
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.churn.G1_Eden_Space                        CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    3638.121 ±  394.668  MB/sec
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.churn.G1_Eden_Space.norm                   CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15  152340.806 ±  614.236    B/op
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.churn.G1_Survivor_Space                    CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15     164.799 ±   16.872  MB/sec
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.churn.G1_Survivor_Space.norm               CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    6907.707 ±  146.892    B/op
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.count                                      CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15    1602.000             counts
   CompressedRecordBatchValidationBenchmark.measureValidateMessagesAndAssignOffsetsCompressed:·gc.time                                       CREATE   RANDOM               ZSTD             200           1000                 2  thrpt   15   80186.000                 ms
   JMH benchmarks done
   ```
   
   This is slow still because changes like these https://github.com/apache/kafka/pull/9229/files#diff-8c632aabf49c81fcae204e3cb479ae861c8243b721ebb2024b96faee468cc9bbL105 and https://github.com/apache/kafka/pull/9229/files#diff-8c632aabf49c81fcae204e3cb479ae861c8243b721ebb2024b96faee468cc9bbL401 are not merged, so currently if the BufferSupplier were to be used it would not actually be recycling buffers in certain cases, in particular within the LogValidator as indicated in that benchmark above. NOTE: the RecordBatchIterationBenchmark looks basically the same since that code uses the BufferSupplier provided by the JMH harness, so recycling is actually occurring. The CompressedRecordBatchValidationBenchmark does not and fixing that is the crux of that other PR which is indeed correct.
   
   Given the snappy recycler implementation is currently acceptable and that zstd-jni uses a very similar one, I propose we lean on zstd-jni's default recycler for the time being (to guarantee buffer recycling regardless of the type of BufferSupplier it gets) and only after https://github.com/apache/kafka/pull/9229 or something like it is merged then we revisit using the BufferSupplier in zstd. I agree in the long term that is the best case, but I don't want a half-fix when we can have a fix here that gets us 98% of the way there. I an eager to get a fast zstd implementation in since we have very promising results but had to stop using it because of the negative performance impact.
   
   Additionally there are some open questions (mostly beyond the scope of this bugfix):
   * The wrapForOutput does not have a BufferSupplier, so where does it come from: static constant/singleton, somewhere else, break the API, do we just lean on the default zstd-jni recycler?
   * Creating the wrapper around BufferSupplier that implements zstd-jni's BufferPool means implementing BufferPool but the checkstyle prohbits this. Would we be allowed to import it? I see jpountz lz4 is allowed.
   * Do we want to do the recycling for snappy? I see see SnappyOutputStream seems to allow for one to pass in a BufferAllocatorFactory implementation which may be interoperable with BufferSupplier. However this may be moot due to my first point. The SnappyInputStream does not seem to accept any recyclers. There are SnappyFramedInputStream and SnappyFramedOutputStream which seem to accept another snappy-specific recycler "BufferPool". Maybe those could be promising?
   * Do we want to do the recycling for GZIP? GZIP seems like a lost cause since that stuff related is buffers is hidden within the standard library, unless it is ported over like the LZ4 streams. 
   * Do we want to create versions of BufferedInputStream and BufferedOutputStream which use BufferSupplier?
   * Does any of this work interfere with any future work related to passing in compressor configuration parameters?


----------------------------------------------------------------
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.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [kafka] ijuma commented on a change in pull request #9499: KAFKA-10470: Zstd upgrade and buffering

Posted by GitBox <gi...@apache.org>.
ijuma commented on a change in pull request #9499:
URL: https://github.com/apache/kafka/pull/9499#discussion_r512056003



##########
File path: gradle/dependencies.gradle
##########
@@ -116,7 +116,7 @@ versions += [
   testRetryPlugin: "1.1.6",
   zinc: "1.3.5",
   zookeeper: "3.5.8",
-  zstd: "1.4.5-6"
+  zstd: "1.4.5-8"

Review comment:
       Ah, you explained this in the PR description. Let me think about that.




----------------------------------------------------------------
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.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [kafka] ijuma commented on a change in pull request #9499: KAFKA-10470: Zstd upgrade and buffering

Posted by GitBox <gi...@apache.org>.
ijuma commented on a change in pull request #9499:
URL: https://github.com/apache/kafka/pull/9499#discussion_r512054037



##########
File path: gradle/dependencies.gradle
##########
@@ -116,7 +116,7 @@ versions += [
   testRetryPlugin: "1.1.6",
   zinc: "1.3.5",
   zookeeper: "3.5.8",
-  zstd: "1.4.5-6"
+  zstd: "1.4.5-8"

Review comment:
       This change means we are using the recycling buffer pool, right? I think we should be passing our own buffer pool.




----------------------------------------------------------------
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.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [kafka] chia7712 commented on a change in pull request #9499: KAFKA-10470: Zstd upgrade and buffering

Posted by GitBox <gi...@apache.org>.
chia7712 commented on a change in pull request #9499:
URL: https://github.com/apache/kafka/pull/9499#discussion_r511726299



##########
File path: clients/src/main/java/org/apache/kafka/common/record/CompressionType.java
##########
@@ -119,7 +119,9 @@ public InputStream wrapForInput(ByteBuffer inputBuffer, byte messageVersion, Buf
         @Override
         public OutputStream wrapForOutput(ByteBufferOutputStream buffer, byte messageVersion) {
             try {
-                return (OutputStream) ZstdConstructors.OUTPUT.invoke(buffer);
+                // Set input buffer (uncompressed) to 16 KB (none by default) to ensure reasonable performance
+                // in cases where the caller passes a small number of bytes to write (potentially a single byte)
+                return new BufferedOutputStream((OutputStream) ZstdConstructors.OUTPUT.invoke(buffer), 16 * 1024);

Review comment:
       Is ```BufferedOutputStream``` good to other compression also?




----------------------------------------------------------------
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.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org