I have finally got around to compare performance of HttpCommon classes
(foundation classes for HttpClient 4.0) and HttpClient. The tests
(admittedly very limited) make evident that HttpCommon consistently
outperforms HttpClient 3.0 by as much as 30 to 40%. It looks like the
performance improvement may become a strong incentive to migrate off
Commons HttpClient to Jakarta HttpClient. 

I found out that one of the major performance hits are incurred as a
result of using standard Java StringBuffer and ByteArrayOutputStream
classes that synchronize on each object mutation. HttpCommon classes
already use a non-synchronizing version of byte array buffer, which
partly contributed to the performance improvement. I am also considering
replacing StringBuffer with a non-synchronizing char array buffer. At
some point we can consider back-porting some of these performance
optimizations to HttpClient 3.1 to close the performance gap somewhat.

What do you think?

Oleg
====

Target server: Apache HTTPD 2.0.54 
500 repetitions

HttpClient 3.0
==============
Request: GET /docs/
Average (nanosec): 1,487,636
Request: GET /docs/mod/quickreference.html
Average (nanosec): 1,335,108
Request: GET /docs/misc/perf-tuning.html
Average (nanosec): 1,318,094
Request: GET /docs/platform/windows.html
Average (nanosec): 943,688

Source code [1]

HttpClient 4.0 (HttpCommon)
===========================
Request: GET /docs/ HTTP/1.1
Average (nanosec): 1,149,150
Request: GET /docs/mod/quickreference.html HTTP/1.1
Average (nanosec): 1,018,970
Request: GET /docs/misc/perf-tuning.html HTTP/1.1
Average (nanosec): 912,732
Request: GET /docs/platform/windows.html HTTP/1.1
Average (nanosec): 711,632

Source code [2]

[1] Http30Test
===========================================================
package tests;

import java.io.IOException;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.text.NumberFormat;

import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpVersion;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;

public class Http30Test {

    private final HttpClient httpclient;

    private final NumberFormat numformat;

    private Http30Test() {
        super();
        this.httpclient = createHttpClient();
        this.numformat = new DecimalFormat("###,###");
    }

    private static HttpClient createHttpClient() {
        HttpClient httpclient = new HttpClient();
        httpclient.getParams().setVersion(HttpVersion.HTTP_1_1);
        httpclient.getParams().setContentCharset("UTF-8");
        httpclient.getParams().setBooleanParameter(
                HttpMethodParams.USE_EXPECT_CONTINUE, false);
        httpclient.getHttpConnectionManager().getParams()
                .setStaleCheckingEnabled(false);
        return httpclient;
    }

    private static HttpMethod[] generateRequests() {
        GetMethod r1 = new GetMethod("/docs/");
        GetMethod r2 = new GetMethod("/docs/mod/quickreference.html");
        GetMethod r3 = new GetMethod("/docs/misc/perf-tuning.html");
        GetMethod r4 = new GetMethod("/docs/platform/windows.html");
        return new HttpMethod[] { r1, r2, r3, r4 };
    }

    private void execute(final HostConfiguration hostconfig,
            final HttpMethod httpmethod, int repeatCount) throws
IOException,
            HttpException {
        long total = 0;
        for (int i = 0; i < repeatCount; i++) {
            try {
                long start = System.nanoTime();
                try {
                    this.httpclient.executeMethod(hostconfig,
httpmethod);
                    InputStream instream =
httpmethod.getResponseBodyAsStream();
                    byte[] buffer = new byte[4096];
                    while (instream.read(buffer) > 0) {
                    }
                    long t = System.nanoTime() - start;
                    total += t;
                } finally {
                    httpmethod.releaseConnection();
                }
            } catch (IOException ex) {
                System.out.println("Request failed: " + ex.toString());
            }
        }
        System.out.println("Request: " + httpmethod.getName() + " "
                + httpmethod.getURI());
        System.out.println("Average (nanosec): "
                + this.numformat.format(total / repeatCount));
    }

