假设我们的慢速函数有签名f[x, y]。
纯内存方法
如果您对内存中的缓存感到满意,最简单的做法就是使用记忆化:
Clear@fmem
fmem[x_, y_] := fmem[x, y] = f[x, y]
这会在每次调用它时使用它以前从未见过的参数组合为其自身添加一个定义。
文件支持的内存方法
但是,如果您在长时间计算期间内存不足或遭受内核崩溃,您将希望通过某种持久性来支持此缓存。最简单的方法是保留一个正在运行的日志文件:
$runningLogFile = "/some/directory/runningLog.txt";
Clear@flog
flog[x_, y_] := flog[x, y] = f[x, y] /.
v_ :> (PutAppend[Unevaluated[flog[x, y] = v;], $runningLogFile]; v)
If[FileExistsQ[$runningLogFile]
, Get[$runningLogFile]
, Export[$runningLogFile, "", "Text"];
]
flog 与fmem 相同,只是它还在运行日志中写入了一个条目,可用于在以后的会话中恢复缓存的定义。最后一个表达式在找到现有日志文件时重新加载这些定义(如果文件不存在则创建该文件)。
当需要手动干预时,日志文件的文本性质很方便。请注意,浮点数的文本表示会引入不可避免的舍入错误,因此在从日志文件重新加载值后,您可能会得到略有不同的结果。如果这很重要,您可以考虑使用二进制 DumpSave 功能,尽管我会将这种方法的细节留给读者,因为它不太方便保存增量日志。
SQL 方法
如果内存真的很紧,并且您希望避免使用大型内存缓存来为其他计算腾出空间,那么之前的策略可能不合适。在这种情况下,您可以考虑使用 Mathematica 的内置 SQL 数据库将缓存完全存储在外部:
fsql[x_, y_] :=
loadCachedValue[x, y] /. $Failed :> saveCachedValue[x, y, f[x, y]]
我在下面定义loadCachedValue 和saveCachedValue。基本思想是创建一个 SQL 表,其中每行包含一个x、y、f 三元组。每次需要值时都会查询 SQL 表。请注意,这种方法大大比内存中的缓存慢,所以当f 的计算花费的时间比 SQL 访问时间长得多时,它最有意义。 SQL 方法不会遭受影响文本日志文件方法的舍入错误。
下面是 loadCachedValue 和 saveCachedValue 的定义,以及其他一些有用的辅助函数:
Needs["DatabaseLink`"]
$cacheFile = "/some/directory/cache.hsqldb";
openCacheConnection[] :=
$cache = OpenSQLConnection[JDBC["HSQL(Standalone)", $cacheFile]]
closeCacheConnection[] :=
CloseSQLConnection[$cache]
createCache[] :=
SQLExecute[$cache,
"CREATE TABLE cached_values (x float, y float, f float)
ALTER TABLE cached_values ADD CONSTRAINT pk_cached_values PRIMARY KEY (x, y)"
]
saveCachedValue[x_, y_, value_] :=
( SQLExecute[$cache,
"INSERT INTO cached_values (x, y, f) VALUES (?, ?, ?)", {x, y, value}
]
; value
)
loadCachedValue[x_, y_] :=
SQLExecute[$cache,
"SELECT f FROM cached_values WHERE x = ? AND y = ?", {x, y}
] /. {{{v_}} :> v, {} :> $Failed}
replaceCachedValue[x_, y_, value_] :=
SQLExecute[$cache,
"UPDATE cached_values SET f = ? WHERE x = ? AND y = ?", {value, x, y}
]
clearCache[] :=
SQLExecute[$cache,
"DELETE FROM cached_values"
]
showCache[minX_, maxX_, minY_, maxY_] :=
SQLExecute[$cache,
"SELECT *
FROM cached_values
WHERE x BETWEEN ? AND ?
AND y BETWEEN ? AND ?
ORDER BY x, y"
, {minX, maxX, minY, maxY}
, "ShowColumnHeadings" -> True
] // TableForm
此 SQL 代码使用浮点值作为主键。这在 SQL 中通常是一个值得商榷的做法,但在目前的情况下可以正常工作。
在尝试使用任何这些功能之前,您必须调用openCacheConnection[]。完成后您应该致电closeCacheConnection[]。只有一次,您必须调用createCache[] 来初始化SQL 数据库。 replaceCachedValue、clearCache 和 showCache 用于手动干预。