在模拟中快速处理规则
Fast handling of rules in a simulation
如果您在离散事件模拟中只有一些规则,这并不重要,但如果您有很多规则并且它们会相互干扰,您可能需要跟踪 "which" 和 "where" 他们被使用了。
- 有人知道如何让下面的代码和原来的函数一样快吗?
- 还有比
eval(parse(...)
更好的选择吗?
这是一个简单的例子,它表明我将速度降低了 100 倍。假设您 运行 一个模拟和一个(许多规则中的)是:Select 时间少于 5 的状态:
> a <- rnorm(100, 50, 10)
> print(summary(microbenchmark::microbenchmark(a[a < 5], times = 1000L, unit = "us")))
expr min lq mean median uq max neval
a[a < 5] 0.76 1.14 1.266745 1.141 1.52 11.404 1000
myfun <- function(a0) {
return(eval(parse(text = myrule)))
}
> myrule <- "a < a0" # The rule could be read from a file.
print(summary(microbenchmark::microbenchmark(a[myfun(5)], times = 1000L, unit = "us")))
expr min lq mean median uq max neval
a[myfun(5)] 137.61 140.271 145.6047 141.411 142.932 343.644 1000
注:我不认为我需要一个额外的rete package可以有效地做簿记。但如果有其他意见,请告诉我...
让我们分析一下:
Rprof()
for (i in 1:1e4) a[myfun(5)]
Rprof(NULL)
summaryRprof()
#$by.self
# self.time self.pct total.time total.pct
#"parse" 0.36 69.23 0.48 92.31
#"structure" 0.04 7.69 0.06 11.54
#"myfun" 0.02 3.85 0.52 100.00
#"eval" 0.02 3.85 0.50 96.15
#"stopifnot" 0.02 3.85 0.06 11.54
#"%in%" 0.02 3.85 0.02 3.85
#"anyNA" 0.02 3.85 0.02 3.85
#"sys.parent" 0.02 3.85 0.02 3.85
#
#$by.total
# total.time total.pct self.time self.pct
#"myfun" 0.52 100.00 0.02 3.85
#"eval" 0.50 96.15 0.02 3.85
#"parse" 0.48 92.31 0.36 69.23
#"srcfilecopy" 0.12 23.08 0.00 0.00
#"structure" 0.06 11.54 0.04 7.69
#"stopifnot" 0.06 11.54 0.02 3.85
#".POSIXct" 0.06 11.54 0.00 0.00
#"Sys.time" 0.06 11.54 0.00 0.00
#"%in%" 0.02 3.85 0.02 3.85
#"anyNA" 0.02 3.85 0.02 3.85
#"sys.parent" 0.02 3.85 0.02 3.85
#"match.call" 0.02 3.85 0.00 0.00
#"sys.function" 0.02 3.85 0.00 0.00
大部分时间都花在了parse
。我们可以用一个基准来证实这一点:
microbenchmark(a[myfun(5)], times = 1000L, unit = "us")
#Unit: microseconds
# expr min lq mean median uq max neval
# a[myfun(5)] 67.347 69.141 72.12806 69.909 70.933 160.303 1000
a0 <- 5
microbenchmark(parse(text = myrule), times = 1000L, unit = "us")
#Unit: microseconds
# expr min lq mean median uq max neval
# parse(text = myrule) 62.483 64.275 64.99432 64.787 65.299 132.903 1000
如果从文件中以文本形式读取规则是一项硬性要求,我认为没有办法加快速度。当然,你不应该重复解析相同的规则,但我现在假设你是这样的。
编辑以回应提供更多解释的评论:
您应该将规则存储为带引号的表达式(例如,如果您需要将它们作为文件存储在使用 saveRDS
的列表中):
myrule1 <- quote(a < a0)
myfun1 <- function(rule, a, a0) {eval(rule)}
microbenchmark(a[myfun1(myrule1, a, 30)], times = 1000L, unit = "us")
#Unit: microseconds
# expr min lq mean median uq max neval
# a[myfun1(myrule1, a, 30)] 1.792 2.049 2.286815 2.304 2.305 30.217 1000
为方便起见,您可以将该表达式列表设为 S3 对象并为其创建一个不错的 print
方法,以便获得更好的概览。
如果您在离散事件模拟中只有一些规则,这并不重要,但如果您有很多规则并且它们会相互干扰,您可能需要跟踪 "which" 和 "where" 他们被使用了。
- 有人知道如何让下面的代码和原来的函数一样快吗?
- 还有比
eval(parse(...)
更好的选择吗?
这是一个简单的例子,它表明我将速度降低了 100 倍。假设您 运行 一个模拟和一个(许多规则中的)是:Select 时间少于 5 的状态:
> a <- rnorm(100, 50, 10)
> print(summary(microbenchmark::microbenchmark(a[a < 5], times = 1000L, unit = "us")))
expr min lq mean median uq max neval
a[a < 5] 0.76 1.14 1.266745 1.141 1.52 11.404 1000
myfun <- function(a0) {
return(eval(parse(text = myrule)))
}
> myrule <- "a < a0" # The rule could be read from a file.
print(summary(microbenchmark::microbenchmark(a[myfun(5)], times = 1000L, unit = "us")))
expr min lq mean median uq max neval
a[myfun(5)] 137.61 140.271 145.6047 141.411 142.932 343.644 1000
注:我不认为我需要一个额外的rete package可以有效地做簿记。但如果有其他意见,请告诉我...
让我们分析一下:
Rprof()
for (i in 1:1e4) a[myfun(5)]
Rprof(NULL)
summaryRprof()
#$by.self
# self.time self.pct total.time total.pct
#"parse" 0.36 69.23 0.48 92.31
#"structure" 0.04 7.69 0.06 11.54
#"myfun" 0.02 3.85 0.52 100.00
#"eval" 0.02 3.85 0.50 96.15
#"stopifnot" 0.02 3.85 0.06 11.54
#"%in%" 0.02 3.85 0.02 3.85
#"anyNA" 0.02 3.85 0.02 3.85
#"sys.parent" 0.02 3.85 0.02 3.85
#
#$by.total
# total.time total.pct self.time self.pct
#"myfun" 0.52 100.00 0.02 3.85
#"eval" 0.50 96.15 0.02 3.85
#"parse" 0.48 92.31 0.36 69.23
#"srcfilecopy" 0.12 23.08 0.00 0.00
#"structure" 0.06 11.54 0.04 7.69
#"stopifnot" 0.06 11.54 0.02 3.85
#".POSIXct" 0.06 11.54 0.00 0.00
#"Sys.time" 0.06 11.54 0.00 0.00
#"%in%" 0.02 3.85 0.02 3.85
#"anyNA" 0.02 3.85 0.02 3.85
#"sys.parent" 0.02 3.85 0.02 3.85
#"match.call" 0.02 3.85 0.00 0.00
#"sys.function" 0.02 3.85 0.00 0.00
大部分时间都花在了parse
。我们可以用一个基准来证实这一点:
microbenchmark(a[myfun(5)], times = 1000L, unit = "us")
#Unit: microseconds
# expr min lq mean median uq max neval
# a[myfun(5)] 67.347 69.141 72.12806 69.909 70.933 160.303 1000
a0 <- 5
microbenchmark(parse(text = myrule), times = 1000L, unit = "us")
#Unit: microseconds
# expr min lq mean median uq max neval
# parse(text = myrule) 62.483 64.275 64.99432 64.787 65.299 132.903 1000
如果从文件中以文本形式读取规则是一项硬性要求,我认为没有办法加快速度。当然,你不应该重复解析相同的规则,但我现在假设你是这样的。
编辑以回应提供更多解释的评论:
您应该将规则存储为带引号的表达式(例如,如果您需要将它们作为文件存储在使用 saveRDS
的列表中):
myrule1 <- quote(a < a0)
myfun1 <- function(rule, a, a0) {eval(rule)}
microbenchmark(a[myfun1(myrule1, a, 30)], times = 1000L, unit = "us")
#Unit: microseconds
# expr min lq mean median uq max neval
# a[myfun1(myrule1, a, 30)] 1.792 2.049 2.286815 2.304 2.305 30.217 1000
为方便起见,您可以将该表达式列表设为 S3 对象并为其创建一个不错的 print
方法,以便获得更好的概览。