JLBH示例4 – QuickFix vs ChronicleFix基准化
在這篇文章中:
- 使用JLBH測試QuickFIX
- 觀察QuickFix延遲如何通過百分位數(shù)降低
- 比較QuickFIX和Chronicle FIX
如JLBH簡介中所述,創(chuàng)建JLBH的主要原因是為了測量Chronicle-FIX引擎。
我們使用了JLBH的所有功能,特別是吞吐量杠桿和協(xié)調遺漏的說明,以獲取一些實際的QuickFIX時間。
在本文的后面,我們將看到ChronicleFIX的一些結果,但首先讓我們看一下對FixFix的開源實現(xiàn)的基準測試。
這是我們將要進行基準測試的場景:
- 客戶端創(chuàng)建一個NewOrderSingle,然后將其傳遞到服務器。
- 服務器解析NewOrderSingle
- 服務器創(chuàng)建一個ExecutionReport,該報告將發(fā)送回客戶端。
- 客戶端收到執(zhí)行報告
從客戶端開始創(chuàng)建NewOrderSingle到客戶端收到ExecutionReport的時間開始計算端到端時間。
注意:我們需要在程序右邊保留調用基準測試的開始時間。 為此,我們使用了一個技巧,并將開始時間設置為標簽ClOrdId。
如果要在服務器上運行基準測試,則應克隆此GitHub存儲庫,所有jar和配置文件都在此處設置。
為了這篇文章,這里是基準測試的代碼。
package org.latency.quickfix;import net.openhft.chronicle.core.Jvm; import net.openhft.chronicle.core.jlbh.JLBHOptions; import net.openhft.chronicle.core.jlbh.JLBHTask; import net.openhft.chronicle.core.jlbh.JLBH; import quickfix.*; import quickfix.field.*; import quickfix.fix42.ExecutionReport; import quickfix.fix42.NewOrderSingle;import java.util.Date; import java.util.concurrent.Executors;/*** Created by daniel on 19/02/2016.* Latency task to test sending a message in QuickFix*/ public class QFJLBHTask implements JLBHTask {private QFClient client;private JLBH lth;private static NewOrderSingle newOrderSingle;private static ExecutionReport executionReport;public static void main(String[] args) {executionReport = new ExecutionReport();executionReport.set(new AvgPx(110.11));executionReport.set(new CumQty(7));executionReport.set(new ClientID("TEST"));executionReport.set(new ExecID("tkacct.151124.e.EFX.122.6"));executionReport.set(new OrderID("tkacct.151124.e.EFX.122.6"));executionReport.set(new Side('1'));executionReport.set(new Symbol("EFX"));executionReport.set(new ExecType('2'));executionReport.set(new ExecTransType('0'));executionReport.set(new OrdStatus('0'));executionReport.set(new LeavesQty(0));newOrderSingle = new NewOrderSingle();newOrderSingle.set(new OrdType('2'));newOrderSingle.set(new Side('1'));newOrderSingle.set(new Symbol("LCOM1"));newOrderSingle.set(new HandlInst('3'));newOrderSingle.set(new TransactTime(new Date()));newOrderSingle.set(new OrderQty(1));newOrderSingle.set(new Price(200.0));newOrderSingle.set(new TimeInForce('0'));newOrderSingle.set(new MaturityMonthYear("201106"));newOrderSingle.set(new SecurityType("FUT"));newOrderSingle.set(new IDSource("5"));newOrderSingle.set(new SecurityID("LCOM1"));newOrderSingle.set(new Account("ABCTEST1"));JLBHOptions jlbhOptions = new JLBHOptions().warmUpIterations(20_000).iterations(10_000).throughput(2_000).runs(3).accountForCoordinatedOmmission(false).jlbhTask(new QFJLBHTask());new JLBH(jlbhOptions).start();}@Overridepublic void init(JLBH lth) {this.lth = lth;Executors.newSingleThreadExecutor().submit(() ->{QFServer server = new QFServer();server.start();});Jvm.pause(3000);client = new QFClient();client.start();}@Overridepublic void complete() {System.exit(0);}@Overridepublic void run(long startTimeNs) {newOrderSingle.set(new ClOrdID(Long.toString(startTimeNs)));try {Session.sendToTarget(newOrderSingle, client.sessionId);} catch (SessionNotFound sessionNotFound) {sessionNotFound.printStackTrace();}}private class QFServer implements Application {void start() {SocketAcceptor socketAcceptor;try {SessionSettings executorSettings = new SessionSettings("src/main/resources/acceptorSettings.txt");FileStoreFactory fileStoreFactory = new FileStoreFactory(executorSettings);MessageFactory messageFactory = new DefaultMessageFactory();FileLogFactory fileLogFactory = new FileLogFactory(executorSettings);socketAcceptor = new SocketAcceptor(this, fileStoreFactory,executorSettings, fileLogFactory, messageFactory);socketAcceptor.start();} catch (ConfigError e) {e.printStackTrace();}}@Overridepublic void onCreate(SessionID sessionId) {}@Overridepublic void onLogon(SessionID sessionId) {}@Overridepublic void onLogout(SessionID sessionId) {}@Overridepublic void toAdmin(Message message, SessionID sessionId) {}@Overridepublic void fromAdmin(Message message, SessionID sessionId)throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue,RejectLogon {}@Overridepublic void toApp(Message message, SessionID sessionId) throws DoNotSend {}@Overridepublic void fromApp(Message message, SessionID sessionId)throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue,UnsupportedMessageType {try {executionReport.set(((NewOrderSingle) message).getClOrdID());Session.sendToTarget(executionReport, sessionId);} catch (SessionNotFound invalidMessage) {invalidMessage.printStackTrace();}}}private class QFClient implements Application {private SessionID sessionId = null;void start() {SocketInitiator socketInitiator;try {SessionSettings sessionSettings = new SessionSettings("src/main/resources/initiatorSettings.txt");FileStoreFactory fileStoreFactory = new FileStoreFactory(sessionSettings);FileLogFactory logFactory = new FileLogFactory(sessionSettings);MessageFactory messageFactory = new DefaultMessageFactory();socketInitiator = new SocketInitiator(this,fileStoreFactory, sessionSettings, logFactory,messageFactory);socketInitiator.start();sessionId = socketInitiator.getSessions().get(0);Session.lookupSession(sessionId).logon();while (!Session.lookupSession(sessionId).isLoggedOn()) {Thread.sleep(100);}} catch (Throwable exp) {exp.printStackTrace();}}@Overridepublic void fromAdmin(Message arg0, SessionID arg1) throws FieldNotFound,IncorrectDataFormat, IncorrectTagValue, RejectLogon {}@Overridepublic void fromApp(Message message, SessionID arg1) throws FieldNotFound,IncorrectDataFormat, IncorrectTagValue, UnsupportedMessageType {long startTime = Long.parseLong(((ExecutionReport) message).getClOrdID().getValue());lth.sample(System.nanoTime() - startTime);}@Overridepublic void onCreate(SessionID arg0) {}@Overridepublic void onLogon(SessionID arg0) {System.out.println("Successfully logged on for sessionId : " + arg0);}@Overridepublic void onLogout(SessionID arg0) {System.out.println("Successfully logged out for sessionId : " + arg0);}@Overridepublic void toAdmin(Message message, SessionID sessionId) {boolean result;try {result = MsgType.LOGON.equals(message.getHeader().getField(new MsgType()).getValue());} catch (FieldNotFound e) {result = false;}if (result) {ResetSeqNumFlag resetSeqNumFlag = new ResetSeqNumFlag();resetSeqNumFlag.setValue(true);((quickfix.fix42.Logon) message).set(resetSeqNumFlag);}}@Overridepublic void toApp(Message arg0, SessionID arg1) throws DoNotSend {}} } 這些是我看到的在服務器Intel?Xeon?CPU E5-2650 v2 @ 2.60GHz上運行的結果。 
 吞吐量為2,000 / s 
