Skip to content

Commit 34542fc

Browse files
committed
Increase default HTTP/1.1 buffer size to 32 KiB
(cherry picked from commit c0a9274)
1 parent a79f87e commit 34542fc

2 files changed

Lines changed: 164 additions & 1 deletion

File tree

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*
2+
* ====================================================================
3+
* Licensed to the Apache Software Foundation (ASF) under one
4+
* or more contributor license agreements. See the NOTICE file
5+
* distributed with this work for additional information
6+
* regarding copyright ownership. The ASF licenses this file
7+
* to you under the Apache License, Version 2.0 (the
8+
* "License"); you may not use this file except in compliance
9+
* with the License. You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing,
14+
* software distributed under the License is distributed on an
15+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
* KIND, either express or implied. See the License for the
17+
* specific language governing permissions and limitations
18+
* under the License.
19+
* ====================================================================
20+
*
21+
* This software consists of voluntary contributions made by many
22+
* individuals on behalf of the Apache Software Foundation. For more
23+
* information on the Apache Software Foundation, please see
24+
* <http://www.apache.org/>.
25+
*
26+
*/
27+
package org.apache.hc.core5.benchmark;
28+
29+
import java.net.InetSocketAddress;
30+
import java.util.concurrent.Future;
31+
import java.util.concurrent.TimeUnit;
32+
33+
import org.apache.hc.core5.http.ContentType;
34+
import org.apache.hc.core5.http.EntityDetails;
35+
import org.apache.hc.core5.http.HttpException;
36+
import org.apache.hc.core5.http.HttpHost;
37+
import org.apache.hc.core5.http.HttpRequest;
38+
import org.apache.hc.core5.http.HttpResponse;
39+
import org.apache.hc.core5.http.HttpStatus;
40+
import org.apache.hc.core5.http.Message;
41+
import org.apache.hc.core5.http.Method;
42+
import org.apache.hc.core5.http.config.Http1Config;
43+
import org.apache.hc.core5.http.impl.bootstrap.AsyncRequesterBootstrap;
44+
import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester;
45+
import org.apache.hc.core5.http.nio.AsyncRequestConsumer;
46+
import org.apache.hc.core5.http.nio.AsyncServerRequestHandler;
47+
import org.apache.hc.core5.http.nio.entity.AsyncEntityProducers;
48+
import org.apache.hc.core5.http.nio.entity.DiscardingEntityConsumer;
49+
import org.apache.hc.core5.http.nio.support.BasicRequestConsumer;
50+
import org.apache.hc.core5.http.nio.support.BasicRequestProducer;
51+
import org.apache.hc.core5.http.nio.support.BasicResponseConsumer;
52+
import org.apache.hc.core5.http.nio.support.BasicResponseProducer;
53+
import org.apache.hc.core5.http.nio.support.BasicServerExchangeHandler;
54+
import org.apache.hc.core5.http.protocol.HttpContext;
55+
import org.apache.hc.core5.io.CloseMode;
56+
import org.apache.hc.core5.reactor.IOReactorConfig;
57+
import org.apache.hc.core5.testing.nio.Http1TestServer;
58+
import org.apache.hc.core5.util.Timeout;
59+
import org.openjdk.jmh.annotations.Benchmark;
60+
import org.openjdk.jmh.annotations.BenchmarkMode;
61+
import org.openjdk.jmh.annotations.Fork;
62+
import org.openjdk.jmh.annotations.Level;
63+
import org.openjdk.jmh.annotations.Measurement;
64+
import org.openjdk.jmh.annotations.Mode;
65+
import org.openjdk.jmh.annotations.OutputTimeUnit;
66+
import org.openjdk.jmh.annotations.Param;
67+
import org.openjdk.jmh.annotations.Scope;
68+
import org.openjdk.jmh.annotations.Setup;
69+
import org.openjdk.jmh.annotations.State;
70+
import org.openjdk.jmh.annotations.TearDown;
71+
import org.openjdk.jmh.annotations.Threads;
72+
import org.openjdk.jmh.annotations.Warmup;
73+
74+
/**
75+
* End-to-end HTTP/1.1 throughput of a large-body GET over loopback, as a function of the
76+
* {@link Http1Config} session buffer size. Both the requester and the server are configured with
77+
* the same buffer size, so this isolates the effect of the buffer default on large messages.
78+
*/
79+
@BenchmarkMode(Mode.Throughput)
80+
@OutputTimeUnit(TimeUnit.SECONDS)
81+
@Warmup(iterations = 3, time = 2, timeUnit = TimeUnit.SECONDS)
82+
@Measurement(iterations = 10, time = 2, timeUnit = TimeUnit.SECONDS)
83+
// A fixed, generous heap keeps GC out of the measurement: at ~6 K x 1 MB/s the transient garbage
84+
// otherwise triggers frequent collections whose pauses dominate the run-to-run variance.
85+
@Fork(value = 1, jvmArgs = {"-Xms4g", "-Xmx4g"})
86+
// Client and server run in the same JVM, so keep concurrency at or below the available cores to
87+
// avoid oversubscription noise; override with JMH -t on larger machines.
88+
@Threads(8)
89+
@State(Scope.Benchmark)
90+
public class Http1BufferSizeBenchmark {
91+
92+
@Param({"8192", "16384", "32768", "65536"})
93+
public int bufferSize;
94+
95+
@Param({"1048576"})
96+
public int bodySize;
97+
98+
private static final Timeout TIMEOUT = Timeout.ofSeconds(60);
99+
100+
private Http1TestServer server;
101+
private HttpAsyncRequester requester;
102+
private HttpHost target;
103+
104+
@Setup(Level.Trial)
105+
public void setUp() throws Exception {
106+
final Http1Config h1Config = Http1Config.custom().setBufferSize(bufferSize).build();
107+
final byte[] body = new byte[bodySize];
108+
109+
server = new Http1TestServer();
110+
server.configure(h1Config);
111+
server.register("*", () -> new BasicServerExchangeHandler<>(new AsyncServerRequestHandler<Message<HttpRequest, Void>>() {
112+
113+
@Override
114+
public AsyncRequestConsumer<Message<HttpRequest, Void>> prepare(
115+
final HttpRequest request, final EntityDetails entityDetails, final HttpContext context) {
116+
return new BasicRequestConsumer<>(entityDetails != null ? new DiscardingEntityConsumer<>() : null);
117+
}
118+
119+
@Override
120+
public void handle(
121+
final Message<HttpRequest, Void> message, final ResponseTrigger responseTrigger,
122+
final HttpContext context) throws HttpException, java.io.IOException {
123+
responseTrigger.submitResponse(
124+
new BasicResponseProducer(HttpStatus.SC_OK,
125+
AsyncEntityProducers.create(body, ContentType.APPLICATION_OCTET_STREAM)),
126+
context);
127+
}
128+
}));
129+
final InetSocketAddress address = server.start();
130+
131+
requester = AsyncRequesterBootstrap.bootstrap()
132+
.setIOReactorConfig(IOReactorConfig.custom().setSoTimeout(TIMEOUT).build())
133+
.setHttp1Config(h1Config)
134+
.setMaxTotal(300)
135+
.setDefaultMaxPerRoute(300)
136+
.create();
137+
requester.start();
138+
target = new HttpHost("http", "localhost", address.getPort());
139+
}
140+
141+
@TearDown(Level.Trial)
142+
public void tearDown() throws Exception {
143+
if (requester != null) {
144+
requester.close(CloseMode.GRACEFUL);
145+
}
146+
if (server != null) {
147+
server.close();
148+
}
149+
}
150+
151+
@Benchmark
152+
public long getLargeBody() throws Exception {
153+
final Future<Message<HttpResponse, Void>> future = requester.execute(
154+
new BasicRequestProducer(Method.GET, target, "/"),
155+
new BasicResponseConsumer<>(new DiscardingEntityConsumer<Void>()),
156+
TIMEOUT, null);
157+
final Message<HttpResponse, Void> message = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
158+
return message.getHead().getCode();
159+
}
160+
161+
}

httpcore5/src/main/java/org/apache/hc/core5/http/config/Http1Config.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,9 @@ public static Http1Config.Builder copy(final Http1Config config) {
141141
}
142142

143143
private static final int INIT_WINDOW_SIZE = 65535;
144-
private static final int INIT_BUF_SIZE = 8192;
144+
// Larger session buffers cut the number of read/write events on large messages; the gain
145+
// levels off past 32 KB while per-connection footprint keeps growing, so 32 KB is the default.
146+
private static final int INIT_BUF_SIZE = 32 * 1024;
145147
private static final Timeout INIT_WAIT_FOR_CONTINUE = Timeout.ofSeconds(3);
146148
private static final int INIT_BUF_CHUNK = -1;
147149
private static final int INIT_MAX_HEADER_COUNT = 100;

0 commit comments

Comments
 (0)