Skip to content

IGNITE-27088 BinaryWriter should use internal String#value - #13529

Open
nizhikov wants to merge 13 commits into
apache:masterfrom
nizhikov:IGNITE-27088
Open

IGNITE-27088 BinaryWriter should use internal String#value#13529
nizhikov wants to merge 13 commits into
apache:masterfrom
nizhikov:IGNITE-27088

Conversation

@nizhikov

@nizhikov nizhikov commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

JmhBinaryStringWriteBenchmark on my local machine (less is better).

Benchmark                                                     (content)  (len)  (zeroCopy)  Mode  Cnt      Score      Error   Units
JmhBinaryStringWriteBenchmark.writeString                         ascii      8        true  avgt    5      3.320 ±    0.001   ns/op
JmhBinaryStringWriteBenchmark.writeString                         ascii      8       false  avgt    5      6.037 ±    0.018   ns/op

JmhBinaryStringWriteBenchmark.writeString                         ascii     64        true  avgt    5      5.582 ±    0.018   ns/op
JmhBinaryStringWriteBenchmark.writeString                         ascii     64       false  avgt    5      8.848 ±    0.021   ns/op

JmhBinaryStringWriteBenchmark.writeString                         ascii    512        true  avgt    5     14.349 ±    0.026   ns/op
JmhBinaryStringWriteBenchmark.writeString                         ascii    512       false  avgt    5     24.264 ±    0.087   ns/op

JmhBinaryStringWriteBenchmark.writeString                         ascii   4096        true  avgt    5    102.171 ±    0.245   ns/op
JmhBinaryStringWriteBenchmark.writeString                         ascii   4096       false  avgt    5    224.555 ±    1.104   ns/op

JmhBinaryStringWriteBenchmark.writeString                        latin1      8        true  avgt    5      4.310 ±    0.011   ns/op
JmhBinaryStringWriteBenchmark.writeString                        latin1      8       false  avgt    5     23.346 ±   62.142   ns/op

JmhBinaryStringWriteBenchmark.writeString                        latin1     64        true  avgt    5     21.721 ±    0.022   ns/op
JmhBinaryStringWriteBenchmark.writeString                        latin1     64       false  avgt    5     42.949 ±    0.404   ns/op

JmhBinaryStringWriteBenchmark.writeString                        latin1    512        true  avgt    5    145.675 ±    0.015   ns/op
JmhBinaryStringWriteBenchmark.writeString                        latin1    512       false  avgt    5    290.322 ±    2.028   ns/op

JmhBinaryStringWriteBenchmark.writeString                        latin1   4096        true  avgt    5   1124.822 ±    3.790   ns/op
JmhBinaryStringWriteBenchmark.writeString                        latin1   4096       false  avgt    5   2310.785 ±   14.185   ns/op

JmhBinaryStringWriteBenchmark.writeString                      cyrillic      8        true  avgt    5      7.697 ±    0.203   ns/op
JmhBinaryStringWriteBenchmark.writeString                      cyrillic      8       false  avgt    5     32.546 ±   10.491   ns/op

JmhBinaryStringWriteBenchmark.writeString                      cyrillic     64        true  avgt    5     26.427 ±    0.036   ns/op
JmhBinaryStringWriteBenchmark.writeString                      cyrillic     64       false  avgt    5     36.267 ±    0.066   ns/op

JmhBinaryStringWriteBenchmark.writeString                      cyrillic    512        true  avgt    5    179.502 ±    0.202   ns/op
JmhBinaryStringWriteBenchmark.writeString                      cyrillic    512       false  avgt    5    238.412 ±    1.142   ns/op

JmhBinaryStringWriteBenchmark.writeString                      cyrillic   4096        true  avgt    5   1477.551 ±   10.929   ns/op
JmhBinaryStringWriteBenchmark.writeString                      cyrillic   4096       false  avgt    5   1843.315 ±   28.290   ns/op

JmhBinaryStringWriteBenchmark.writeString                         mixed      8        true  avgt    5     10.294 ±    0.020   ns/op
JmhBinaryStringWriteBenchmark.writeString                         mixed      8       false  avgt    5     46.383 ±    2.601   ns/op

