以下代码大约需要15秒才能生成10k UUID的向量。我将需要生成1M或更多,我计算这需要15 * 10 * 10/60分钟,或大约25分钟。有没有更快的方法来实现这一目标?
library(uuid)
library(dplyr)
start_time <- Sys.time()
temp <- sapply( seq_along(1:10000), UUIDgenerate )
end_time <- Sys.time()
end_time - start_time
# Time difference of 15.072 secs
基本上,我正在寻找一种能够实现Java所述性能提升的R方法:Performance of Random UUID generation with Java 7 or Java 6
它们应符合RFC 4122,但其他要求是灵活的。
答案 0 :(得分:4)
提供选项use.time
将大大加快流程。它可以设置为TRUE
或FALSE
,以确定UUID是否基于时间。在这两种情况下,它都会比不指定此选项快得多。
对于10k UUID,
library(uuid)
library(dplyr)
start_time <- Sys.time()
temp <- sapply( seq_along(1:10000), function(ign) UUIDgenerate(FALSE) )
end_time <- Sys.time()
end_time - start_time
# 10k: 0.01399994 secs
start_time <- Sys.time()
temp <- sapply( seq_along(1:10000), function(ign) UUIDgenerate(TRUE) )
end_time <- Sys.time()
end_time - start_time
# 10k: 0.01100016 secs
即使缩放到100M,仍然比原来的15秒更快的运行时间。
start_time <- Sys.time()
temp <- sapply( seq_along(1:100000000), function(ign) UUIDgenerate(FALSE) )
end_time <- Sys.time()
end_time - start_time
# 100M: 1.154 secs
start_time <- Sys.time()
temp <- sapply( seq_along(1:100000000), function(ign) UUIDgenerate(TRUE) )
end_time <- Sys.time()
end_time - start_time
# 100M: 3.7586 secs
答案 1 :(得分:4)
提前排行:不,目前无法在不影响唯一性核心前提的情况下加快使用uuid
生成大量UUID的速度。 (使用uuid
,即。)
事实上,您使用use.time=FALSE
的建议会产生严重影响(在Windows上)。见下文。
可以大规模获得更快的性能,而不是uuid
。见下文。
uuid
uuid::UUIDgenerate
的效果应考虑操作系统。更具体地说,是随机性的来源。看看表演是否重要,是的,在哪里:
library(microbenchmark)
microbenchmark(
rf=replicate(1000, uuid::UUIDgenerate(FALSE)),
rt=replicate(1000, uuid::UUIDgenerate(TRUE)),
sf=sapply(1:1000, function(ign) uuid::UUIDgenerate(FALSE)),
st=sapply(1:1000, function(ign) uuid::UUIDgenerate(TRUE))
)
# Unit: milliseconds
# expr min lq mean median uq max neval
# rf 8.675561 9.330877 11.73299 10.14592 11.75467 66.2435 100
# rt 89.446158 90.003196 91.53226 90.94095 91.13806 136.9411 100
# sf 8.570900 9.270524 11.28199 10.22779 12.06993 24.3583 100
# st 89.359366 90.189178 91.73793 90.95426 91.89822 137.4713 100
...所以使用use.time=FALSE
总是更快。 (我添加了sapply
示例,以便与您的答案代码进行比较,以表明replicate
永远不会慢。请在此处使用replicate
,除非您觉得需要某些数字参数原因。)
然而,有一个问题:
R.version[1:3]
# _
# platform x86_64-w64-mingw32
# arch x86_64
# os mingw32
length(unique(replicate(1000, uuid::UUIDgenerate(TRUE))))
# [1] 1000
length(unique(replicate(1000, uuid::UUIDgenerate(FALSE))))
# [1] 20
鉴于每次调用UUID都是唯一的,这是令人不安的,并且是Windows上随机性不足的症状。 (WSL是否为此提供了出路?另一个研究机会......)
Linux上的uuid
为了比较,在非Windows平台上的结果相同:
microbenchmark(
rf=replicate(1000, uuid::UUIDgenerate(FALSE)),
rt=replicate(1000, uuid::UUIDgenerate(TRUE)),
sf=sapply(1:1000, function(ign) uuid::UUIDgenerate(FALSE)),
st=sapply(1:1000, function(ign) uuid::UUIDgenerate(TRUE))
)
# Unit: milliseconds
# expr min lq mean median uq max neval
# rf 20.852227 21.48981 24.90932 22.30334 25.11449 74.20972 100
# rt 9.782106 11.03714 14.15256 12.04848 15.41695 100.83724 100
# sf 20.250873 21.39140 24.67585 22.44717 27.51227 44.43504 100
# st 9.852275 11.15936 13.34731 12.11374 15.03694 27.79595 100
R.version[1:3]
# _
# platform x86_64-pc-linux-gnu
# arch x86_64
# os linux-gnu
length(unique(replicate(1000, uuid::UUIDgenerate(TRUE))))
# [1] 1000
length(unique(replicate(1000, uuid::UUIDgenerate(FALSE))))
# [1] 1000
(我对linux上use.time=FALSE
的使用时间比在windows上长两倍这一事实略显好奇......)
如果您可以访问SQL服务器(几乎可以肯定...请参阅SQLite ...),那么您可以通过使用服务器的UUID生成实现来处理这个规模问题< / em>,意识到存在一些细微差别。
(旁注:有&#34; V4&#34;(完全随机),&#34; V1&#34;(基于时间),&#34; V1mc&#34;(基于时间和包括系统的mac地址)UUID。uuid
如果use.time=FALSE
则给出V4,否则给出V1,编码系统的mac地址。)
Windows上的某些性能比较 (所有时间都以秒为单位):
# n uuid postgres sqlite sqlserver
# 1 100 0 1.23 1.13 0.84
# 2 1000 0.05 1.13 1.21 1.08
# 3 10000 0.47 1.35 1.45 1.17
# 4 100000 5.39 3.10 3.50 2.68
# 5 1000000 63.48 16.61 17.47 16.31
SQL的使用有一些开销,在大规模完成时不需要花费很长时间。
PostgreSQL需要uuid-ossp
扩展,可以
CREATE EXTENSION "uuid-ossp"
安装/可用后,您可以使用以下内容生成n
UUID
n <- 3
pgcon <- DBI::dbConnect(...)
DBI::dbGetQuery(pgcon, sprintf("select uuid_generate_v1mc() as uuid from generate_series(1,%d)", n))
# uuid
# 1 53cd17c6-3c21-11e8-b2bf-7bab2a3c8486
# 2 53cd187a-3c21-11e8-b2bf-dfe12d92673e
# 3 53cd18f2-3c21-11e8-b2bf-d3c64c6ad73f
存在其他UUID功能。 https://www.postgresql.org/docs/9.6/static/uuid-ossp.html
SQLite包含限制能力,但这个hack适用于V4风格的UUID(长度n
):
sqlitecon <- DBI::dbConnect(RSQLite::SQLite(), ":memory:") # or your own
DBI::dbGetQuery(sqlitecon, sprintf("
WITH RECURSIVE cnt(x) as (
select 1 union all select x+1 from cnt limit %d
)
select (hex(randomblob(4))||'-'||hex(randomblob(2))||'-'||hex(randomblob(2))||'-'||hex(randomblob(2))||'-'||hex(randomblob(6))) as uuid
from cnt", n))
# uuid
# 1 EE6B08DA-2991-BF82-55DD-78FEA48ABF43
# 2 C195AAA4-67FC-A1C0-6675-E4C5C74E99E2
# 3 EAC159D6-7986-F42C-C5F5-35764544C105
这需要一点点痛苦才能将其格式化,最好是精确的。如果不遵守这种格式,您可能会发现小的性能改进。)
SQL Server需要临时创建一个表(带有newsequentialid()
),生成一个序列,拉动自动生成的ID,并丢弃该表。有点过分,特别是考虑到使用SQLite的简易性,但是YMMV。 (没有提供代码,也没有增加太多。)
除了执行时间和足够随机性之外,还有各种关于数据库表的讨论(现在未提及),这些数据库表通过使用非连续的UUID来指示性能影响。这与索引页面等有关,超出了本答案的范围。
然而,假设这是真的......假设大约在同一时间插入的行(在时间上相关)通常被组合在一起(直接或子分组),那么保持同一天是一件好事在同一个db索引页中使用UUID键的数据,因此V4(完全随机)UUID可能会降低大型组(和大型表)的DB性能。出于这个原因,我个人更喜欢V1而不是V4。
其他(仍未启用)讨论考虑在UUID中包含可直接跟踪的MAC地址,以轻微违反内部信息。出于这个原因,我个人倾向于V1mc而不是V1。
(但我还没有办法用RSQLite
做得很好,所以我依赖附近的postgresql
。幸运的是,我使用postgresql足够其他我在windows上用docker保存实例的东西。)