disruptor調優方法


翻譯自disruptor在github上的文檔,https://github.com/LMAX-Exchange/disruptor/wiki/Getting-Started

 

Basic Tuning Options 基本的調優方法

Using the above approach will work functionally in the widest set of deployment scenarios. However, if you able to make certain assumptions about the hardware and software environment that the Disruptor will run in then you can take advantage of a number of tuning options to improve performance. There are 2 main options for tuning, single vs. multiple producers and alternative wait strategies. ------------------------------------------------------------------------------------------------- 使用上面方法在大多數的部署場景里都可以很好的工作。 但是,如果你能搞清楚你將在什么樣的硬件和軟件環境中運行disruptor的話,有一些個調優方法能夠提高性能。 主要有2種方法進行調優:單生產者vs多生產者,選擇合適的等待策略。

Single vs. Multiple Producers  單生產者還是多生產者?

One of the best ways to improve performance in concurrect systems is to ahere to the Single Writer Princple, this applies to the Disruptor. If you are in the situation where there will only ever be a single thread producing events into the Disruptor, then you can take advantage of this to gain additional performance. 改進並發系統性能的最佳辦法之一就是遵守“單寫入者”定律,Disruptor就是這樣干的。 如果你只需要用一個單生產者線程生成事件投入Disruptor的話,這樣的應用場景會獲得額外的性能表現。

public class LongEventMain {    

