Product SiteDocumentation Site

Chapter 9. Optimization

This section covers ways to optimize MRG Messaging applications to improve performance. Some optimizations involve the structure of your code, others involve tuning parameters.
Benchmarks can be utilized to determine the expected throughput and latency of MRG Messaging in your environment. Red Hat supplies a set of benchmark applications in the qpidc-perftest package. This package contains:
  • perftest for measuring throughput, and
  • latencytest for measuring latency.
Each of these programs provide options for testing performance in different conditions. The can be viewed by using the --help option at the shell prompt:
$ perftest --help

$ latencytest --help
You can use the benchmarking tools to determine how changing a setting affects performance in your environment, so you can find the best settings to use in your program.
If these benchmarks perform significantly better than your application using the same configuration and settings, it is quite likely that MRG Messaging is not the bottleneck.

Note

This section is still under development. If you have found any tricks for optimizing your installation, why not let us know, and get it included in this document? Just follow the instructions in Reporting a Bug.
Try these first
When you start optimizing your installation of MRG Messaging, try these things first:
  1. Use asynchronous communication with the broker where possible. The Qpid APIs allow both synchronous and asynchronous communication. When performing a large amount of message transfers, asynchronous communication is much faster.
    From C++
    The Session interface will issue commands synchronously. The AsyncSession interface will issue them asynchronously. You can convert from one to the other easily using async(session) and sync(session). You can also synchronise at a specific point using session.sync()
    From Python
    session.auto_sync = False will turn off the default synchronous behaviour. You can synchronise explicitly using session.sync(timeout)
    From Java JMS
    All messaging is asynchronous in the Java JMS client, however persistent messaging offers both synchronous and asynchronous publishing. Synchronous messaging publishing is more reliable, but can be quite slow.
    Synchronous publishing can be set by adding the -Dsync_persistence=true option as a global property. This will make all publishing slower.
    Synchronous publishing can also be set as a connection option. More information on doing this is available from the MRG Messaging Tutorial
  2. Accept messages in batches rather than one by one. This decreases network traffic, while still guaranteeing delivery.
    From C++
    If you are using automatic acceptance, set the autoAck setting in SubscriptionSettings to a value greater than 1 (the value is the size of the batch that will be accepted). If managing accepts manually, do so in batches. For example:
    //a batch of messages is identified by a SequenceSet containing
    //the relevant Ids:
    qpid::framing::SequenceSet batch;
    
    //add message ids to the set: 
    batch.add(message.getId());
    
    //etc
    subscription.accept(batch);
    
    //or 
    session.messageAccept(batch);
    
    From Python
    batch = RangedSet() ... 
    
    #add message ids to the set 
    batch.add(message.id) 
    
    #etc
    session.message_accept(batch)
    
    From Java JMS
    Check the MRG Messaging Tutorial for instructions.
    For exclusive, auto-delete queues there is often no real value to using the explicit accept mode. Turning off the need to accept messages at all may also offer a performance gain. In C++, this is achieved by specifying ACCEPT_MODE_NONE as the acceptMode in SubscriptionSettings. In Python you would specify the accept mode when issuing a subscription request: session.messageSubscribe(queue='q', accept_mode=session.accept_mode.none). In JMS the queues used for topic subscriptions will do this automatically.
  3. Use pre-fetch. Pre-fetching instructs the broker to deliver messages to the client in anticipation of them being consumed.
    From C++
    Pre-fetch is controlled through the flowControl setting on SubscriptionSettings. The default is an unlimited pre-fetch which may overwhelm the client. For example, to set a prefetch of 100 messages:
    SubscriptionSettings settings; 
    settings.flowControl = FlowControl::messageWindow(100);
    
    From Python
    The default is an unlimited pre-fetch. For example, to reduce that to a prefetch window of 10 messages:
    session.message_subscribe(destination="my-subscriber", queue="my-queue") session.message_set_flow_mode(destination="my-subscriber", session.flow_mode.window) session.message_flow(destination="my-subscriber", session.credit_unit.message, 100) session.message_flow(destination="my-subscriber", session.credit_unit.byte, 0xFFFFFFFF)
    
    From Java JMS
    Use either the DUPS_OK or AUTO_ACK acknowledgement mode. For compliance with the JMS specification, the AUTO_ACK acknowledgement mode should always be used with a pre-fetch value of 0. This ensures one message is received and acknowledged at a time, which results in slow performance.
    The maximum pre-fetch amount can be set by adding the -Dsync_prefetch=800 option as a global property. The default value is 1000.
    The maximum pre-fetch amount can also be set as a connection option. More information on doing this is available from the MRG Messaging Tutorial
  4. Consider enabling TCP-NODELAY. This will generally improve latency but can also impact throughput. However if you are using very small transactions with a synchronous commit, this option can also improve throughput.
    From C++
    Set the tcpNodelay option to true on the ConnectionSettings instance passed to Connection::open().
    #include <qpid/client/ConnectionSettings.h>
    		
    ConnectionSettings connectionSettings;
    		
    connectionSettings.host = "localhost";
      connectionSettings.port = 5672;
      connectionSettings.tcpNoDelay = true;
    		
    connection.open(connectionSettings);
    
  5. Consider tuning the maximum frame size used. This will affect the degree to which the broker tries to batch messages for delivery to clients. To improve latency, try reducing the value from the default 64kb. This will not prevent messages larger than the max frame size being sent, but it will impact the maximum size of the message headers. Picking a value that is large enough for the majority of messages to fit in a single content frame is likely to be most optimal.
    From C++
    Set the maxFrameSize option on the ConnectionSettings instance passed to Connection::open().
    #include <qpid/client/ConnectionSettings.h>
    		
    ConnectionSettings connectionSettings;
    		
    connectionSettings.host = "localhost";
      connectionSettings.port = 5672;
      connectionSettings.maxFrameSize = 65535;
    		
    connection.open(connectionSettings);
    
    From Java JMS
    This option is not configurable under Java JMS
  6. Consider using bounds to control the size of the outgoing message queue. This specifies the maximum number of buffers that the outgoing message queue can hold.
    From C++
    Set the bounds property on the ConnectionSettings instance passed to Connection::open().
    #include <qpid/client/ConnectionSettings.h>
    		
    ConnectionSettings connectionSettings;
    		
    connectionSettings.host = "localhost";
      connectionSettings.port = 5672;
      connectionSettings.maxFrameSize = 65535;
    connectionSettings.bounds = 4;
    
    connection.open(connectionSettings);
    
  7. Experiment with different options using the perftest tool, available from the qpidc-perftest package.