timw opened a new pull request, #862:
URL: https://github.com/apache/pekko-grpc/pull/862

   # Motivation
   
   This work started slightly accidentally, as I was primarily trying to 
complete some work on the full grpc/grpc-web client <-> server RPC flows, and 
when I rebased found that the protocol reader/writer internals I was evolving 
had been exposed to the code generation.
   
   There are a couple of "reverts" in this MR, but I hope they're seen as 
building on the previous contributions - I think the addition of JMH 
benchmarking and the unary/strict optimisation ideas are significant, and this 
MR is primarily trying to retain and build on those benefits with simpler/more 
maintainable implementations.
   
   # Description of changes
   
   This MR improves the benchmark suite, and simplifies and builds on the 
implementation of the recent unary RPC performance improvements:
    - the handler benchmarks are extended to all RPC styles and both the Java 
and Scala generated handlers - before and after benchmarks are presented below.
    - the unary response optimisation is generalised to support all protocol 
variants and compression codecs while encapsulating the strict encoding concern 
a bit better in the protocol implementations.
    - some inlines and general use of FastFuture have been added that have 
minor performance boosts.
    - the Scala GrpcMarshalling.handleUnary implementation is greatly 
simplified. The vast majority of the benefit of the previous implementation can 
be achieved with FastFuture, and it can be improved slightly over that with 
judicious use of parasitic executors for subsequent transforms. The net 
performance is marginally faster now, with much less code.
    - the Java GrpcMarshalling.handleUnaryResponse implementation has also been 
simplified - CompletionStage already implements FastFuture like immediate 
transforms for completed futures, so the optimisations implemented had minimal 
benefit relative to their complexity. 
    - the Java unary RPC code generation has been simplified - the generated 
code implementations for these exposed a lot of internal APIs in the protocol 
reader/writer, making evolving those tricky, and had minimal/no impact on 
benchmark performance.
    - the duplicated handler generation in the Scala code generation has also 
been reverted - I couldn't see any difference in benchmark performance between 
a specific handler and the lifted/defaulted partial, but happy to discuss that 
one if someone has seen a difference in benchmarks.
    - a minor change to avoid buffer copying in gzip compress/uncompress
   
   # Benchmark Comparison
   
   These are benchmarks with the "full" profile run a few times to make sure 
