F# F中的生活时间记忆#
不确定我是否正确,或者是否有更好的方法或现有的库已经解决了这个问题 特别是我不确定CAS是否需要内存围栏。。。我想不是,但最好问问 我还尝试使用代理和可变字典,但我的直觉是,它会更慢,这一点得到了证实,而且实现更复杂F# F中的生活时间记忆#,f#,F#,不确定我是否正确,或者是否有更好的方法或现有的库已经解决了这个问题 特别是我不确定CAS是否需要内存围栏。。。我想不是,但最好问问 我还尝试使用代理和可变字典,但我的直觉是,它会更慢,这一点得到了证实,而且实现更复杂 module CAS = open System.Threading let create (value: 'T) = let cell = ref value let get () = !cell let re
module CAS =
open System.Threading
let create (value: 'T) =
let cell = ref value
let get () = !cell
let rec swap f =
let before = get()
let newValue = f before
match Interlocked.CompareExchange<'T>(cell, newValue, before) with
| result when obj.ReferenceEquals(before, result) ->
newValue
| _ ->
swap f
get, swap
module Memoization =
let timeToLive milis f =
let get, swap = CAS.create Map.empty
let evict key =
async {
do! Async.Sleep milis
swap (Map.remove key) |> ignore
} |> Async.Start
fun key ->
let data = get()
match data.TryFind key with
| Some v -> v
| None ->
let v = f key
swap (Map.add key v) |> ignore
evict key
v
模块CAS=
开放系统。线程
让我们创建(值:'T)=
让单元格=参考值
让我们开始吧!细胞
让rec交换f=
let before=get()
让newValue=f在
match Interlocated.CompareeExchange如果您愿意将要记忆的内容限制为采用字符串输入的函数,则可以重用System.Runtime.Caching
中的功能
作为核心库的一部分,这应该是相当健壮的(您希望…),但是字符串限制是非常严重的,如果您想对性能进行比较,您必须对当前的实现进行基准测试
open System
open System.Runtime.Caching
type Cached<'a>(func : string -> 'a, cache : IDisposable) =
member x.Func : string -> 'a = func
interface IDisposable with
member x.Dispose () =
cache.Dispose ()
let cache timespan (func : string -> 'a) =
let cache = new MemoryCache(typeof<'a>.FullName)
let newFunc parameter =
match cache.Get(parameter) with
| null ->
let result = func parameter
let ci = CacheItem(parameter, result :> obj)
let cip = CacheItemPolicy()
cip.AbsoluteExpiration <- DateTimeOffset(DateTime.UtcNow + timespan)
cip.SlidingExpiration <- TimeSpan.Zero
cache.Add(ci, cip) |> ignore
result
| result ->
(result :?> 'a)
new Cached<'a>(newFunc, cache)
let cacheAsync timespan (func : string -> Async<'a>) =
let cache = new MemoryCache(typeof<'a>.FullName)
let newFunc parameter =
match cache.Get(parameter) with
| null ->
async {
let! result = func parameter
let ci = CacheItem(parameter, result :> obj)
let cip = CacheItemPolicy()
cip.AbsoluteExpiration <- DateTimeOffset(DateTime.UtcNow + timespan)
cip.SlidingExpiration <- TimeSpan.Zero
cache.Add(ci, cip) |> ignore
return result
}
| result ->
async { return (result :?> 'a) }
new Cached<Async<'a>>(newFunc, cache)
如果您从未对直接访问底层缓存感兴趣,那么很明显,您可以返回一个新函数,该函数的签名与旧函数的签名相同——但考虑到缓存是IDisposable的,这似乎是不明智的
我想在很多方面我更喜欢你的解决方案,但当我遇到类似的问题时,我有一个不正常的想法,如果可以的话,我真的应该使用内置的东西。你测试过吗?它有效吗?似乎很有效。我觉得很好。我认为您不需要使用compareeexchange
的内存屏障。挑剔:应该是millis
。您可能会比较ConcurrentDictionary
的性能。感谢您的提示,这可能是一个很好的替代方法。我很难找到具有不可变映射的CA比ConcurrentDictionary更高效的场景。在一个特定的测试中,CAS的速度慢了10倍(30秒比3秒),我怀疑这可能是一个bug。将配置文件并返回报告。
let getStuff =
let cached = cacheAsync (TimeSpan(0, 0, 5)) uncachedGetStuff
// deal with the fact that the cache is IDisposable here
// however is appropriate...
cached.Func