高頻交易
高頻交易是指從那些人們無法利用的極為短暫的市場變化中尋求獲利的計算機化交易,比如,某種證券買入價和賣出價差價的微小變化,或者某只股票在不同交易所之間的微小價差。在高頻交易中,自動化應用程序每天處理幾億個市場信號,在全球各地的交易所發送上千萬個訂單。為了保持業務競爭力,響應時間必須始終保持在微秒級,尤其是在黑天鵝異常事件等高峰期。
高頻交易系統的典型系統結構一般是這樣:金融交易信號將轉換成內部市場數據格式(交易使用TCP、UDP等各種協議)和多種格式(如二進制、SBE、JSON、FIX等)。然后,這些標准化的消息被發送到算法服務器、統計引擎、UI、Log Server和各種數據庫(緩存、文件或分布式數據庫)。任何延遲都會帶來都會帶來高成本的結果。例如,根據根據舊的價格進行決策或下單太遲。為了獲得微秒級的優勢,大部分交易參與者都會投入高價硬件:一個超頻液冷CPU的服務器池(2020年可以買56核、5.6GHz、1TB內存的服務器),組裝在主交換數據中心、高端納秒級網絡交換機、專用跨洋線,甚至是微波網絡。
常見的高頻交易系統使用高度定制的Linux內核,並且帶有操作系統旁路,這樣數據就可以直接從網卡 "跳轉" 到應用程序、基於IPC 進程間通信,甚至使用FPGA(可編程單用途芯片)。至於編程語言,一般首先想到的就是C++,事實上也確實是這個領域的天然選擇。C++的最大優勢就是運行速度快,最接近機器代碼,而且是直接根據目標平台進行編譯,具有高效穩定的特點。
使用Java代替C++
我們做了一個不同的選擇。在過去14年里,我們在外匯算法交易領域用Java進行開發,並使用廉價的硬件代替昂貴的高端設備。
在一個團隊小,資源有限以及熟練開發人員欠缺的工作環境,Java意味着我們可以快速進行軟件迭代,因為Java生態系統比C系列具有更快的開發效率。可以在早上討論改進措施,並在下午在生產中實施、測試和發布。
與需要幾周甚至幾個月軟件更新時間的大型公司相比,這是一個關鍵優勢。在這個領域,一個錯誤可以在幾秒鍾內抹去一整年的利潤,因此不能在質量上妥協。我們使用了許多開源庫和項目,實現了嚴格的敏捷開發環境,包括使用Jenkins、Maven、單元測試、夜間構建和Jira。通過Java,開發人員可以專注於業務邏輯,而不是像C++那樣調試內存Coredump或跟指針打交道。而且,由於Java強大的內存管理,初級程序員也可以立即參與開發代碼,並且風險可控。
只要有良好的設計模式和干凈的編碼習慣,就可以用Java達到C++的延遲。我們都知道,使Java成為軟件開發強大和方便語言的原因,同時也是它的缺點的最主要的原因,那就是Java虛擬機(JVM)。
Java即時編譯代碼(Just in Time 編譯器),意味着第一次遇到一些代碼時,也可能產生編譯延遲。Java管理內存的方式是通過在堆空間中分配內存塊。每隔一段時間,它就會清理這個空間,刪除舊的對象,為新的對象騰出空間。主要問題是,為了進行准確的統計,應用程序線程需要被瞬間 "凍結"。這個過程被稱為垃圾收集(GC)。GC是低延遲應用程序開發人員放棄 Java 的主要原因。
市場上Java 虛擬機最常見和標准的是 Oracle Hotspot JVM,它在 Java 社區中被廣泛使用,主要是出於歷史原因。對於要求非常高的應用程序,Azul Systems 提供了一個很棒的替代方案,稱為 Zing。Zing是Oracle Hotspot JVM一個強大的替代品。Zing解決了GC暫停和JIT編譯問題。
讓我們來研究使用Java的固有問題和可能的解決方案。
理解Java即時編譯器
像C++這樣的語言被稱為編譯語言,因為交付的代碼完全是二進制的,可以直接在CPU上執行。PHP或Perl 被稱為解釋語言,因為解釋器(安裝在目標機器上)會邊運行邊編譯每一行代碼。
Java介於兩者之間;它將代碼編譯成所謂的 Java 字節碼,而字節碼又可以在它認為合適的時候被編譯成二進制。Java之所以不在啟動時編譯代碼,與長期的性能優化有關。通過觀察應用程序的運行情況,分析實時的方法調用和類的初始化,Java 會編譯經常調用的部分代碼。它甚至可能會根據經驗做出一些假設(這部分代碼永遠不會被調用,或者這個對象永遠是一個 String)。
因此,實際編譯后的代碼速度非常快,但依然有3個缺點。
1、一個方法需要被調用一定的次數來達到編譯閾值,然后才能被優化和編譯(這個限制是可以配置,但通常是10000 次左右的調用)。在此之前,未經優化的代碼並沒有以 "全速" 運行。Java在更快的編譯和高質量的編譯之間做了一個取舍(如果假設不對,會有重新編譯的代價)。
2、當Java應用程序重啟時,又回到了原點,必須等待再次達到這個閾值。
3、有些應用程序(比如我們的場景)有一些不頻繁但很關鍵的方法,這些方法只會被調用少數幾次,但當它們被調用時,需要極快的速度(想想看,一個風險或止損函數只有在緊急情況下才會被調用)。
Azul Zing通過讓其JVM將編譯后的方法和類的狀態 "保存" 在它所謂的配置文件中來解決這些問題。這種名為 ReadyNow!® 的獨特功能,意味着Java應用程序始終以最佳速度運行,即使在重新啟動后也是如此。當使用現有的配置文件重新啟動應用程序時,Azul JVM會立即調用其先前的結果並直接編譯標注的的方法,從而解決了 Java 預熱問題。
此外,可以在開發環境中建立一個配置文件,以模擬生產行為。然后,優化后的配置文件可以部署在生產環境中,因為所有的關鍵路徑都被編譯和優化了。Zing的延遲隨着時間的推移保持相當穩定。百分位數分布表明,1%的時間里,Hotspot JVM產生的延遲是 Zing JVM的16倍。
解決垃圾收集(GC)暫停的問題
在垃圾收集過程中,整個應用程序可能會凍結幾毫秒到幾秒不等(延遲隨着代碼復雜度和堆大小而增加),更糟糕的是,你無法控制這種情況何時發生。雖然暫停一個應用程序幾毫秒甚至幾秒鍾對於許多Java應用程序來說可能是可以接受的,但對於低延遲應用程序來說卻是一場災難,無論是汽車、航空航天、醫療還是金融領域。
GC的影響在Java開發者中是一個很大的話題;一個完整的垃圾收集通常被稱為 "stop-the-world",因為它會凍結整個應用程序。
多年來,許多GC算法都試圖在吞吐量(多少CPU用於實際的應用邏輯而不是垃圾收集)與 GC暫停之間做一個取舍。
自Java 9以來,G1 收集器一直是默認 GC,其主要思想是根據用戶提供的時間目標來划分GC暫停時間。它通常提供較短的暫停時間,但代價是較低的吞吐量。此外,暫停時間會隨着堆的大小而增加。Java提供了大量的設置來調整其垃圾收集(以及 JVM),從堆大小到收集算法,以及分配給GC的線程數。所以,看到Java應用程序配置了大量的自定義選項是很常見的。
很多開發者已經轉向各種技術來完全避免GC。主要思路是,如果創建的對象少了,需要清除的對象就會變少。一個古老的技術是使用可重用對象的對象池。例如,一個數據庫連接池將持有10個已打開的連接的引用,准備在需要時使用。
多線程通常需要鎖,這會導致同步延遲和暫停(特別是當它們共享資源時)。一個流行的設計是一個環形緩沖隊列系統,在一個無鎖的設置中,有許多線程寫和讀。一些專家甚至選擇完全自己實現 Java 內存管理,自己管理內存分配,雖然解決了一個問題,但卻帶來了更多的復雜性和風險。在這種情況下,顯然應該考慮其他 JVM,於是我們決定嘗試 Azul Zing JVM。很快,我們就實現了非常高的吞吐量,停頓可以忽略不計。
這是因為Zing使用了一個獨特的收集器,叫做C4(Continuurrentously Concurrent Compacting Collector),它允許無暫停地收集垃圾,而不關心Java堆的大小(最高可達8TB)。這是通過在應用程序仍在運行時,並發映射和壓縮內存來實現。此外,它不需要修改任何代碼,延遲和速度的提升都是開箱即見,無需冗長的配置。在這種情況下,Java程序員可以享受到兩全其美的好處,既可以享受到 Java 的簡單性(無需偏執於創建新對象),又可以享受到Zing的底層性能,使整個系統的延遲高度可預測。
多虧了GC easy,一個通用的GC日志分析器,我們可以在真實的自動交易應用中(在模擬環境中)快速比較兩種JVM。在高頻交易的應用中,使用Zing的GC比使用標准的 Oracle Hotspot JVM 小 180 倍左右。更令人印象深刻的是,GC暫停通常與實際應用暫停時間相對應,而Zing智能GC通常是在最小或沒有實際暫停的情況下平行發生的。
總結
Java在享受簡單性和面向業務的特性同時,仍然可以實現高性能和低延遲。雖然C++ 仍然可用於特定的底層組件,如驅動程序、數據庫、編譯器和操作系統,但大多數現實中都可以用Java來開發,包括象高頻交易這樣要求苛刻的應用。
java jdk 下載 來自 嗖嗖下載