您現在的位置是:首頁 > 人文
透過高效的物件編組改進應用程式的序列化
為什麼要引用十六進位制
每日分享最新,最流行的軟體開發知識與最新行業趨勢,希望大家能夠一鍵三連,多多支援,跪求關注,點贊,留言。
演示如何使用物件封送示例將小字串編碼為長原語,以及如何提高應用的序列化效能。
高效的程式碼不僅執行得更快;如果它使用較少的計算資源,則執行起來可能更便宜。特別是,分散式雲應用程式可以受益於快速、輕量級的序列化。
開源 Java 序列化器
Chronicle-Wire是一個開源 Java 序列化程式,可以讀取和寫入不同的訊息格式,例如 JSON、YAML 和原始二進位制資料。此序列化程式可以在壓縮資料格式化(在同一空間中儲存更多資料)與壓縮資料(減少所需儲存量)之間找到一箇中間地帶。相反,資料儲存在儘可能少的位元組中,而不會導致效能下降。這是透過編組一個物件來完成的。
什麼是物件編組?為什麼使用它?
編組是序列化的另一個名稱。換句話說,它是將物件的記憶體表示轉換為另一種格式的過程。使用wire,我們可以編寫與書面格式無關的編組程式碼,因此可以使用相同的編組程式碼來生成/讀取 YAML、JSON 或二進位制表示。因為我們可以生成人類可讀的表示,我們可以簡單地實現一個toString()方法只需寫入一個可讀的連線例項(以及等於和雜湊碼,假設序列化形式等同於物件的標識)。此外,對於可讀的有線例項,我們可以將數值寫入字串表示(例如,時間戳長轉換器)或使用長轉換以數字形式儲存短文字值,以便緊湊寫入二進位制表示。這允許您選擇最適合應用程式的格式。例如,在讀取手工製作的配置檔案時,我們可以使用 YAML。在透過電線傳送到另一臺機器或將其儲存在機器可讀檔案中時,我們可以使用二進位制檔案。或者,我們也可以在它們之間進行轉換,例如除錯透過線路傳輸的二進位制訊息,我們可以從二進位制格式讀取並使用 YAML 格式記錄。這都可以使用相同的程式碼執行。
LongConverter 示例
本示例介紹了一個簡單的普通舊 Java 物件 (POJO) 示例。
public class LongConversionExampleA {
public static class House {
long owner;
public void owner(CharSequence owner) {
this。owner = Base64LongConverter。INSTANCE。parse(owner);
}
@Override
public String toString() {
return “House{” +
“owner=” + owner +
‘}’;
}
}
public static void main(String[] args) {
House house = new House();
house。owner(“Bill”);
System。out。println(house);
}
}
我們透過將 String 物件儲存為 long 來開始該過程。此處使用ABase64LongConverter來解析提供的 CharSequence 並將結果作為 long 返回。示例程式碼可以在LongConversionExampleA中看到。
public class LongConversionExampleA {
public static class House {
long owner;
public void owner(CharSequence owner) {
this。owner = Base64LongConverter。INSTANCE。parse(owner);
}
@Override
public String toString() {
return “House{” +
“owner=” + owner +
‘}’;
}
}
public static void main(String[] args) {
House house = new House();
house。owner(“Bill”);
System。out。println(house);
}
}
然後將房主的姓名列印為一個數字,因為它已被儲存為長:
House{owner=670118}
列印 YAML 示例
然後我們可以擴充套件這個類以使用 Chronicle 的基類之一 SelfDescribeingMarshallable,它允許我們簡單地實現一個toString()方法,並且可以重建物件。這對於從檔案構建單元測試中的示例資料很有用。這也意味著您可以在日誌檔案中轉儲物件並重建原始物件。下面的程式碼中演示的是 。addAlias;這允許引用 House 而不是 to net。openhft。chronicle。LongConversionExampleB$House。
LongConversionExampleB說明了如何將輸出列印為 YAML:
public class LongConversionExampleB {
static {
ClassAliasPool。CLASS_ALIASES。addAlias(LongConversionExampleB。House。class);
}
public static class House extends SelfDescribingMarshallable {
@LongConversion(Base64LongConverter。class)
long owner;
public void owner(CharSequence owner) {
this。owner = Base64LongConverter。INSTANCE。parse(owner);
}
}
public static void main(String[] args) {
House house = new House();
house。owner(“Bill”);
System。out。println(house);
}
}
執行它時,不是列印數字,而是列印以下內容:
!House {
Owner: Bill
}
列印 JSON 示例
如果我們希望輸出為 JSON,我們可以從以下行中刪除LongConversionExampleB:
System。out。println(house);
並將其替換為 Wire,因為這是一種更輕量級的替代方案:
Wire wire = WireType。JSON。apply(Bytes。allocateElasticOnHeap());
wire。getValueOut()。object(house);
System。out。println(wire);
這將輸出以下內容:
{“owner”: “Bill”}
為什麼這有幫助?為什麼我們不能將它最初儲存為字串而不是長字串?
將其儲存為 long 是儲存此資料的更有效方式。雖然通常有 8 位元組到長,但透過使用@LongConversion(Base64LongConverter。class),我們可以將 10 個 Base64 編碼字元儲存到 8 位元組長中。
這怎麼可能?
通常,當我們談論一個位元組時,一個位元組可以表示 256 個不同字元中的一個。
然而,由於我們使用了 Base64LongConverter,我們無法表示 256 個字元之一,而是說 8 位位元組只能表示 64 個字元之一:
。ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+
透過限制一個位元組可以表示的字元數,可以將更多的字元壓縮成一個長字元。
如果這 64 個字元不包括您需要的字元怎麼辦?或者如果仍然太多怎麼辦?
Chronicle Wire 有不同的版本LongConverter,從 aBase64LongConverter到 Base32LongConverter。此外,還可以自定義您自己的基本編碼。畢竟,更少的字元會帶來更緊湊的資料儲存方式,這反過來又意味著資料的讀寫速度更快,誰不想這樣呢?
欄位組示例
雖然上面的示例可以很好地儲存少量字元,但如果儲存一些更長的字元,例如家庭地址呢?
這是我們可以利用Chronicle Bytes@FieldGroup的地方:
import net。openhft。chronicle。bytes。Bytes;
import net。openhft。chronicle。bytes。FieldGroup;
在下面的LongConversionExampleC中,我們將介紹如何將多個 long 儲存到FieldGroup。 在此示例中,@FieldGroup最多可以儲存 5 個長字元,因此最多可以儲存 40 個字元。
在“如何在 Java 序列化中獲得 C++ 速度”一文中可以看到以原始 long 形式儲存它的好處。
public static class House extends SelfDescribingMarshallable {
@FieldGroup(“address”)
// 5 longs, each at 8 bytes = 40 bytes, so we can store a String with up to 39 ISO-8859 characters (as the first byte contains the length)
private long text4a, text4b, text4c, text4d, text4e;
private transient Bytes address = Bytes。forFieldGroup(this, “address”);
public void address(CharSequence owner) {
address。append(owner);
}
}
下面的示例繼續說明如何建立一個byte[]來儲存位元組,將房屋物件寫入其中,然後讀取它們。
public static void main(String[] args) {
House house = new House();
house。address(“82 St John Street, Clerkenwell, London”);
// creates a buffer to store bytes
final Bytes<?> t = allocateElasticOnHeap();
// the encoding format
final Wire wire = BINARY。apply(t);
// writes the house object to the bytes
wire。getValueOut()。object(house);
// dumps out the contents of the bytes
System。out。println(t。toHexString());
System。out。println(t);
// reads the house object from the bytes
final House object = wire。getValueIn()。object(House。class);
// prints the value of text4
System。out。println(object。address);
}
當我們使用toHexString( )時,這個例子打印出我們的資料,如圖 1 所示。這是生成十六進位制轉儲的標準方法。綠色部分代表“偏移量”。從字串開頭到當前位置的位元組數。紅色部分突出顯示儲存資料的“十六進位制值”。為了閱讀這個,我們可以取十六進位制數 48(在頂行),首先將其轉換為十進位制 - HEX 48,因為十進位制是 72。然後我們取這個十進位制 72 並使用ASCII 字元圖表,它告訴我們這是字元“H”。如果我們在藍色部分看到“ASCI IOS-8859”,我們會看到它對應於“H”中的第三個字元。
十六進位制字串輸出
圖 1。 toHexString() 輸出
@Base64
如上面的示例所示,我們使用了:
@LongConversion(Base64LongConverter。class)
應該注意的是,這可以簡化為:
@Base64
在下面的程式碼塊中可以看到一個正在實現的示例:
package net。openhft。chronicle。wire;
import net。openhft。chronicle。bytes。Bytes;
import net。openhft。chronicle。wire。converter。Base64;
public class Example {
public static class Base64LongConverterValue extends SelfDescribingMarshallable {
@LongConversion(Base64LongConverter。class)
long value;
public Base64LongConverter value(String msg) {
value = Base64LongConverter。INSTANCE。parse(msg);
return this;
}
}
public static class Base64Value extends SelfDescribingMarshallable {
@Base64
long value;
public Base64Value value(String msg) {
value = Base64。INSTANCE。parse(msg);
return this;
}
}
public static void main(String[] args) {
new Example()。start();
}
private static void start() {
Bytes b = Bytes。allocateEleasticOnHeap();
Wire w = WireType。JSON。apply(b);
w。getValueOut()。object(new Base64Value()。value(“hello”));
System。out。println(w。toString());
}
}
建立自己的註釋
Base64建立包含您自己選擇的 64 個字元的註釋很容易。下面是我們如何建立@Base64,它利用 SymbolsLongConverter。
package net。openhft。chronicle。wire。converter;
import net。openhft。chronicle。wire。*;
import java。lang。annotation。*;
@Retention(RetentionPolicy。RUNTIME)
@Target({ElementType。FIELD, ElementType。PARAMETER})
@LongConversion(Base64。class)
public @interface Base64 {
LongConverter INSTANCE = new SymbolsLongConverter(
“。ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_”);
}
新增時間戳
下面的示例演示瞭如何在每次建立事件時建立時間戳。
public class NanoTimeTest {
@Test
public void yaml() {
Wire wire = Wire。newYamlWireOnHeap();
UseNanoTime writer = wire。methodWriter(UseNanoTime。class);
long ts = NanoTime。INSTANCE。parse(“2022-06-17T12:35:56”);
writer。time(ts);
writer。event(new Event(ts));
assertEquals(“” +
“time: 2022-06-17T12:35:56\n” +
“。。。\n” +
“event: {\n” +
“ start: 2022-06-17T12:35:56\n” +
“}\n” +
“。。。\n”, wire。toString());
}
interface UseNanoTime {
void time(@NanoTime long time);
void event(Event event);
}
static class Event extends SelfDescribingMarshallable {
@NanoTime
private long start;
Event(long start) {
this。start = start;
}
}
}
JLBH 基準表現
為了探索這些示例的效率,建立了這個TriviallyCopyableJLBH。java測試。從第 23-26 行可以看出,我們可以在執行TriviallyCopyableHouse(“House1”)或 House(“House2”)之間切換BinaryWire。需要注意的重要一點是,使用普通可複製物件來提高 java 序列化速度。這表明我們可以每秒序列化和反序列化 100,000 條訊息。Trivially Copyable 版本甚至更快,尤其是在較高的百分位數上。
基準效能
圖 2。 TriviallyCopyable 和 BinaryWire 之間的基準效能
*序列化和反序列化訊息的微秒
結論
總體而言,LongConversion 是有益的,因為比較原始 long 比比較字串更有效。即使我們考慮到 String 最初可以使用它們的hashcode()。 此外,原始 long 直接儲存在 Object 中(此示例使用“House”物件),因此在訪問它們時,您不必經歷訪問物件(例如字串)時所獲得的間接級別。其參考。
將資料儲存到原語TriviallyCopyable中,可以透過簡單地將 java 物件的記憶體複製為序列化位元組來序列化物件。上圖顯示該技術改善了序列化和反序列化延遲。
推薦文章
- 三種關係,容易發展成情人
而這個時候的男人,還在為了女人的示好還沾沾自喜,很容易就會變得親密,發展成情人關係...
- 三星S20 Ultra的螢幕雖然出色,但是很傷眼睛,很多人還不知道
OLED的螢幕製造商三星在八年前的旗艦機S2用過全程DC調光,是顯示屏底層的DC調光,不同現在說的類DC調光...
- 《阿凡達》為什麼人類不用核彈消滅敵人?因為地球已經徹底玩完了
相信大家都看了卡梅隆的新作《阿凡達:水之道》了吧,不得不說,這部電影裡面,我總覺得把前作的反派都給洗白了,也算是編劇的過人之處了,但是,問題也跟著出現了,在劇情裡面,我們都知道,人類已經近乎於掌握了整個潘多拉星球了,可他們卻被潘多拉星球的原...