  public static void main(String[] args) throws Exception     {        

  //.....        

  // Construct the Disruptor with a SingleProducerSequencer        

  Disruptor<LongEvent> disruptor = new Disruptor(factory,       

                            bufferSize,     

                            ProducerType.SINGLE, // Single producer                                                       

                            new BlockingWaitStrategy(),   

                                         executor);

        //.....     }

}

To give an indication of how much of a performance advantage can be achieved through this technique we can change the producer type in the OneToOne performance test. Tests run on i7 Sandy Bridge MacBook Air. 為了證明這樣的技術到底能帶來多少性能上的提升, 我們可以通過調整producerType來一場1對1的性能測試。(譯者注:ProducersType.Single vs. ProducersType.Multiple ) 測試環境:i7 Sandy Bridge MacBook Air

Multiple Producer

Run 0, Disruptor=26,553,372 ops/sec

Run 1, Disruptor=28,727,377 ops/sec

Run 2, Disruptor=29,806,259 ops/sec

Run 3, Disruptor=29,717,682 ops/sec

Run 4, Disruptor=28,818,443 ops/sec

Run 5, Disruptor=29,103,608 ops/sec

Run 6, Disruptor=29,239,766 ops/sec

Single Producer

Run 0, Disruptor=89,365,504 ops/sec

Run 1, Disruptor=77,579,519 ops/sec

Run 2, Disruptor=78,678,206 ops/sec

Run 3, Disruptor=80,840,743 ops/sec

Run 4, Disruptor=81,037,277 ops/sec

Run 5, Disruptor=81,168,831 ops/sec

Run 6, Disruptor=81,699,346 ops/sec

Alternative Wait Strategies 可選擇的等待策略

BlockingWaitStrategy 阻塞等待策略

The default wait strategy used by the Disruptor is the BlockingWaitStrategy. Internally the BlockingWaitStrategy uses a typical lock and condition variable to handle thread wake-up. The BlockingWaitStrategy is the slowest of the available wait strategies, but is the most conservative with the respect to CPU usage and will give the most consistent behaviour across the widest variety of deployment options. However, again knowledge of the deployed system can allow for additional performance. -------------------------------------------------------------------------------------------------------- disruptor的默認消費者等待策略是BlockingWaitStrategy。 BlockingWaitStrategy內部使用的是典型的鎖和條件變量機制,來處理線程的喚醒。 這種策略是所有disruptor等待策略中最慢的一種,但是是最保守使用消耗cpu的一種用法,並且在不同的部署環境下最能保持性能一致。 但是,隨着我們對部署系統的了解可以優化出額外的性能。(譯者注:BlockingWaitStrategy是適用面最廣的默認策略, 但是隨着對具體系統部署環境的具體分析,我們可以采用其他策略替換它,來取得更好的優化性能)

SleepingWaitStrategy  休眠等待策略

Like the BlockingWaitStrategy the SleepingWaitStrategy it attempts to be conservative with CPU usage, by using a simple busy wait loop, but uses a call to  LockSupport.parkNanos(1)  in the middle of the loop. On a typical Linux system this will pause the thread for around 60μs. However it has the benefit that the producing thread does not need to take any action other increment the appropriate counter and does not require the cost of signalling a condition variable. However, the mean latency of moving the event between the producer and consumer threads will be higher. It works best in situations where low latency is not required, but a low impact on the producing thread is desired. A common use case is for asynchronous logging. --------------------------------------------------------------------------------------------------------- 像BlockingWaitStrategy一樣,SleepingWaitStrategy也是屬於一種保守使用cpu的策略。 它使用一個簡單的loop繁忙等待循環,但是在循環體中間它調用了LockSupport.parkNanos(1)。 通常在linux系統這樣會使得線程停頓大約60微秒。但是這樣做的好處是,生產者線程不需要額外的動作去累加計數器,也不需要產生條件變量信號量開銷。 但是這樣帶來的負面影響是,在生產者線程與消費者線程之間傳遞event的延遲變高了。所以SleepingWaitStrategy適合在不需要低延遲, 但需要很低的生產者線程影響的情形。一個典型的案例就是異步日志記錄功能。

YieldingWaitStrategy  服從等待策略

The YieldingWaitStrategy is one of 2 Wait Strategies that can be use in low latency systems, where there is the option to burn CPU cycles with the goal of improving latency. The YieldingWaitStrategy will busy spin waiting for the sequence to increment to the appropriate value. Inside the body of the loop  Thread.yield()  will be called allowing other queued threads to run. This is the recommended wait strategy when need very high performance and the number of Event Handler threads is less than the total number of logical cores, e.g. you have hyper-threading enabled. ---------------------------------------------------------------------------------------------------------- YieldingWaitStrategy是2種可以用於低延遲系統的等待策略之一,充分使用壓榨cpu來達到降低延遲的目標。 YieldingWaitStrategy不斷的循環等待sequence去遞增到合適的值。 在循環體內,調用Thread.yield()來允許其他的排隊線程執行。 這是一種在需要極高性能並且event handler線程數少於cpu邏輯內核數的時候推薦使用的策略。 例如,你開啟了超線程。(譯者注:超線程是intel研發的一種cpu技術,可以使得一個核心提供兩個邏輯線程,比如4核心超線程后有8個線程)

BusySpinWaitStrategy  繁忙旋轉等待策略

The BusySpinWaitStrategy is the highest performing Wait Strategy, but puts the highest constraints on the deployment environment. This wait strategy should only be used if the number of Event Handler threads is smaller than the number of physical cores on the box.  E.g. hyper-threading should be disabled. BusySpinWaitStrategy是性能最高的等待策略,但是受部署環境的制約依賴也越強。 僅僅當event處理線程數少於物理核心數的時候才應該采用這種等待策略。 例如,超線程不可開啟。

Clearing Objects From the Ring Buffer 清理RingBuffer里的對象

When passing data via the Disruptor, it is possible for objects to live longer than intended. To avoid this happening it may be necessary to clear out the event after processing it. If you have a single event handler clearing out the value within the same handler is sufficient. If you have a chain of event handlers, then you may need a specific handler placed at the end of the chain to handle clearing out the object. 當通過disruptor傳數據的時候,有可能disruptor中的對象會存活的比打算的要長(已被使用過,垃圾數據)。為了避免這種情況,非常有必要在event處理之后去清理這些個對象。如果只有一個event handler的話,那么直接在這個handler中完成清理就可以了(處理完業務邏輯之后就清理掉evnet對象),而如果你有多個event handler組成了chain的話,拿需要自定義一個專門用來清理對象的handler,然后把它放在handler chain中的最后一個。

class ObjectEvent<T>
{
    T val;

    void clear()
    {
        val = null;
    }
}

public class ClearingEventHandler<T> implements EventHandler<ObjectEvent<T>>
{
    public void onEvent(ObjectEvent<T> event, long sequence, boolean endOfBatch)
    {
        // Failing to call clear here will result in the 
        // object associated with the event to live until
        // it is overwritten once the ring buffer has wrapped
        // around to the beginning.
        event.clear(); 
    }
}

public static void main(String[] args)
{
    Disruptor<ObjectEvent<String>> disruptor = new Disruptor<>(
        () -> ObjectEvent<String>(), bufferSize, DaemonThreadFactory.INSTANCE);

    disruptor
        .handleEventsWith(new ProcessingEventHandler())
        .then(new ClearingObjectHandler());
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM