簡體   English   中英

如何在 F# 中構建帶有預取的惰性序列?

[英]how can I build a lazy sequence with some prefetch, in F#?

我有一個需要攝取大量數據(大約 25gb)的應用程序。

以前測試時數據很小,全部加載到ram中,但現在我必須將其更改為stream。

它被分成幾個 mb 的塊,塊通過異步 function 加載,它可以根據幾個因素從磁盤或數據庫中提取塊。

一種方法是像這樣提供塊:

let blockSequence =
    seq {
        for id in blockIds do
            let data = loadDataAsync id |> Async.runSynchronously
            yield Some data
        yield None
    }

由於有時必須從數據庫中提取數據,因此速度可能會很慢,我想添加一些“預取”,以便在使用時間之前獲取塊數據。

我的一個想法是構建一個數據加載器列表:

let loaders =
    blockIds
    |> List.map (fun id -> async { loadDataAsync id })

但是我會處理異步類型和我的返回類型。

另一個想法是將其包裝在一個惰性塊中:

let loaders =
    blockIds
    |> List.map (fun id -> lazy (loadDataAsync id))

所以我有一個統一的類型,但我需要在它們被拉動之前“戳”元素,總的來說這會很亂。

然后我在考慮一個隊列,我可以在其中始終保持 x 元素在它們被消耗之前加載,但在另一個線程上處理加載。 這可以通過有一個線程來檢查隊列中存在多少元素,如果它低於閾值,加載下一個並將其排入隊列。

就像是:

let queue = ConcurrentQueue<DataType>()
let wait  = EventWaitHandle (false)
some thread ->
    for id in blockIds do
        wait.WaitForOne()
        if queue.Length < x then
            queue.Add (load...)
        wait.Reset()
        

main thread ->

    let data = queue.TryDequeue....
    wait.Set()

但我不可能是第一個需要這個的人,所以有沒有人提出一個好的解決方案?


編輯:

我想出了一個解決方案,但我被困在消費部分。

// trades buffer
let private prefetch = 10
let private tradesBuffer = BlockingQueueAgent<TradeData [] option>(prefetch)

// producing the data
let thread = Thread(ThreadStart(fun _ ->
            async {
                for b in timeBlocks do
                    let! data = loadFromCacheAsync (makeFilename instrument interval b)
                    do! tradesBuffer.AsyncAdd (Some data)
                do! tradesBuffer.AsyncAdd (None)
            }
            |> Async.RunSynchronously
        )
    )


// consuming it
PSEUDO CODE THAT CAN'T WORK
seq {
    let rec pullData () =
        match tradesBuffer.Get() with
        | Some data ->
            yield Some data
            pullData ()
            
        | None ->
            yield None
    pullData ()                
}

我怎樣才能使拉動看起來像一個序列?

這可以工作:

    seq {
        let mutable keepDoingIt = true
        while keepDoingIt do
            let data = tradesBuffer.Get ()
            yield data
            if data.IsNone then keepDoingIt <- false
    }

但我試圖避免可變的(主要是作為練習,因為它已經足夠好了)

您可以使用遞歸來避免while循環:

let rec loop () =
    seq {
        let data = tradesBuffer.Get ()
        yield data
        if data.IsSome then
            yield! loop()
    }

暫無
暫無

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

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