吞吐量為10,000 / s
Percentile run1 run2 run3 % Variation 50: 184.32 176.13 176.13 0.00 90: 573.44 270.34 249.86 5.18 99: 19398.66 2686.98 5111.81 37.56 99.9: 28835.84 7733.25 7995.39 2.21 99.99: 30932.99 9699.33 9175.04 3.67 worst: 30932.99 9699.33 9175.04 3.67平均值是?200us,但是當您通過百分位數(shù)時,延遲確實開始降低。 這在很大程度上歸因于所創(chuàng)建的垃圾量! 您可以通過使用jvm標志-verbosegc運行基準測試來查看此信息。 實際上,當您將吞吐量提高到50,000 / s時,甚至完全耗盡了第90個百分位數(shù)(每10個迭代中有1個),并且最終會延遲數(shù)毫秒。
吞吐量為50,00 / s
Percentile run1 run2 run3 % Variation var(log) 50: 176.13 176.13 176.13 0.00 11.82 90: 12845.06 29884.42 3604.48 82.94 21.01 99: 34603.01 94371.84 17301.50 74.81 25.26 99.9: 42991.62 98566.14 25690.11 65.41 25.84 99.99: 45088.77 98566.14 27787.26 62.94 25.93 worst: 45088.77 98566.14 27787.26 62.94 25.93 這里的問題不僅是平均時間(假設?200us對您來說太慢了),而且更令人擔憂的是,隨著吞吐量的增加以及研究更高的百分位數(shù),數(shù)字的降低方式。 讓我們比較一下Chronicle-FIX。 該測試在完全相同的場景和同一臺計算機上運行。 
 結果看起來像這樣: 
