内部简单二进制编码(SBE)
SBE是用于金融行業(yè)的非常快速的序列化庫(kù),在本博客中,我將介紹一些使其快速發(fā)展的設(shè)計(jì)選擇。
序列化的全部目的是對(duì)消息進(jìn)行編碼和解碼,并且有很多可用的選項(xiàng),從XML,JSON,Protobufer,Thrift,Avro等開(kāi)始。
XML / JSON是基于文本的編碼/解碼,在大多數(shù)情況下都很好,但是當(dāng)延遲很重要時(shí),這些基于文本的編碼/解碼就會(huì)成為瓶頸。
Protobuffer / Thrift / Avro是二進(jìn)制選項(xiàng),使用非常廣泛。
SBE也是二進(jìn)制的,是基于機(jī)械同情而構(gòu)建的,以利用基礎(chǔ)硬件(CPU緩存,預(yù)取程序,訪問(wèn)模式,管線指令等)的優(yōu)勢(shì)。
CPU和內(nèi)存革命的小歷史。
我們的行業(yè)看到了功能強(qiáng)大的8位,16位,32位,64位處理器,現(xiàn)在普通的臺(tái)式機(jī)CPU可以執(zhí)行近數(shù)十億條指令,只要程序員能夠編寫程序來(lái)生成這種類型的負(fù)載即可。 內(nèi)存也變得便宜,獲得512 GB服務(wù)器非常容易。
我們必須改變編程方式以利用所有這些東西,數(shù)據(jù)結(jié)構(gòu)和算法也必須改變。
讓我們潛入sbe。
全棧方法
大多數(shù)系統(tǒng)依賴于運(yùn)行時(shí)優(yōu)化,但是SBE已采用全棧方法,并且第一級(jí)優(yōu)化由編譯器完成。
模式 –用于定義消息的布局和數(shù)據(jù)類型的XML文件。
編譯器 –將模式作為輸入并生成IR。 在這一層中發(fā)生了很多魔術(shù),例如使用最終/常量,優(yōu)化代碼。
消息 –實(shí)際消息被緩沖區(qū)包裝。
全棧方法允許在各個(gè)級(jí)別進(jìn)行優(yōu)化。
無(wú)垃圾或少垃圾
這對(duì)于低延遲系統(tǒng)非常重要,如果不注意它,則應(yīng)用程序?qū)o(wú)法正確使用CPU緩存,并且可能進(jìn)入GC暫停狀態(tài)。
SBE是圍繞flyweight模式構(gòu)建的,它全部與重用對(duì)象有關(guān),以減輕JVM上的內(nèi)存壓力。
它具有緩沖區(qū)的概念,并且可以重復(fù)使用,編碼器/解碼器可以將緩沖區(qū)作為輸入并對(duì)其進(jìn)行處理。 編碼器/解碼器不分配或分配很少(即在使用String的情況下)。
SBE建議使用直接/分出緩沖區(qū)使GC完全脫離圖像,這些緩沖區(qū)可以在線程級(jí)別分配,并且可以用于消息的解碼和編碼。
緩沖區(qū)使用情況的代碼段。
final ByteBuffer byteBuffer = ByteBuffer.allocateDirect(4096);final UnsafeBuffer directBuffer = new UnsafeBuffer(byteBuffer);tradeEncoder .tradeId(1).customerId(999).qty(100).symbol("GOOG").tradeType(TradeType.Buy);緩存預(yù)取
CPU已內(nèi)置基于硬件的預(yù)取器。 緩存預(yù)取是計(jì)算機(jī)處理器使用的一種技術(shù),可通過(guò)在實(shí)際需要之前將指令或數(shù)據(jù)從較慢內(nèi)存中的原始存儲(chǔ)中提取到較快的本地內(nèi)存中,從而提高執(zhí)行性能。
從快速CPU緩存訪問(wèn)數(shù)據(jù)比從主存儲(chǔ)器訪問(wèn)數(shù)據(jù)快許多個(gè)數(shù)量級(jí)。
等待時(shí)間,您應(yīng)該知道的博客文章詳細(xì)介紹了CPU高速緩存的速度。
如果算法正在流式傳輸并且所使用的基礎(chǔ)數(shù)據(jù)像數(shù)組一樣連續(xù),則預(yù)取效果很好。 數(shù)組訪問(wèn)非常快,因?yàn)樗沁B續(xù)且可預(yù)測(cè)的
SBE使用數(shù)組作為基礎(chǔ)存儲(chǔ),并將字段打包在其中。
數(shù)據(jù)以小批量的高速緩存行(通常為8個(gè)字節(jié))移動(dòng),因此,如果應(yīng)用程序要求1個(gè)字節(jié),它將獲得8個(gè)字節(jié)的數(shù)據(jù)。 由于數(shù)據(jù)打包在數(shù)組中,因此可以提前訪問(wèn)單字節(jié)預(yù)取數(shù)組內(nèi)容,這將加快處理速度。
將預(yù)取器視為數(shù)據(jù)庫(kù)表中的索引。 如果讀取基于這些索引,則應(yīng)用程序?qū)⑹芤妗?
流媒體訪問(wèn)
SBE支持所有原始類型,并且還允許定義大小可變的自定義類型,這使編碼器和解碼器可以流式傳輸和順序傳輸。 從高速緩存行中讀取數(shù)據(jù)具有很好的好處,并且解碼器幾乎不需要了解有關(guān)消息的元數(shù)據(jù)(即偏移量和大小)。
附帶權(quán)衡的讀取順序必須基于布局順序,尤其是在對(duì)可變類型的數(shù)據(jù)進(jìn)行編碼的情況下。
例如寫正在使用以下順序
tradeEncoder .tradeId(1).customerId(999).tradeType(TradeType.Buy).qty(100).symbol("GOOG").exchange("NYSE");對(duì)于String屬性(符號(hào)和交換),讀取順序必須是第一個(gè)符號(hào) ,然后是exchange ,如果應(yīng)用程序交換順序,則它將讀取錯(cuò)誤的字段,對(duì)于可變長(zhǎng)度屬性,另一項(xiàng)讀取應(yīng)僅為一次,因?yàn)樗橇髟L問(wèn)模式。
好東西是有代價(jià)的!
不安全的API
數(shù)組綁定檢查可能會(huì)增加開(kāi)銷,但是SBE使用的是不安全的API,并且沒(méi)有額外的綁定檢查開(kāi)銷。
在生成的代碼上使用常量
編譯器生成代碼時(shí),它會(huì)預(yù)先計(jì)算內(nèi)容并使用常量。 一個(gè)示例是在生成的代碼中存在字段偏移,但不進(jìn)行計(jì)算。
程式碼片段
public static int qtyId(){return 2;}public static int qtySinceVersion(){return 0;}public static int qtyEncodingOffset(){return 16;}public static int qtyEncodingLength(){return 8;}這需要權(quán)衡,這對(duì)性能有利,但對(duì)靈活性不利。 您無(wú)法更改字段順序,必須在末尾添加新字段。
關(guān)于常量的另一個(gè)好處是,它們僅在生成的代碼中存在,而在消息中卻沒(méi)有,這是非常有效的。
分支免費(fèi)代碼
每個(gè)內(nèi)核都有多個(gè)并行運(yùn)行的端口,幾乎沒(méi)有指令會(huì)像分支,mod,除法那樣阻塞。 SBE編譯器生成的代碼無(wú)需使用這些昂貴的指令,并且具有基本的指針碰撞數(shù)學(xué)功能。
沒(méi)有昂貴指令的代碼非常快,它將利用內(nèi)核的所有端口。
Java序列化的示例代碼
public void writeFloat(float v) throws IOException {if (pos + 4 <= MAX_BLOCK_SIZE) {Bits.putFloat(buf, pos, v); pos += 4; } else {dout.writeFloat(v); } }public void writeLong(long v) throws IOException {if (pos + 8 <= MAX_BLOCK_SIZE) {Bits.putLong(buf, pos, v); pos += 8; } else {dout.writeLong(v); } }public void writeDouble(double v) throws IOException {if (pos + 8 <= MAX_BLOCK_SIZE) {Bits.putDouble(buf, pos, v); pos += 8; } else {dout.writeDouble(v); } }SBE的示例代碼
public TradeEncoder customerId(final long value) {buffer.putLong(offset + 8, value, java.nio.ByteOrder.LITTLE_ENDIAN); return this;}public TradeEncoder tradeId(final long value) {buffer.putLong(offset + 0, value, java.nio.ByteOrder.LITTLE_ENDIAN); return this;}郵件大小上的一些數(shù)字。
類型級(jí)元帥.SerializableMarshal-> 267號(hào)
輸入class marshal.ExternalizableMarshal-> size 75
輸入類元帥SBEMarshall->大小49
SBE最緊湊且速度最快,SBE的作者聲稱它比Google proto緩沖區(qū)快20到50倍。
可提供SBE代碼@ simple-binary-encoding
博客中使用的示例代碼可在@ sbeplayground獲得
翻譯自: https://www.javacodegeeks.com/2018/06/inside-simple-binary-encoding-sbe.html
總結(jié)
以上是生活随笔為你收集整理的内部简单二进制编码(SBE)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: xml 数字签名 破解_JAVA中带有数
- 下一篇: maven设置代理服务器_使用Maven