我一直在努力将LLRBT的C#实现移植到F#,现在我正确地运行它。我的问题是如何优化这个?
我有一些想法
可以找到完整的来源here。 C#代码取自Delay's Blog。
当前表现
F#Elapsed = 00:00:01.1379927身高:26,伯爵:487837
C#Elapsed = 00:00:00.7975849身高:26,伯爵:487837
module Erik
let Black = true
let Red = false
[<AllowNullLiteralAttribute>]
type Node(_key, _value, _left:Node, _right:Node, _color:bool) =
let mutable key = _key
let mutable value = _value
let mutable left = _left
let mutable right = _right
let mutable color = _color
let mutable siblings = 0
member this.Key with get() = key and set(x) = key <- x
member this.Value with get() = value and set(x) = value <- x
member this.Left with get() = left and set(x) = left <- x
member this.Right with get() = right and set(x) = right <- x
member this.Color with get() = color and set(x) = color <- x
member this.Siblings with get() = siblings and set(x) = siblings <- x
static member inline IsRed(node : Node) =
if node = null then
// "Virtual" leaf nodes are always black
false
else
node.Color = Red
static member inline Flip(node : Node) =
node.Color <- not node.Color
node.Right.Color <- not node.Right.Color
node.Left.Color <- not node.Left.Color
static member inline RotateLeft(node : Node) =
let x = node.Right
node.Right <- x.Left
x.Left <- node
x.Color <- node.Color
node.Color <- Red
x
static member inline RotateRight(node : Node) =
let x = node.Left
node.Left <- x.Right
x.Right <- node
x.Color <- node.Color
node.Color <- Red
x
static member inline MoveRedLeft(_node : Node) =
let mutable node = _node
Node.Flip(node)
if Node.IsRed(node.Right.Left) then
node.Right <- Node.RotateRight(node.Right)
node <- Node.RotateLeft(node)
Node.Flip(node)
if Node.IsRed(node.Right.Right) then
node.Right <- Node.RotateLeft(node.Right)
node
static member inline MoveRedRight(_node : Node) =
let mutable node = _node
Node.Flip(node)
if Node.IsRed(node.Left.Left) then
node <- Node.RotateRight(node)
Node.Flip(node)
node
static member DeleteMinimum(_node : Node) =
let mutable node = _node
if node.Left = null then
null
else
if not(Node.IsRed(node.Left)) && not(Node.IsRed(node.Left.Left)) then
node <- Node.MoveRedLeft(node)
node.Left <- Node.DeleteMinimum(node)
Node.FixUp(node)
static member FixUp(_node : Node) =
let mutable node = _node
if Node.IsRed(node.Right) then
node <- Node.RotateLeft(node)
if Node.IsRed(node.Left) && Node.IsRed(node.Left.Left) then
node <- Node.RotateRight(node)
if Node.IsRed(node.Left) && Node.IsRed(node.Right) then
Node.Flip(node)
if node.Left <> null && Node.IsRed(node.Left.Right) && not(Node.IsRed(node.Left.Left)) then
node.Left <- Node.RotateLeft(node.Left)
if Node.IsRed(node.Left) then
node <- Node.RotateRight(node)
node
type LeftLeaningRedBlackTree(?isMultiDictionary) =
let mutable root = null
let mutable count = 0
member this.IsMultiDictionary =
Option.isSome isMultiDictionary
member this.KeyAndValueComparison(leftKey, leftValue, rightKey, rightValue) =
let comparison = leftKey - rightKey
if comparison = 0 && this.IsMultiDictionary then
leftValue - rightValue
else
comparison
member this.Add(key, value) =
root <- this.add(root, key, value)
member private this.add(_node : Node, key, value) =
let mutable node = _node
if node = null then
count <- count + 1
new Node(key, value, null, null, Red)
else
if Node.IsRed(node.Left) && Node.IsRed(node.Right) then
Node.Flip(node)
let comparison = this.KeyAndValueComparison(key, value, node.Key, node.Value)
if comparison < 0 then
node.Left <- this.add(node.Left, key, value)
elif comparison > 0 then
node.Right <- this.add(node.Right, key, value)
else
if this.IsMultiDictionary then
node.Siblings <- node.Siblings + 1
count <- count + 1
else
node.Value <- value
if Node.IsRed(node.Right) then
node <- Node.RotateLeft(node)
if Node.IsRed(node.Left) && Node.IsRed(node.Left.Left) then
node <- Node.RotateRight(node)
node
答案 0 :(得分:4)
我很惊讶有这样的表演差异,因为这看起来像一个直截了当的音译。我认为两者都是在“发布”模式下编译的?您是单独运行(冷启动),还是同一程序中的两个版本都反转了两个版本(例如暖缓存)?完成任何剖析(有一个好的剖析器)?比较内存消耗(甚至fsi.exe可以帮助解决这个问题)?
(我认为这种可变数据结构实现没有任何明显的改进。)
答案 1 :(得分:3)
我写了一个不可变版本,它的表现比上面的可变版本好。到目前为止我只实现了插入。我还在试图弄清楚性能问题是什么。
type ILLRBT =
| Red of ILLRBT * int * ILLRBT
| Black of ILLRBT * int * ILLRBT
| Nil
let flip node =
let inline flip node =
match node with
| Red(l, v, r) -> Black(l, v, r)
| Black(l, v, r) -> Red(l, v, r)
| Nil -> Nil
match node with
| Red(l, v, r) -> Black(flip l, v, flip r)
| Black(l, v, r) -> Red(flip l, v, flip r)
| Nil -> Nil
let lRot = function
| Red(l, v, Red(l', v', r'))
| Red(l, v, Black(l', v', r')) -> Red(Red(l, v, l'), v', r')
| Black(l, v, Red(l', v', r'))
| Black(l, v, Black(l', v', r')) -> Black(Red(l, v, l'), v', r')
| _ -> Nil // could raise an error here
let rRot = function
| Red( Red(l', v', r'), v, r)
| Red(Black(l', v', r'), v, r) -> Red(l', v', Red(r', v, r))
| Black( Red(l', v', r'), v, r)
| Black(Black(l', v', r'), v, r) -> Black(l', v', Red(r', v, r))
| _ -> Nil // could raise an error here
let rec insert node value =
match node with
| Nil -> Red(Nil, value, Nil)
| n ->
n
|> function
| Red(Red(_), v, Red(_))
| Black(Red(_), v, Red(_)) as node -> flip node
| x -> x
|> function
| Red(l, v, r) when value < v -> Red(insert l value, v, r)
| Black(l, v, r) when value < v -> Black(insert l value, v, r)
| Red(l, v, r) when value > v -> Red(l, v, insert r value)
| Black(l, v, r) when value > v -> Black(l, v, insert r value)
| x -> x
|> function
| Red(l, v, Red(_))
| Black(l, v, Red(_)) as node -> lRot node
| x -> x
|> function
| Red(Red(Red(_),_,_), v, r)
| Black(Red(Red(_),_,_), v, r) as node -> rRot node
| x -> x
let rec iter node =
seq {
match node with
| Red(l, v, r)
| Black(l, v, r) ->
yield! iter l
yield v
yield! iter r
| Nil -> ()
}
答案 2 :(得分:2)
如果您愿意考虑不可变的实现,您可能希望在功能设置here中查看Chris Okasaki关于红黑树的论文。
答案 3 :(得分:1)
我的问题是如何优化这个?
在可变的情况下,您应该能够通过使用Node
结构数组而不是堆分配每个Node
来获得更好的性能。在不可变的情况下,您可以尝试将红色节点转换为结构。