[英]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.