lizining1231 opened a new pull request, #3727:
URL: https://github.com/apache/dubbo-go/pull/3727

   ### Description
   Fixes #3726 link #3718
   
   ### Description
   The optional `marshalAppender` extension added there covered the IDL path 
(`protoBinaryCodec`) and server-side non-IDL responses 
(`tripleServerCodecSession`), but not the client-side non-IDL request path 
(Java interop and generic calls), which goes through `protoWrapperCodec`. Every 
unary call therefore encoded its arguments with `innerCodec.Marshal`, allocated 
a separate buffer for the outer `TripleRequestWrapper`, and copied that buffer 
into the transport.
   
   This change brings `protoWrapperCodec` onto the same extension: the outer 
wrapper is encoded directly into the caller-provided (pooled) buffer. It is 
purely additive, codecs without the extension keep their exact previous 
behaviour.
   
   ### Changes
   
   `protocol/triple/triple_protocol/codec.go`:
   
   - Add `MarshalAppend` to `protoWrapperCodec`, implementing the optional 
`marshalAppender` interface. It delegates to 
`proto.MarshalOptions{}.MarshalAppend`, so its output is byte-identical to 
`Marshal`.
   - Extract a shared `requestWrapper` helper so `Marshal` and `MarshalAppend` 
build the same `TripleRequestWrapper` and can never drift apart.
   - Inner payloads still come from `innerCodec.Marshal`, so the win is 
confined to the outer wrapper's allocation and copy.
   
   ### Test
   
   | Test | Description |
   |---|---|
   | `TestMarshalPerfTypeGuard` | Scope guard: the wrapper must implement 
`marshalAppender` for both hessian2 and msgpack, plus a reverse guard that 
hides the extension and forces the slow path |
   | `TestMarshalPerfClientWrapperFastPath` | Both send wires reach 
`MarshalAppend` exactly once (probe whose `Marshal` always fails), and the fast 
path emits byte-identical output to the slow path across single / multi / POJO 
/ bare / void / nil argument shapes |
   | `TestMarshalPerfClientWrapperConcurrentSend` | 32 goroutines x 100 sends 
sharing one `bufferPool`, race-free under `-race` |
   | `TestMarshalPerfClientWrapperLargeBufferDropped` | Buffers grown beyond 
the 8MiB recycle cap are dropped by the pool (asserting the pool's cap, not 
byte equality) and leave no residue for the next small message; both wires |
   | `TestNonIDLUnary_PublicEntry_MarshalAppendEffective` | Non-IDL unary calls 
end to end through `NewClient` / `CallUnary` on {gRPC, Triple} x {hessian2, 
msgpack} all reach the fast path |
   | `BenchmarkWrapperCodecFastPath` / `SlowPath` | A/B benchmarks: hessian2 
and msgpack inner codecs x 128B / 1KiB / 16KiB / 1MiB x both wires |
   
   ### Validation
   
   #### pprof CPU Profile diff
   
   <img width="1910" height="872" alt="image" 
src="https://github.com/user-attachments/assets/9084ab5d-ee00-43db-8f11-5344489da732";
 />
   
   #### Marshal Fast-Path Benchmark Results
   
   **go test bench -bench=WrapperCodec**
   
   **allocs/op reduced from 13 to 11**
   
   | Message | Before | After |
   | ------- | ------ | ----- |
   | 128B    | 12     | 10    |
   | 1KiB    | 13     | 11    |
   | 16KiB   | 13     | 11    |
   | 1MiB    | 13     | 11    |
   
   Before, every op paid for the outer wrapper's own slice on top of the inner 
codec's allocations. The fast path writes that wrapper straight into the pooled 
buffer, so both allocations disappear.
   
   **B/op reduced by up to -49.9% (msgpack inner codec)**
   
   | Message | Before  | After   | Reduction |
   | ------- | ------- | ------- | --------- |
   | 128B    | 1901    | 1649    | -13.26%   |
   | 1KiB    | 4414    | 3186    | -27.82%   |
   | 16KiB   | 38598   | 20098   | -47.93%   |
   | 1MiB    | 2115543 | 1059381 | -49.92%   |
   
   The saving is exactly one payload-sized wrapper buffer, so B/op no longer 
carries a second copy of the message and the gap widens with payload size. Note 
that the outer wrapper always contains the inner-encoded bytes, so this path is 
bounded at -50%; removing the inner copy needs append support from the inner 
codecs and is out of scope here.
   
   The same A/B with a hessian2 inner codec:
   
   | Message | Before  | After   | Reduction |
   | ------- | ------- | ------- | --------- |
   | 128B    | 884     | 627     | -29.07%   |
   | 1KiB    | 3777    | 2539    | -32.78%   |
   | 16KiB   | 82649   | 63997   | -22.57%   |
   | 1MiB    | 7813072 | 6763493 | -13.43%   |
   
   hessian2's own encoder allocates far more, which dilutes the ratio; the 
absolute saving is still one payload-sized wrapper buffer.
   
   Reproduce:
   ```bash
       # pprof
       go test -run '^$' -bench 
'^BenchmarkWrapperCodecSlowPath$/msgpack/1MiB/triple$' -benchtime=30000x \
         -cpuprofile cpu_slow.out ./protocol/triple/triple_protocol/
       go test -run '^$' -bench 
'^BenchmarkWrapperCodecFastPath$/msgpack/1MiB/triple$' -benchtime=30000x \
         -cpuprofile cpu_fast.out ./protocol/triple/triple_protocol/
       go tool pprof -top -diff_base cpu_slow.out cpu_fast.out
       # benchmark
       go test -run '^$' -bench WrapperCodec -benchmem -count=3 
./protocol/triple/triple_protocol/
   ```
   
   ### Checklist
   - [x] I confirm the target branch is `develop`
   - [x] I have run `make fmt` to format my code
   - [x] I have run `make test` to run local tests
   - [x] I have added tests that prove my fix is effective or that my feature 
works
   


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