Допустим, у меня есть несколько очень больших векторов. Они хранятся на диске. Мне нужно получить к ним доступ по отдельности, читая из каждого соответствующего файла, который поместит их в память. Я бы выполнил некоторую функцию на одном векторе, а затем перешел бы к следующему, к которому мне нужен доступ. Мне нужно иметь возможность указать каждому вектору в памяти собирать мусор каждый раз, когда мне нужно получить доступ к другому вектору. Я не уверен, что performMajorGC
гарантирует, что вектор будет удален сборщиком мусора, если в моей программе указано, что я должен снова получить доступ к тому же вектору позже, ссылаясь на то же имя функции, которое считывает вектор с диска. В таком случае я бы снова прочитал его в память, использовал его, а затем собирал мусор. Как я могу убедиться, что это гаражная коллекция, используя то же имя функции для вектора, который читается из того же файла?
Буду признателен за любой совет спасибо
В ответ Даниэлю Вагнеру:
myvec x :: Int -> IO (Vector (Vector ByteString))
myvec x = do let ioy = do y <- Data.ByteString.Lazy.readFile ("data.csv" ++ (show x))
guard (isRight (Data.Csv.decode NoHeader y))
return y
yy <- ioy
return (head $ snd $ partitionEithers [Data.Csv.decode NoHeader yy])
myvecvec :: Vector (IO (Vector (Vector ByteString)))
myvecvec = generate 100 (\x -> myvec x)
somefunc1 :: IO (Vector (Vector ByteString)) -> IO ()
somefunc1 iovv = do vv <- iovv
somefunc1x1 vv :: Vector (Vector ByteString) -> IO ()
-- то же самое для somefunc2 и 3
oponvec :: IO ()
oponvec = do somefunc1 (myvecvec ! 0)
performGC
somefunc2 (myvecvec ! 1)
performGC
somefunc3 (myvecvec ! 0)
@DanielWagner Итак, будет ли код, который я написал в своем редактировании, эффективно приводить к сборке мусора каждого вектора, как я предполагал, между операциями? Мне даже нужно использовать PerformGC? Могу ли я полагаться на сборщик мусора для сбора каждого вектора, как я описал, без явного использования PerformGC?
...да, ничего существенного не сохраняется от одной строки oponvec
к другой. Но ничего себе, этот код можно значительно улучшить. Почему ioy
заслуживает названия (в отличие от myvec x = do { y <- readFile ("data.csv" ++ show x); case decode NoHeader y of { Left err -> die (show err); Right v -> return v }}
? Почему вообще существуют myvecvec
и somefunc1
(в отличие от oponvec = do { myvec 0 >>= somefunc1x1; myvec 1 >>= somefunc1x1; myvec 0 >>= somefunc1x1 }
)?
@DanielWagner Извините, это было очень небрежно с моей стороны
Вы можете проверить это, используя слабый указатель следующим образом:
import qualified Data.Vector.Unboxed as V
import System.Mem.Weak
import System.Mem
main :: IO ()
main = do
let xs = V.fromList [1..1000000:: Int]
wkp <- mkWeakPtr xs Nothing
performGC
xs' <- deRefWeak wkp
print xs'
В моей системе это печатает Nothing
, что означает, что вектор был освобожден. Однако я не знаю, гарантирует ли GHC, что это произойдет.
Вот программа, которая проверяет предложение @amalloy:
import qualified Data.Vector.Unboxed as V
import Control.Monad
import Data.Word
{-# NOINLINE newLarge #-}
newLarge :: Word8 -> V.Vector Word8
newLarge n = V.replicate 5000000000 n -- 5GB
main :: IO ()
main = forM_ [1..10] $ \i -> print (V.sum (newLarge i))
Это использует ровно 5 ГБ на моей машине, что показывает, что никогда не бывает двух больших векторов, выделенных одновременно.
I need to be able to instruct each vector in memory to be garbage collected every time I need to access a different vector.
Ты? Почему? Если это просто потому, что они большие, и вы беспокоитесь о том, чтобы разместить вектор в памяти, то не беспокойтесь об этом. Если требуется место в памяти, а объект недоступен, сборщик мусора подберет его. Если память не нужна, ничего делать не нужно. И если объект достижим, запуск GC не поможет. Так что нет случаев, когда ручное вмешательство в сборщик мусора принесет пользу.
И если вы хотите собрать его по какой-либо другой причине, кроме освобождения памяти, вам нужно объяснить это в вопросе, потому что эта цель наверняка повлияет на ответы.
Пожалуйста, смотрите мой комментарий Даниэлю Вагнеру и мою правку в ответ на его вопрос. Я использую раздел подкачки очень часто, поэтому отключение подкачки для программ, которые превышают мою память, чтобы заставить GC собирать без использования подкачки, на самом деле не является для меня вариантом, насколько я смог это сделать. Параметр RTS -M(<size>) не работает для предотвращения доступа к подкачке.
-1 от меня: это комментарий, а не ответ. И я не согласен, это звучит как сценарий, в котором мог важно детерминистически GC массива перед выделением другого. Стандартный сборщик мусора не работает мгновенно, и вполне возможно выделить гораздо больше памяти, чем на самом деле требуется в любой момент времени.
Предположительно, чтение в память — это
IO
действие. Ссылка на действиеIO
не содержит ссылку на результат, полученный этим действием. Так что почти навернякаperformMajorGC
достаточно. Но чтобы быть действительно уверенным, нам нужно увидеть код.