JmhBinaryStringWriteBenchmark.writeString                         mixed     64        true  avgt    5     62.867 ±    0.189   ns/op
JmhBinaryStringWriteBenchmark.writeString                         mixed     64       false  avgt    5     83.888 ±    0.378   ns/op

JmhBinaryStringWriteBenchmark.writeString                         mixed    512        true  avgt    5    460.052 ±    3.914   ns/op
JmhBinaryStringWriteBenchmark.writeString                         mixed    512       false  avgt    5    616.741 ±    2.437   ns/op

JmhBinaryStringWriteBenchmark.writeString                         mixed   4096        true  avgt    5   3826.752 ±   10.238   ns/op
JmhBinaryStringWriteBenchmark.writeString                         mixed   4096       false  avgt    5   4928.261 ±   35.569   ns/op

gc data (less is better):

JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate           ascii      8        true  avgt    5      0.001 ±    0.001  MB/sec
JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate           ascii      8       false  avgt    5   3791.402 ±   11.164  MB/sec

JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate           ascii     64        true  avgt    5      0.001 ±    0.001  MB/sec
JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate           ascii     64       false  avgt    5   8622.362 ±   20.482  MB/sec

JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate           ascii    512        true  avgt    5      0.001 ±    0.001  MB/sec
JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate           ascii    512       false  avgt    5  20752.465 ±   74.644  MB/sec

JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate           ascii   4096        true  avgt    5      0.001 ±    0.001  MB/sec
JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate           ascii   4096       false  avgt    5  17463.293 ±   85.742  MB/sec

JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate          latin1      8        true  avgt    5      0.001 ±    0.001  MB/sec
JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate          latin1      8       false  avgt    5   3760.722 ± 7985.487  MB/sec

JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate          latin1     64        true  avgt    5      0.001 ±    0.001  MB/sec
JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate          latin1     64       false  avgt    5   5151.522 ±   48.374  MB/sec

JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate          latin1    512        true  avgt    5      0.001 ±    0.001  MB/sec
JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate          latin1    512       false  avgt    5   5360.883 ±   37.511  MB/sec

JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate          latin1   4096        true  avgt    5      0.001 ±    0.001  MB/sec
JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate          latin1   4096       false  avgt    5   5295.783 ±   32.472  MB/sec

JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate        cyrillic      8        true  avgt    5      0.001 ±    0.001  MB/sec
JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate        cyrillic      8       false  avgt    5   2122.894 ±  757.435  MB/sec

JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate        cyrillic     64        true  avgt    5      0.001 ±    0.001  MB/sec
JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate        cyrillic     64       false  avgt    5   9255.990 ±   16.977  MB/sec

JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate        cyrillic    512        true  avgt    5      0.001 ±    0.001  MB/sec
JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate        cyrillic    512       false  avgt    5  10368.183 ±   49.686  MB/sec

JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate        cyrillic   4096        true  avgt    5      0.001 ±    0.001  MB/sec
JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate        cyrillic   4096       false  avgt    5  10612.277 ±  163.026  MB/sec

JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate           mixed      8        true  avgt    5      0.001 ±    0.001  MB/sec
JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate           mixed      8       false  avgt    5   1480.599 ±   83.550  MB/sec

JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate           mixed     64        true  avgt    5      0.001 ±    0.001  MB/sec
JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate           mixed     64       false  avgt    5   4001.626 ±   18.040  MB/sec

JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate           mixed    512        true  avgt    5      0.001 ±    0.001  MB/sec
JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate           mixed    512       false  avgt    5   4007.996 ±   15.843  MB/sec

JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate           mixed   4096        true  avgt    5      0.001 ±    0.001  MB/sec
JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate           mixed   4096       false  avgt    5   3969.266 ±   28.658  MB/sec

@ignitetcbot

Copy link
Copy Markdown
Contributor

TCBot Test Analysis

Possible Blockers (0)

No blockers found.

New Tests (4)

  • Binary Objects: 4 tests
    • IgniteBinaryObjectsTestSuite: StringWriterSelfTest.testCorpus - PASSED
    • IgniteBinaryObjectsTestSuite: StringWriterSelfTest.testLargeStrings - PASSED
    • IgniteBinaryObjectsTestSuite: StringWriterSelfTest.testRandomStrings - PASSED
    • IgniteBinaryObjectsTestSuite: StringWriterSelfTest.testStreamPosition - PASSED

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

A Checkstyle-enforced unused import in StringWriter.java will break the build, and the JMH benchmark’s zeroCopy parameterization is not reliable due to JVM-static initialization of the toggle.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Introduces a zero-copy UTF-8 string serialization path for Ignite binary writing, controlled by a new system property, and adds tests/benchmarks to validate and measure the behavior.

Changes:

  • Added StringWriter to serialize String to BinaryOutputStream without allocating temporary UTF-8 byte arrays when enabled.
  • Added IGNITE_BINARY_STRING_ZERO_COPY system property (default true) and wired it into BinaryWriterExImpl.
  • Updated DirectByteBufferStream string encoding/decoding to be explicitly UTF-8 and added coverage/benchmarking for the new string writer.
File summaries
File Description
modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBinaryObjectsTestSuite.java Adds the new StringWriterSelfTest to the binary objects test suite.
modules/core/src/test/java/org/apache/ignite/internal/binary/StringWriterSelfTest.java New differential tests ensuring StringWriter output matches UTF-8 String#getBytes serialization.
modules/core/src/main/java/org/apache/ignite/internal/direct/stream/DirectByteBufferStream.java Uses UTF-8 explicitly for string serialization; adds ASCII fast-path via internal string value.
modules/commons/src/main/java/org/apache/ignite/IgniteCommonsSystemProperties.java Adds IGNITE_BINARY_STRING_ZERO_COPY property and default value constant.
modules/binary/impl/src/main/java/org/apache/ignite/internal/binary/StringWriter.java New zero-copy UTF-8 string writer with compact-string and SIMD-negative-scan optimizations when available.
modules/binary/impl/src/main/java/org/apache/ignite/internal/binary/BinaryWriterExImpl.java Enables StringWriter path when IGNITE_BINARY_STRING_ZERO_COPY is enabled.
modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/binary/JmhBinaryStringWriteBenchmark.java Adds JMH benchmark intended to compare zero-copy vs legacy behavior.
Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +29 to +32
/**
* Tests that {@link StringWriter} output is byte-identical to serialization of the {@link String#getBytes()} result,
* which was used before zero-copy string serialization was introduced.
*/
byte[] latin1 = latin1Value(val);

if (latin1 != null) {
if (out.hasArray()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.NET/C++ code goes through hasArray()==false branch as I can see, thus we need carefully bench such a branch too.


str = sb.toString();

out = BinaryStreams.outputStream(4 * len + 64);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I try to run it instead of master with fast refactoring for PlatformOutputStreamImpl (C++.Net) like :
and found a bit perf drop (in measurements !!!!) thus real performance test need to be executed.
I changed:

        long poolPtr = allocatePool();
        long memPtr = allocatePooled(poolPtr, 10_000);
        PlatformMemory res = pool.get(memPtr);
        out = res.output();

        //out = new PlatformAbstractMemory BinaryStreams.outputStream(4 * len + 64);
PR
Benchmark                                                     (content)  (len)  (zeroCopy)  Mode  Cnt    Score    Error   Units
JmhBinaryStringWriteBenchmark.writeString                      cyrillic    512        true  avgt    5  523.005 ± 13.730   ns/op
JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate        cyrillic    512        true  avgt    5    0.001 ±  0.001  MB/sec


master
Benchmark                                                     (content)  (len)  (zeroCopy)  Mode  Cnt     Score     Error   Units
JmhBinaryStringWriteBenchmark.writeString                      cyrillic    512        true  avgt    5   443.634 ±  11.679   ns/op
JmhBinaryStringWriteBenchmark.writeString:gc.alloc.rate        cyrillic    512        true  avgt    5  5572.091 ± 145.617  MB/sec

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants