“网格”单位的更改



“网格”图形包的主要缺点之一是速度慢。这使得一些依赖于“网格”的重要包(如“ggplot2”)变慢。例如,下面显示的散点图大致相同,但一个使用“图形”绘制,另一个使用“ggplot2”绘制。

“ggplot2”版本绘制所需时间是前者的 4 倍以上。

(本文中的基准是使用“bench”包在基于 rocker/r-devel 的 Docker 容器中生成的,但使用 --enable-memory-profiling 构建的 R-devel (r77995) 和安装了许多 R 包;Docker 映像 pmur002/grid-new-units-r-3.6.3pmur002/grid-new-units-r-devel 可从 DockerHub 获得。)

Thomas Lin Pedersen 发现,在典型的“ggplot2”用法中,大量时间用于创建和操作“网格”“单位”对象,这导致 R 4.0.0 版本的“网格”图形包中“单位”的内部实现发生变化。

一方面,这不是新闻,因为“网格”单位的公开行为根本没有改变。但是,此更改有两个重要后果:一个针对用户,另一个针对开发者。

对于用户来说,进行更改的原因是速度;使用新的“网格”单位,某些操作会快很多(在许多情况下快一个数量级或更多)。例如,以下仅创建单位对象的代码使用新的单位实现后速度提高了十倍。

library(grid)

simpleUnit <- function() {
    unit(1:100, c('mm'))
}
stdUnit <- function() {
    unit(1:100, c('mm', 'inches'))
}

尽管操作单位只是“ggplot2”等包所做工作的一小部分,但单位速度提升的影响足以在“ggplot2”绘图的生成中显现出来。以下绘图包括简单和复杂“ggplot2”绘图的示例。

以下计时显示,“网格”中的新单位实现可以使“ggplot2”绘图的速度提高 10%-20%。

对于开发者来说,“网格”单位更改的影响应该是中性的,但如果某个包一直在窥探和戳“网格”单位的内部实现,则可能会带来灾难性后果。

我们相信已经确定了大多数此类情况,并且现在已经修复了其中大多数情况。如果某些问题尚未发现,以下已知问题和解决方案可能会有所帮助

  • 一个软件包有可能包含一个已保存的 R 对象,其中包含旧式“网格”单位。新“网格”实现中提供了保护,以将此类对象升级为新式单位,或在最坏的情况下生成错误。重新创建已保存的 R 对象有望解决任何问题。

  • 多个软件包从“单位”对象中提取属性,例如,从 unit(1, "mm") 中提取“mm”;有一个新的 grid::unitType() 函数,它可能有助于软件包避免将来访问“网格”单位内部。

前面还有一个小的谎言:“网格”单位的公开行为实际上已经发生了一点变化,因为现在某些单位的打印方式有所不同。例如,以下代码和输出显示,对单位进行算术运算会产生不同的打印结果。

Loading required package: grDevices
> getRversion()
[1] <e2><80><98>3.6.3<e2><80><99>
> library(grid)
> unit(1, "npc") - unit(1, "cm")
[1] 1npc-1cm
> 
Loading required package: grDevices
> getRversion()
[1] <e2><80><98>4.0.0<e2><80><99>
> library(grid)
> unit(1, "npc") - unit(1, "cm")
[1] sum(1npc, -1cm)
> 

新单位的原始设计和实现由 Thomas Lin Pedersen 贡献。Paul Murrell 贡献了次要修复和功能,并领导了软件包中问题的测试、诊断和补救。Paul Murrell 的贡献部分得到了 R Studio 对奥克兰大学基金会的捐赠支持。两位作者都感谢 CRAN 团队的耐心和支持,以及受这些更改影响的众多软件包作者的合作。