    public static void main(String[] args) throws Exception {
        if (args.length < 2) {
            System.out
                    .println("Usage tests.performance.PerformanceTest
<port> <count>");
            System.exit(1);
        }
        Http30Test test = new Http30Test();

        int port = Integer.parseInt(args[0]);
        int count = Integer.parseInt(args[1]);

        HostConfiguration hostconfig = new HostConfiguration();
        hostconfig.setHost("localhost", port);

        HttpMethod[] requests = generateRequests();

        Thread.sleep(1000);

        for (int i = 0; i < requests.length; i++) {
            test.execute(hostconfig, requests[i], count);
        }
    }

}

[2] Http40Test
===========================================================
package tests;

import java.io.IOException;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.text.NumberFormat;

import org.apache.http.HttpClientConnection;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpMutableRequest;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.Protocol;
import org.apache.http.executor.HttpRequestExecutor;
import org.apache.http.impl.DefaultHttpClientConnection;
import org.apache.http.impl.DefaultHttpParams;
import org.apache.http.impl.HttpGetRequest;
import org.apache.http.impl.io.PlainSocketFactory;
import org.apache.http.io.SocketFactory;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.RequestConnControl;
import org.apache.http.protocol.RequestContent;
import org.apache.http.protocol.RequestExpectContinue;
import org.apache.http.protocol.RequestTargetHost;
import org.apache.http.protocol.RequestUserAgent;

public class Http40Test {

    private final HttpRequestExecutor httpexecutor;
    private final NumberFormat numformat;
    
    private PerformanceTest() {
        super();
        this.httpexecutor = createExecutor();
        this.numformat = new DecimalFormat("###,###"); 
    }
    
    private static HttpRequestExecutor createExecutor() {
        HttpParams params = new DefaultHttpParams(null);
        params.setParameter(HttpProtocolParams.PROTOCOL_VERSION,
HttpVersion.HTTP_1_1)
            .setParameter(HttpProtocolParams.HTTP_CONTENT_CHARSET,
"UTF-8")
            .setParameter(HttpProtocolParams.USER_AGENT, "Jakarta HTTP")
            .setBooleanParameter(HttpProtocolParams.USE_EXPECT_CONTINUE,
false)
            .setBooleanParameter(HttpConnectionParams.STALE_CONNECTION_CHECK, 
false);
            
        HttpRequestExecutor httpexecutor = new HttpRequestExecutor();
        httpexecutor.setParams(params);
        
        // Required request interceptors
        httpexecutor.addRequestInterceptor(new RequestContent());
        httpexecutor.addRequestInterceptor(new RequestTargetHost());
        // Recommended request interceptors
        httpexecutor.addRequestInterceptor(new RequestConnControl());
        httpexecutor.addRequestInterceptor(new RequestUserAgent());
        httpexecutor.addRequestInterceptor(new RequestExpectContinue());
        return httpexecutor;
    }

    private static HttpMutableRequest[] generateRequests() {
        HttpGetRequest  r1 = new HttpGetRequest("/docs/");
        HttpGetRequest  r2 = new
HttpGetRequest("/docs/mod/quickreference.html");
        HttpGetRequest  r3 = new
HttpGetRequest("/docs/misc/perf-tuning.html");
        HttpGetRequest  r4 = new
HttpGetRequest("/docs/platform/windows.html");
        return new HttpMutableRequest[] { r1, r2, r3, r4 };
    }
    
    private void execute(
            final HttpRequest request, 
            final HttpClientConnection conn, 
            int repeatCount) throws IOException, HttpException {
        long total = 0;
        for (int i = 0; i < repeatCount; i++) {
            try {
                long start = System.nanoTime();
                HttpResponse response =
this.httpexecutor.execute(request, conn);
                InputStream instream =
response.getEntity().getContent();
                byte[] buffer = new byte[4096];
                while (instream.read(buffer) > 0) {
                }
                long t = System.nanoTime() - start;
                total += t;
            } catch (IOException ex) {
                System.out.println("Request failed: " + ex.toString());
            }
        }
        System.out.println("Request: " + request.getRequestLine());
        System.out.println("Average (nanosec): " +
this.numformat.format(total / repeatCount));
    }

    public static void main(String[] args) throws Exception {
        if (args.length < 2) {
            System.out.println("Usage tests.performance.PerformanceTest
<port> <count>");
            System.exit(1);
        }
        PerformanceTest test = new PerformanceTest();
        
        int port = Integer.parseInt(args[0]);
        int count = Integer.parseInt(args[1]);
        
        SocketFactory socketfactory =
PlainSocketFactory.getSocketFactory();
        Protocol http = new Protocol("http", socketfactory, 80);
        HttpHost host = new HttpHost("localhost", port, http);

        HttpRequest[] requests = generateRequests();
        
        Thread.sleep(1000);
        
        DefaultHttpClientConnection conn = new
DefaultHttpClientConnection(host);
        try {
            for (int i = 0; i < requests.length; i++) {
                test.execute(requests[i], conn, count);
            }
        } finally {
            conn.close();
        }
    }
    
}




---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to