请考虑以下数据模型:
data Artist = Artist Text
data Song = Song Artist Text
data Catalogue = Catalogue (Set Artist) (Set Song)
您可以看到Artist
和Song
都引用了Catalogue
。 Catalogue
包含从Song
引用的所有艺术家的列表,因此Artist
的相同值会从两个地方引用。
假设我们使用以下函数的多个应用程序生成Catalogue
值:
insertSong :: Song -> Catalogue -> Catalogue
insertSong song@(Song artist title) (Catalogue artists songs) =
Catalogue (Set.insert artist artists) (Set.insert song songs)
很明显,Catalogue
会被Artist
所引用的Song
的相同值引用填充,从而通过不存储这些值的副本来节省内存
问题在于,当我尝试通过单独反序列化一组艺术家和一组歌曲来从序列化数据重新创建目录时,应用程序占用的内存比生成Catalogue
的相同值时占用更多内存insertSong
。我怀疑它是由Artist
s引用的同一Song
和Catalogue
之间丢失的关系造成的,这就是为什么我得到Artist
占用的值的副本额外的记忆。
我看到的唯一解决方案是先对这组艺术家进行反序列化,然后在强制性地将Artist
的值替换为第一组中的值时反序列化这组歌曲。
所以我的问题是:
答案 0 :(得分:6)
请注意,如果您对字符串进行任何类型的计算,也会丢失共享(即使artist1
和artist2
相同并且共享,f artist1
和{{1可能不是)。如果这成为问题,您也可以对数据结构进行类似的更改。
答案 1 :(得分:3)
一个简单的解决方案似乎是使用有点退化的地图来缓存数据:
{-# LANGUAGE DeriveDataTypeable, RankNTypes #-}
import Control.Monad
import Control.Monad.State
import Data.Map (Map)
import qualified Data.Map as M
type Cache a = Map a a
如果已经存在与此缓存相等的条目,我们可以查询此缓存,并将其替换为缓存的缓存:
cached :: (Ord a) => a -> State (Cache a) a
cached x = state $ \m ->
case M.lookup x m of
Just x' -> (x', m)
Nothing -> (x, M.insert x x m)
这样,如果我们加载几个类型为a
的相等元素,我们将它们转换为单个元素。这可以在反序列化期间完成,也可以在结束时完成。
也许可以进一步概括它并使用SYB通过缓存映射数据结构中某些给定类型的所有值:
import Data.Data (Data)
import Data.Generics.Aliases (mkM)
import Data.Generics.Schemes (everywhereM)
import Data.Typeable (Typeable)
replaceFromCache
:: (Ord a, Typeable a, Data b)
=> b -> State (Cache a) b
replaceFromCache = everywhereM (mkM cached)
然后我们可以替换某些数据结构中的所有艺术家,如
data Artist = Artist String
deriving (Eq, Ord, Typeable)
cacheAllArtists :: (Data b) => b -> b
cacheAllArtists b = evalState (replaceFromCache b) (M.empty :: Cache Artist)
或者我们可以使用Proxy
幻像类型来创建通用版本:
cacheAll :: (Ord a, Typeable a, Data b)
=> Proxy a -> b -> b
cacheAll p = flip evalState (emptyOf p) . replaceFromCache
where
emptyOf p = asTypeOf2 M.empty p
asTypeOf2 :: f a -> Proxy a -> f a
asTypeOf2 = const
cacheAllArtists :: (Data b) => b -> b
cacheAllArtists = cacheAll (Proxy :: Proxy Artist)
(免责声明:我还没有测试过上述任何代码。)
答案 2 :(得分:1)
我偶然发现了一个接近问题的项目。请参阅RefSerialize。