簡體   English   中英

在 Java 中快速比較大數據集中的值和小數據集中的值

[英]Compare values in large dataset with ones in small dataset quickly in Java

目標

我正在嘗試為 Minecraft 創建一個類似於 Skyblock 的島嶼插件,並且我希望能夠為每個玩家自己的 3D 島嶼區域計算“島嶼等級”。

情況

我有一個小的hash map給我一個十進制值,用於塊類型和它們可以處於的狀態之間的可能組合的一些子集。

塊的數據集很大:大約有 16777216 個塊,我需要計算它們每個“值”的總和,如前面提到的 map 給出的。

“Pseudo-Java”中的一個幼稚(而且非常緩慢)的實現

double total = 0;
for (BlockData block : blocks) {
    for (Entry<Key, Double> entry : map) {
        Key key = entry.getKey();

        // Type check
        if (!key.getString().equals(block.getString()) continue;
        
        // States check (Only ones explicitly defined by entry must match)
        States blockStates = block.getStates();
        States keyStates = key.getStates();
        for (Entry<String, String> state : keyStates) {
            if (!state.getValue().equals(blockStates.get(state.getKey()))
                continue;
        }

        total += entry.getValue();
    }
}

我怎樣才能有效地實現水平計算?

PS Delta 編碼在這種環境中不可行,因為由於 API 限制和混淆,我無法收聽塊的設置器。

我已經設法在第一次運行時將其縮減到不到 1 秒,即使是 6700 萬塊。 我想我會在這里分享我的解決方案。

天真的方法

我的第一次嘗試是簡單地在多個線程上運行這種簡單的方法(等於或 2 倍於程序可用的邏輯處理器數量)。 這很慢,即使處理少量塊也需要大約 12 秒左右。

尋找瓶頸

IntelliJ IDEA,我正在使用的 IDE,預裝了一個很棒的工具,叫做 Java Flight Recorder。 特別是,調用樹在發現和消除瓶頸方面非常有幫助。

調用樹列出了用於任何特定方法調用的程序時間百分比。 這是一個很好的、易於閱讀的視圖,有助於快速找到瓶頸。

對於我的具體情況,塊數據對象由無法讀取特定狀態的外部 API 提供。 最初,我嘗試解析塊數據的字符串表示,但從性能的角度來看,這是一個糟糕的想法。 我的解決方案是為塊數據 QuickBlockData 編寫一個包裝器 class,它具有自定義的equalshashCode方法。

equals方法訪問外部 API 的內部塊數據,而不是 API 公開提供的數據。 它只比較了兩個塊數據對象中存在的 state 密鑰。

hashCode方法在不破壞 hash 代碼合約的情況下無法利用任何狀態,因此它只返回塊數據的類型枚舉,而不是查看任何狀態。

快速實用程序

我決定使用 FastUtil 的Object2DoubleOpenHashMap作為我的值 map,因為它最接近地代表了我想要擁有的數據結構,並且還提供了相對於 Java 自己的 hash Z1D78DC8ED51214E59FE244 實現的合理性能改進。

我沒有直接使用Object2DoubleOpenHashMap ,而是在ValueMap class 中進行了擴展,這使我可以在 getter 中運行一些搶先檢查,例如根據 map 中包含的類型的HashSet檢查輸入的類型。

並行化和塊

使用提供塊數據的同一個 API,可以異步加載 16x16 列的塊、塊,然后單獨解析它們。 這非常有用,因為這意味着我可以在加載每個塊時對其進行處理。

API 還提供了塊快照,它們是線程安全的對象,在創建時提供了有關塊的一些數據。 至關重要的是,他們提供了一種在塊內的特定塊列中查找最高塊的方法,這是一種減少我需要處理的塊的簡單方法。

概括

我用了:

  • 用於塊數據的包裝器 class,具有執行我想要的性能 equals 方法(盡管它迫使我使用糟糕的 hash 代碼)
  • 一個 ValueMap class,它擴展了 it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap,允許我快速獲取一些塊數據的值,具有可自定義的默認值,並具有對塊類型的搶先檢查。
  • 由外部 API 提供的並行計算和分塊,使用原子雙精度來計算區域的總值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM