Skip to content

Save additional copies on byte[] and String REST resources #47198

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 7, 2025

Conversation

franz1981
Copy link
Contributor

Similarly to #46936 this is saving multiple copies before sending an HTTP response to the wire

e.g.

before this PR (for String):

If Http 1.1:

after this PR (for String):

  • only one copy happens, but requires 3 times the available String size, since UTF-8 max allocated space is required; usually this is not a problem since Netty pooled buffers have size-based caching, which means that is likely not allocating anything new

I can modify as well the String allocation to perform exact length check - or by using reflection, I can ask to the String itself (using the coder field into it) if is a latin one, in O(1) - but I don't want to push this too far.

For the byte[] case instead, there's a similar story (since Buffer.buffer(byte[]) perform another copy in a vertx heap based buffer, but there's no drawback when it comes with preallocated capacity.

@franz1981
Copy link
Contributor Author

@tsegismont This is for you as well bud; both because it involves some inner Netty behaviour and because it likely show some existing gap in Vertx Buffer's impl itself

Copy link

quarkus-bot bot commented Apr 6, 2025

Status for workflow Quarkus CI

This is the status report for running Quarkus CI on commit 7590321.

✅ The latest workflow run for the pull request has completed successfully.

It should be safe to merge provided you have a look at the other checks in the summary.

You can consult the Develocity build scans.


Flaky tests - Develocity

⚙️ JVM Integration Tests - JDK 17

📦 integration-tests/injectmock

io.quarkus.it.mockbean.PerClassSpyTest.testWithoutSpy - History

  • Mock accessed after inline mocks were cleared - org.mockito.exceptions.misusing.DisabledMockException
org.mockito.exceptions.misusing.DisabledMockException: Mock accessed after inline mocks were cleared
	at io.quarkus.it.mockbean.PerClassSpyTest$IdentityService.call(PerClassSpyTest.java:43)
	at io.quarkus.it.mockbean.PerClassSpyTest.testWithoutSpy(PerClassSpyTest.java:36)
	at java.base/java.lang.reflect.Method.invoke(Method.java:569)
	at io.quarkus.test.junit.QuarkusTestExtension.runExtensionMethod(QuarkusTestExtension.java:1026)
	at io.quarkus.test.junit.QuarkusTestExtension.interceptTestMethod(QuarkusTestExtension.java:873)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)

⚙️ JVM Integration Tests - JDK 17 Windows

📦 integration-tests/injectmock

io.quarkus.it.mockbean.PerClassSpyTest.testWithoutSpy - History

  • Mock accessed after inline mocks were cleared - org.mockito.exceptions.misusing.DisabledMockException
org.mockito.exceptions.misusing.DisabledMockException: Mock accessed after inline mocks were cleared
	at io.quarkus.it.mockbean.PerClassSpyTest$IdentityService.call(PerClassSpyTest.java:43)
	at io.quarkus.it.mockbean.PerClassSpyTest.testWithoutSpy(PerClassSpyTest.java:36)
	at java.base/java.lang.reflect.Method.invoke(Method.java:569)
	at io.quarkus.test.junit.QuarkusTestExtension.runExtensionMethod(QuarkusTestExtension.java:1026)
	at io.quarkus.test.junit.QuarkusTestExtension.interceptTestMethod(QuarkusTestExtension.java:873)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)

⚙️ JVM Integration Tests - JDK 21

📦 integration-tests/injectmock

io.quarkus.it.mockbean.PerClassSpyTest.testWithoutSpy - History

  • Mock accessed after inline mocks were cleared - org.mockito.exceptions.misusing.DisabledMockException
org.mockito.exceptions.misusing.DisabledMockException: Mock accessed after inline mocks were cleared
	at io.quarkus.it.mockbean.PerClassSpyTest$IdentityService.call(PerClassSpyTest.java:43)
	at io.quarkus.it.mockbean.PerClassSpyTest.testWithoutSpy(PerClassSpyTest.java:36)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at io.quarkus.test.junit.QuarkusTestExtension.runExtensionMethod(QuarkusTestExtension.java:1026)
	at io.quarkus.test.junit.QuarkusTestExtension.interceptTestMethod(QuarkusTestExtension.java:873)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)

📦 integration-tests/virtual-threads/grpc-virtual-threads

io.quarkus.grpc.example.streaming.VertxVirtualThreadTest.testGrpcClient - History

  • 1 expectation failed. Expected status code <200> but was <500>. - java.lang.AssertionError
java.lang.AssertionError: 
1 expectation failed.
Expected status code <200> but was <500>.

	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
	at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:73)
	at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:108)

@geoand geoand merged commit f6e3239 into quarkusio:main Apr 7, 2025
51 checks passed
@quarkus-bot quarkus-bot bot added this to the 3.22 - main milestone Apr 7, 2025
@tsegismont
Copy link
Contributor

@franz1981 this looks interesting, have you got a significant improvement, in plain text benchmark?

@franz1981
Copy link
Contributor Author

franz1981 commented Apr 7, 2025

this looks interesting, have you got a significant improvement, in plain text benchmark?

I checked that there was no regression; but in our perf tests we tends to use the blocking worker pool and not running on the I/O thread - so it is less evident what improvement you got since the main bottleneck there is the thread handoff.

We should add some more test which uses byte[] in the I/O thread instead: IDK if @geoand got some idea for the byte[] case since I have noticed that is fairly tested in our Quarkus tests so maybe is used way more than I was expecting (for common user scenarios) - and that means can be a nice additional test to have too.

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

Successfully merging this pull request may close these issues.

3 participants