the results are concordant.
   Results are from an M4 Macbook Pro with Java 21.
   
   ## Benchmarks before
   ```
   GrpcMarshallingBenchmark.handleUnaryStrict                                 
thrpt   30  53399977.378 ± 236563.959  ops/s
   GrpcMarshallingBenchmark.marshall                                          
thrpt   30  75091936.170 ± 396471.159  ops/s
   GrpcMarshallingBenchmark.marshallStream                                    
thrpt   30    873235.788 ±   3800.080  ops/s
   GrpcMarshallingBenchmark.unmarshallJavaStrict                              
thrpt   30   4162386.536 ±  29487.458  ops/s
   GrpcMarshallingBenchmark.unmarshallJavaStrictStreamed                      
thrpt   30     80746.708 ±    840.984  ops/s
   GrpcMarshallingBenchmark.unmarshallStrict                                  
thrpt   30  47609563.052 ± 457915.633  ops/s
     
   JavaHandlerBenchmark.generatedBidiStreamingNonstrictRequestProcessing      
thrpt   30     31026.673 ±    775.275  ops/s
   JavaHandlerBenchmark.generatedBidiStreamingStrictRequestProcessing         
thrpt   30     33088.268 ±    271.861  ops/s
   JavaHandlerBenchmark.generatedClientStreamingNonstrictRequestProcessing    
thrpt   30     44649.643 ±   1424.900  ops/s
   JavaHandlerBenchmark.generatedClientStreamingStrictRequestProcessing       
thrpt   30     49516.739 ±   2797.536  ops/s
   JavaHandlerBenchmark.generatedServerStreamingNonstrictRequestProcessing    
thrpt   30     22387.423 ±    285.747  ops/s
   JavaHandlerBenchmark.generatedServerStreamingStrictRequestProcessing       
thrpt   30     35161.133 ±    227.209  ops/s
   JavaHandlerBenchmark.generatedUnaryNonstrictRequestProcessing              
thrpt   30     49822.353 ±    784.518  ops/s
   JavaHandlerBenchmark.generatedUnaryStrictRequestProcessing                 
thrpt   30    468428.800 ±   3426.273  ops/s
   JavaHandlerBenchmark.oldStyleUnaryNonstrictRequestProcessing               
thrpt   30     50196.435 ±    815.611  ops/s
   JavaHandlerBenchmark.oldStyleUnaryStrictRequestProcessing                  
thrpt   30    455882.911 ±   3345.942  ops/s
   
   ScalaHandlerBenchmark.generatedBidiStreamingNonstrictRequestProcessing     
thrpt   30     25468.353 ±    260.275  ops/s
   ScalaHandlerBenchmark.generatedBidiStreamingStrictRequestProcessing        
thrpt   30     27334.586 ±    277.373  ops/s
   ScalaHandlerBenchmark.generatedClientStreamingNonstrictRequestProcessing   
thrpt   30     47795.740 ±   1224.781  ops/s
   ScalaHandlerBenchmark.generatedClientStreamingStrictRequestProcessing      
thrpt   30     52320.519 ±   1715.364  ops/s
   ScalaHandlerBenchmark.generatedServerStreamingNonstrictRequestProcessing   
thrpt   30     23030.293 ±    250.371  ops/s
   ScalaHandlerBenchmark.generatedServerStreamingStrictRequestProcessing      
thrpt   30     29067.371 ±    307.424  ops/s
   ScalaHandlerBenchmark.generatedUnaryNonstrictRequestProcessing             
thrpt   30     62822.560 ±   1118.223  ops/s
   ScalaHandlerBenchmark.generatedUnaryStrictRequestProcessing                
thrpt   30    508877.824 ±   4035.343  ops/s
   ScalaHandlerBenchmark.oldStyleUnaryNonstrictRequestProcessing              
thrpt   30     63475.816 ±    668.029  ops/s
   ScalaHandlerBenchmark.oldStyleUnaryStrictRequestProcessing                 
thrpt   30     99281.076 ±   1255.909  ops/s
   ScalaHandlerBenchmark.syntheticClientStreamingNonstrictRequestProcessing   
thrpt   30     53280.750 ±   1396.899  ops/s
   ScalaHandlerBenchmark.syntheticClientStreamingStrictRequestProcessing      
thrpt   30     59851.853 ±    582.574  ops/s
   ScalaHandlerBenchmark.syntheticUnaryNonstrictRequestProcessing             
thrpt   30     71707.502 ±   1382.759  ops/s
   ScalaHandlerBenchmark.syntheticUnaryStrictRequestProcessing                
thrpt   30  29881100.522 ± 588646.009  ops/s
   ```
   
   ## Benchmarks after
   
   ```
   GrpcMarshallingBenchmark.handleUnaryStrict                                 
thrpt   30   23005007.241 ± 473057.466  ops/s
   GrpcMarshallingBenchmark.marshall                                          
thrpt   30  130375569.815 ± 830605.090  ops/s
   GrpcMarshallingBenchmark.marshallStream                                    
thrpt   30     793487.445 ±   9462.529  ops/s
   GrpcMarshallingBenchmark.unmarshallJavaStrict                              
thrpt   30    4265269.981 ±   4172.738  ops/s
   GrpcMarshallingBenchmark.unmarshallJavaStrictStreamed                      
thrpt   30      84973.568 ±    742.753  ops/s
   GrpcMarshallingBenchmark.unmarshallStrict                                  
thrpt   30   48102785.062 ± 145163.062  ops/s
   
   JavaHandlerBenchmark.generatedBidiStreamingNonstrictRequestProcessing      
thrpt   30      31139.727 ±    163.322  ops/s
   JavaHandlerBenchmark.generatedBidiStreamingStrictRequestProcessing         
thrpt   30      34658.776 ±    189.357  ops/s
   JavaHandlerBenchmark.generatedClientStreamingNonstrictRequestProcessing    
thrpt   30      45756.087 ±    980.167  ops/s
   JavaHandlerBenchmark.generatedClientStreamingStrictRequestProcessing       
thrpt   30      51655.524 ±    934.029  ops/s
   JavaHandlerBenchmark.generatedServerStreamingNonstrictRequestProcessing    
thrpt   30      23278.436 ±    405.283  ops/s
   JavaHandlerBenchmark.generatedServerStreamingStrictRequestProcessing       
thrpt   30      36367.638 ±    504.533  ops/s
   JavaHandlerBenchmark.generatedUnaryNonstrictRequestProcessing              
thrpt   30      52138.210 ±    768.502  ops/s
   JavaHandlerBenchmark.generatedUnaryStrictRequestProcessing                 
thrpt   30     468946.623 ±   3685.495  ops/s
   JavaHandlerBenchmark.oldStyleUnaryNonstrictRequestProcessing               
thrpt   30      51820.826 ±    522.548  ops/s
   JavaHandlerBenchmark.oldStyleUnaryStrictRequestProcessing                  
thrpt   30     459969.700 ±   1485.594  ops/s
   
   ScalaHandlerBenchmark.generatedBidiStreamingNonstrictRequestProcessing     
thrpt   30      32508.149 ±    273.993  ops/s
   ScalaHandlerBenchmark.generatedBidiStreamingStrictRequestProcessing        
thrpt   30      35353.414 ±    252.348  ops/s
   ScalaHandlerBenchmark.generatedClientStreamingNonstrictRequestProcessing   
thrpt   30      53125.037 ±    876.229  ops/s
   ScalaHandlerBenchmark.generatedClientStreamingStrictRequestProcessing      
thrpt   30      66494.721 ±    894.347  ops/s
   ScalaHandlerBenchmark.generatedServerStreamingNonstrictRequestProcessing   
thrpt   30      24302.384 ±    363.502  ops/s
   ScalaHandlerBenchmark.generatedServerStreamingStrictRequestProcessing      
thrpt   30      35497.762 ±   2977.292  ops/s
   ScalaHandlerBenchmark.generatedUnaryNonstrictRequestProcessing             
thrpt   30      62792.381 ±   2802.438  ops/s
   ScalaHandlerBenchmark.generatedUnaryStrictRequestProcessing                
thrpt   30     533037.363 ±   2054.773  ops/s
   ScalaHandlerBenchmark.oldStyleUnaryNonstrictRequestProcessing              
thrpt   30      65645.750 ±    442.854  ops/s
   ScalaHandlerBenchmark.oldStyleUnaryStrictRequestProcessing                 
thrpt   30     519181.866 ±   4505.690  ops/s
   ScalaHandlerBenchmark.syntheticClientStreamingNonstrictRequestProcessing   
thrpt   30      61688.996 ±    380.460  ops/s
   ScalaHandlerBenchmark.syntheticClientStreamingStrictRequestProcessing      
thrpt   30      72524.001 ±    708.499  ops/s
   ScalaHandlerBenchmark.syntheticUnaryNonstrictRequestProcessing             
thrpt   30      74921.041 ±    814.497  ops/s
   ScalaHandlerBenchmark.syntheticUnaryStrictRequestProcessing                
thrpt   30   24562915.594 ± 261286.650  ops/s
   ```
   
   ## Benchmark changes summary
   
   GrpcMarshallingBenchmarks have some dips, but these are probably too fine 
grained and the higher level benchmarks show improvements - (you can see in the 
synthetic tests that very fine grained benchmarks see really large throughput 
variations with minor changes, so probably aren't usefully representative).
   
   Java request processing is largely unchanged - consistently 0-5% improvement 
across the board.
   
   Scala request processing shows some notable improvements:
    - Bidi streaming 28% improvement for strict and non-strict entities
    - Client streaming 27%/11% improvement for strict/non-strict entities
    - Server streaming 26%/6% improvement for strict/non-strict entities
    - Unary 0-5% improvement for strict entities, unchanged for non-strict
   
    There is also a 423% improvement for existing (pekko-grpc < 2.0.0) "old 
style" generated handlers without re-generation, which might be useful if the 
2.0.0 API/ABI doesn't change - essentially introducing FastFuture into the 
unmarshal brings much of the benefits to existing code without having to 
regenerate to use the new `handleUnary`.
   


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