我有三个这样定义的数据结构,其中S
,LL
,M
和Object
代表Set
,ListLike
,Map
和ByteString
分别为:
nouns :: IO [Object]
nouns = liftM LL.words $ B.readFile "nounlist.txt"
obj :: IO ObjectSet
obj = liftM S.fromList nouns
actions :: IO ActionMap
actions = do
n <- nouns
let l = foldl' (\z x -> (x,Sell):(x,Create):z) [] n
return $ M.fromList $
(\(x,y) -> ((x, Verb y []), Out (Verb y []) x)) <$> l
现在我有一个函数将未评估的Set和Map绑定到变量a
和o
。一旦进入query
,就会通过用户输入接受无限循环的查询并进行处理。通过查找生成适当的响应。
process :: IO ()
process = do
a <- actions
o <- obj
forever $ query "" a o
请记住,我的Map由300,000多个键值对组成:在我的计算机上调用第一个查询时,第一次评估的初始时间开销在大约3-5秒之间;这很好,完全可以预料到。其他所有后续电话都是快速响应的,就像我想要的那样。但是,这只是因为我将此代码作为独立的可执行文件运行,并且可以保留在IO ()
process
内。如果我将这段代码(以及未列出的其余代码)转换成一个库来与...说明一个 Snap Framework Web应用程序,我就不一定会有这种奢侈。基本上我想说的是:如果我要从forever
删除process
,那么评估的Map和Set肯定会被垃圾收集。实际上,当我从Snap应用程序调用函数时会发生这种情况(我不能保留forever
,因为它会阻止Snap应用程序)。来自Snap应用程序的每个后续调用都将具有相同的3-5秒开销,因为它会重新评估相关数据结构。
是否有一种简单的方法可以将Map和Set保存在内存中,以便每次后续查找都很快?我想出的一个想法是运行一个线程来休眠并维护Map和Set的存储。然而,这对我来说似乎有点矫枉过正。我在俯瞰什么?感谢您对我冗长的解释感到满意。
注意:我不一定要寻找代码答案,更多的是建议,建议等。
答案 0 :(得分:1)
我相信TVar
可以做到。
import Control.Concurrent.Concurrent
intial=do
objects <- newTVarIO Nothing
--I didn't understand your example code well, bear with me (and fix this.)
queryMachine <- mkQueryMachine objects
return queryMachine
queryMachine objects=QueryMachine $ do
objects''' <- atomically $ do
objects' <- readTVar objects
case objects' of
Nothing -> do
let objects'' = objectsMaker
writeTVar objects $ Just objects''
return objects''
Just objects'' -> return objects''
profitFrom objects'''
适应您自己的需要。
<强>解释强>
TVar是STM monad中的一个可变变量。 STM非常安全。 atomically
将其转换为IO操作。对于您正在执行此操作的多种类型的操作,请创建单独的STM操作,并在每个操作上调用atomically
。这是因为你希望atomically
块很小,所以他们所采取的锁定不会占用过多的锁定#34; (等等。)上述代码可以通过比较来自Control.Concurrent.STM.TSem的TSem来改进,这将确保只有一个线程会尝试计算它,以防两个请求同时进入。
答案 1 :(得分:1)
以下是我对IORef
所做的想法:
import Data.IORef
import System.IO.Unsafe
import Control.Monad
val_ :: IORef (Maybe Integer)
val_ = unsafePerformIO $ newIORef Nothing
val :: IO Integer
val = do
v <- readIORef val_
case v of
Just v' -> return v'
Nothing -> do
v' <- readFile "large.txt"
-- replace this part with your actual computation
let l = sum $ map (fromIntegral . fromEnum) v'
writeIORef val_ $ Just l
return l
main = do
writeFile "large.txt" (replicate (10^7) '0')
putStrLn "reading"
replicateM_ 10 (val >>= print)
您确保只评估一次耗时的操作。当您第一次执行val
时,它会将值写入IORef
并在以后每次从那里检索它。当我跑main
时,第一次打印数字需要几秒钟,之后根本没有时间打印。
您需要unsafePerformIO
,因为IORef x
无法进行垃圾回收,但会IO (IORef x)
。
请注意,写入IORef
不评估任何内容,即使您之前致电val
,也会在第一次使用时对其进行评估。
更简单的解决方案可能是使用monad变换器。您没有提供一个示例,说明您的快照程序将在何处使用此表,因此我无法给出一个令人满意的示例。