簡體   English   中英

提高F# map插入性能

[英]Increase F# map insertion performance

我目前正在對 F# 地圖與 C# 字典進行一些測試。 我意識到它們在實現方面完全不同,但它們確實為各自的語言提供了相同的用途。

我設計了一個簡單的測試來檢查插入時間,因為 F# map 是不可變的,因此它必須為每次插入創建一個全新的 map。 我想知道這有多大的打擊。

測試如下:

 //F# 
 module Test = 
    let testMapInsert () = 
        let sw = Stopwatch()
        let rec fillMap endIdx curr map =
            if curr = endIdx then 
                map 
            else 
                fillMap endIdx (curr + 1) (map |> Map.add curr curr)
        sw.Start ()
        let q = fillMap 100000000 Map.empty
        sw.Stop ()
        printfn "%A" sw.ElapsedMilliseconds

 //C#
 class Program
    {
        static void Test(int x) {
            var d = new Dictionary<int,int>();
            for (int i = 0; i < x; i++) {
                d.Add(i,i);
            }
        }
        static void Main(string[] args) {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            Test(10000000);
            sw.Stop();
            System.Console.WriteLine(sw.ElapsedMilliseconds);
            //FSHARP.Test.testMapInsert(); f# function called in c#.

        }
    }

使用它進行 1000 萬個元素插入會產生以下以 ms 為單位的時間:

C#: 332

F#: 13605

我認為 C# 字典會快一點,但差別很大。

有沒有辦法為這種用例加速 F# 字典? 或者這就是它的方式和 F# map 在這些情況下為了線程安全而權衡性能?

正如評論中提到的,區別不是基於 C# 和 F# 之間的區別,而是基於基於不可變樹的 map 和基於哈希表的字典之間的區別。

使用#time ,我在 F# 交互式中獲得以下性能:

#time 
// Immutable tree-based F# map (~14 sec)
let mutable map = Map.empty
for i in 0 .. 10000000 do
  map <- Map.add i i map

// Mutable hashtable-based .NET dictionary (~0.3 sec)
let dict = System.Collections.Generic.Dictionary<_, _>()
for i in 0 .. 10000000 do
  dict.Add(i, i)

有趣的問題是——你能讓不可變的 F# map 更快嗎? 原則上,如果您知道您正在使用已排序的數組,則可以更快地構建 map。 F# map 沒有任何操作可以讓您執行此操作,但可以添加。

當我定義自己的 Map 類型時,它與 F# map 共享內部結構:

type MapTree<'Key, 'Value when 'Key : comparison > = 
  | MapEmpty 
  | MapOne of 'Key * 'Value
  | MapNode of 'Key * 'Value * MapTree<'Key, 'Value> *  MapTree<'Key, 'Value> * int

然后我可以定義ofSortedArray操作:

let height = function
  | MapEmpty -> 0
  | MapOne _ -> 1
  | MapNode(_, _, _, _, h) -> h

let rec ofSortedArray (data:_[]) i j = 
  if i = j then MapOne(data.[i])
  elif i > j then MapEmpty 
  else 
    let m = i + (j - i) / 2
    let l, r = ofSortedArray data i (m - 1), ofSortedArray data (m + 1) j
    let k, v = data.[m]
    MapNode(k, v, l, r, 1 + (max (height l) (height r)))

這仍然遠不如可變哈希表有效,但我得到以下信息:

// Immutable tree-based F# map, using sorted array 
let arr = [| for i in 0 .. 10000000 -> i, i |] // ~1 sec
let map = ofSortedArray arr 0 10000000         // ~3 sec

如果您真的想使用它,則需要您自己的 F# map 版本 - 或者您可以向 F# 核心庫發送拉取請求,添加對此類內容的支持!

暫無
暫無

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

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