F#中模块与类类型的功能体系结构的性能



我知道F#中鼓励使用功能体系结构,但我遇到了类类型所不存在的性能墙。

我有一个状态类型,其中有一堆字段,它通过代码管道传递给一系列函数。在每个状态下,当发生某种转换时,都会创建一个新对象。

例如:

match ChefHelpers.evaluateOpening t brain with
| Some openOrder ->
info $"open order received: {openOrder.Side}"
match! ExchangeBus.submitOneOrderRetryAsync brain.Exchange openOrder with
| Ok _->
{brain.CookOrder.Instrument.PriceToString openOrder.Price} / sl.{stopLossOrder.Side.Letter} at {brain.CookOrder.Instrument.PriceToString stopLossOrder.Price}"
let m = $"{t.Timestamp.Format}: send open order {openOrder.Side}, last price was {brain.CookOrder.Instrument.PriceToString brain.Analysis.LastPrice}"
return Message m, ({ brain.WithStatus (Cooking MonitorForClose) with OpenOrder = Some openOrder })
| Error e ->
let m = $"{t.Timestamp}: couldn't open position:n{e.Describe()}"
return! ChefHelpers.cancelAllOrdersAsync brain (ChefError m) (ExchangeError (Other (e.Describe())))
| None ->
return NoMessage, brain

持有所有状态的物体"大脑"将在哪里传递、更新等

这在实时运行时效果非常好,因为每件事最多每秒执行2-3次。

当我想在静态数据上运行相同的代码来检查行为等时,情况就不同了,因为我在等待它完成的时候要运行它数百万次。所有这些代码都在处理小列表、进行基本的比较、算术等,因此重建主对象的成本非常突出,变得非常明显。

我试图将其中的一些逻辑重建为对象类型,其中状态是一堆可变变量,性能差异很大。

我有很多这样的代码:

type A = { }
let a : A = ...
let a = doSomething1 a
let a = doSomething2 a
let a =    
match x with
| true -> doSomething3 a
| false -> a
etc

我认为整个工具体系结构都是用这样的代码构建的。

其中有很多:

let a = { a with X = 3 }

但是管道中没有并发,而且它是线性的,所以在最后一行的情况下,如果我有办法告诉编译器:它是同一个对象,没有在其他地方使用,在适当的地方编辑它,那么性能会更好。

我可以使用什么策略来保持代码的可读性,但尽量减少这个问题?问题是实际的数据复制吗?主对象有18个字段,所以我无法想象它会大于200字节,为它分配空间?还是会产生大量垃圾收集?

这不是一件简单的事情,因为这是一个无处不在的成本,在网络内部。

任何反馈/想法都会很棒,尤其是";如果你做错了,就改为"X":(

从设计的角度来看,在我看来,18个字段实际上是一个相当大的记录。也许你可以将其纳入子记录中,这样你就不会不断地重新分配整个东西了?因此,取而代之的是:

type A =
{
X : int
Field1 : int
Field2 : int
...
Field18 : int
}

你可以用这个代替:

type Sub1 =
{
Field1 : int
...
Field9 : int
}
type Sub2 =
{
Field10 : int
...
Field18 : int
}
type A =
{
X : int
Sub1 : Sub1
Sub2 : Sub2
}

那么CCD_ 1的性能成本大概会更低。

额外的想法:你可能还想模仿哈斯克尔的孩子们,研究专门为读取和更新部分不可变数据而设计的镜头。

最新更新