2013-08-13 38 views
7

Không chắc chắn liệu tôi có quyền này hay không hoặc liệu có cách nào hay hơn hoặc thư viện hiện có đang giải quyết vấn đề này.Thời gian để ghi nhớ trực tiếp trong F #

Đặc biệt, tôi không chắc liệu CAS có cần hàng rào bộ nhớ hay không ... Tôi nghĩ là không tốt hơn.

Tôi cũng đã thử với một tác nhân và từ điển có thể thay đổi nhưng trực giác của tôi rằng nó sẽ chậm hơn được xác nhận và việc triển khai có liên quan nhiều hơn.

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 
+0

Bạn đã thử nghiệm chưa? Nó có hoạt động không? – Daniel

+0

Dường như hoạt động rất tốt. –

+0

Điều đó có vẻ ổn với tôi. Tôi không nghĩ rằng bạn cần một rào cản bộ nhớ với 'CompareExchange'. Nitpick: phải là 'millis'. Bạn có thể so sánh sự hoàn hảo của 'ConcurrentDictionary'. – Daniel

Trả lời

1

Nếu bạn sẵn sàng để giới hạn những gì để memoize chức năng mà phải mất một đầu vào chuỗi, bạn có thể tái sử dụng các chức năng từ System.Runtime.Caching.

Điều này phải hợp lý như một phần của thư viện chính (bạn hy vọng ...) nhưng giới hạn chuỗi là một điều khá nặng và bạn phải chuẩn mực so với triển khai hiện tại nếu bạn muốn so sánh về hiệu suất.

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) 

Cách sử dụng:

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 

Nếu bạn không bao giờ quan tâm đến việc truy cập vào bộ nhớ cache cơ bản trực tiếp bạn có thể rõ ràng là chỉ trả về một hàm mới với cùng một chữ ký của người già - nhưng với bộ nhớ cache là IDisposable , điều đó dường như không khôn ngoan.

Tôi nghĩ theo nhiều cách tôi thích giải pháp của bạn, nhưng khi tôi đối mặt với một vấn đề tương tự, tôi đã có một suy nghĩ sai lầm rằng tôi thực sự nên sử dụng công cụ tích hợp nếu có thể.

Các vấn đề liên quan