吞吐量為2000 / s
Percentile run1 run2 run3 % Variation 50: 16.90 16.90 16.90 0.00 90: 18.94 18.94 18.94 0.00 99: 26.11 30.21 23.04 17.18 99.9: 35.84 39.94 33.79 10.81 99.99: 540.67 671.74 401.41 65.41 worst: 638.98 1081.34 606.21 61.59吞吐量為10,000 / s
Percentile run1 run2 run3 % Variation 50: 16.90 16.90 16.13 3.08 90: 18.94 18.94 18.94 0.00 99: 26.11 22.02 20.99 3.15 99.9: 88.06 33.79 83.97 49.75 99.99: 999.42 167.94 802.82 71.59 worst: 1146.88 249.86 966.66 65.67吞吐量為50,000 / s
Percentile run1 run2 run3 % Variation 50: 15.62 15.10 15.62 2.21 90: 17.92 16.90 16.90 0.00 99: 22.02 30.21 29.18 2.29 99.9: 120.83 352.26 33.79 86.27 99.99: 335.87 802.82 96.26 83.03 worst: 450.56 901.12 151.55 76.73Chronicle-FIX的平均值約為16us,比QuickFIX快12倍。 但這不僅僅因為幾乎所有時間都在TCP往返中。 當您測量TCP時間時(請參閱最新發(fā)布的JLBH示例3 –吞吐量對延遲的影響 ),結果發(fā)現(xiàn)大部分時間是TCP?10us。 因此,如果扣除TCP時間,就可以得到。
- QuickFix 200 – 10 = 190
- 編年史-16-10 = 6
- Chronicle-FIX比QF快30倍以上
正如已經(jīng)證明的那樣,如果您關心較高的百分位數(shù),就會比這差得多。 為了完整起見,應該注意的是,作為基準測試的服務器噪聲很大。 它的延遲峰值約為400us,這說明較高百分比中顯示的數(shù)字更大。 此測試還使用環(huán)回TCP,這給Linux內核帶來了巨大壓力。 實際上,當您將吞吐量提高得很高時(會通過簡單的TCP測試進行嘗試)會發(fā)生奇怪的事情-因此,這不是測試Chronicle-FIX的最佳方法。 它僅用作與Quick FIX的比較。
使用Chronicle-FIX,如果您在調整后的服務器上測量將修復消息解析為數(shù)據(jù)模型(包括日志記錄)的過程,則實際上可以看到此概要文件已通過10,000 / s至200,000 / s的吞吐量概要文件進行了測試:
Percentile run1 run2 run3 run4 run5 50: 1.01 1.01 1.01 1.01 1.06 90: 1.12 1.12 1.12 1.12 1.12 99: 1.38 1.31 1.44 1.31 2.11 99.9: 2.88 2.88 2.88 2.88 4.03 99.99: 3.26 3.14 3.39 3.14 6.02 worst: 5.25 6.27 22.02 20.99 18.94翻譯自: https://www.javacodegeeks.com/2016/04/jlbh-examples-4-benchmarking-quickfix-vs-chroniclefix-2.html
總結
以上是生活随笔為你收集整理的JLBH示例4 – QuickFix vs ChronicleFix基准化的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 又a又飒是什么意思 又a又飒解释
- 下一篇: 万事遂意是什么意思 万事遂意解释
