- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我试着做一些引导并计算 colMeans
,自然选择了矩阵来存储数据,但是采样很慢:
m[sample(n,replace=TRUE),]
data.table
是最快的。
require(microbenchmark)
require(data.table)
n = 2000
nc = 8000
m = matrix(1:(n*nc) ,nrow = n)
DF = as.data.frame(m)
DT = as.data.table(m)
s=sample(n, replace=TRUE)
microbenchmark(m[s,], DF[s,],DT[s,])
# Unit: milliseconds
# expr min lq mean median uq max neval
# m[s, ] 371.9271 402.3542 421.7907 420.8446 437.8251 506.1788 100
# DF[s, ] 182.3189 199.0865 218.0746 213.9451 231.1518 409.8625 100
# DT[s, ] 129.8225 139.1977 156.9506 150.4321 164.3104 254.2048 100
最佳答案
乍一看,两种可能性都出现在 R 的函数中 MatrixSubset on line 265 .
可能两者都不是。只是猜测。
1. 它似乎在缓存效率低下的方向上循环。
for (i = 0; i < nrs; i++) { // rows
...
for (j = 0; j < ncs; j++) { // columns
...
您的示例有很多列(8,000)。每次内循环获取一个新列时,它都需要将保存该值的 RAM 页从 RAM 中提取到缓存中(很可能是 L2)。下一次提取是不同的列,因此不太可能重用 L2 中已经存在的页面。一个
matrix
在内部是一个巨大的连续向量:所有第 1 列,然后是所有第 2 列,依此类推。页面获取相对昂贵。走向“错误”的方向会导致太多的页面提取。更多关于 CPU 缓存
here .
gcc -floop-interchange
这是默认开启的。更多
here .由于 for 循环内部的复杂性,这种优化可能不会在这种情况下发生;也许在这种情况下是 switch 语句。或者,您在操作系统上使用的 R 版本可能不是用带有该选项的编译器编译的,或者没有打开。
matrix
中的每个项目上都发生了打开类型。 .即使
matrix
是单一类型!所以这是浪费。即使开关是
optimized with a jump table对于矩阵中的每个项目,该跳转表可能仍在发生(“可能”是因为 CPU 可能会预测切换)。由于您的示例
matrix
61MB 很小,我更倾向于这是罪魁祸首,而不是走向错误的方向。
// Check the row numbers once up front rather than 8,000 times.
// This is a contiguous sweep and therefore almost instant
// Declare variables i and ii locally for safety and maximum compiler optimizations
for (int i = 0; i < nrs; i++) {
int ii = INTEGER(sr)[i];
if (ii != NA_INTEGER && (ii < 1 || ii > nr))
errorcall(call, R_MSG_subs_o_b);
}
// Check the column numbers up front once rather than 2,000 times
for (int j = 0; j < ncs; j++) {
int jj = INTEGER(sc)[j];
if (jj != NA_INTEGER && (jj < 1 || jj > nc))
errorcall(call, R_MSG_subs_o_b);
}
// Now switch once on type rather than 8,000 * 2,000 times
// Loop column-by-column not row-by-row
int resi=0; // contiguous write to result (for page efficiency)
int ii, jj; // the current row and column, bounds checked above
switch (TYPEOF(x)) {
case LGLSXP: // the INTSXP will work for LGLSXP too, currently
case INTSXP:
for (int j=0; j<ncs; j++) { // column-by-column
jj = INTEGER(sc)[j];
for (int i=0; i<nrs; i++) { // within-this-column
ii = INTEGER(sr)[i];
INTEGER(result)[resi++] = (ii == NA_INTEGER || jj == NA_INTEGER) ? NA_INTEGER : INTEGER(x)[ii + jj * nr];
}
}
break;
case REALSXP:
for (int j=0; j<ncs; j++) {
jj = INTEGER(sc)[j];
for (int i=0; i<nrs; i++) {
ii = INTEGER(sr)[i];
REAL(result)[resi++] = (ii == NA_INTEGER || jj == NA_INTEGER) ? NA_REAL : REAL(x)[ii + jj * nr];
}
}
break;
case ...
如您所见,这种方式有更多的代码,因为相同的
for
循环必须在
switch()
中一遍又一遍地重复案件。代码可读性和健壮性的原因可能是原始代码为何如此:在 R 的实现中出现拼写错误的可能性较小。这已经证明了,因为我懒惰地没有专门为 LOGICAL 实现 LGLSXP 案例。我知道 LOGICAL 与当前在基础 R 中的 INTEGER 完全相同。但这可能会在 future 发生变化,所以如果 LOGICAL 确实发生变化(比如
char
而不是
int
用于 RAM 效率)。
memcpy
使用单个双循环完成。与类型的大小。
SET_STRING_ELT
和
SET_VECTOR_ELT
仍然必须用于维护这些对象的引用计数。所以这应该只是双
for
的 3 次重复循环来维护。或者,该习语可以包装成
#define
这是在 R 的其他部分完成的。
(ii == NA_INTEGER || jj == NA_INTEGER) ? :
)(对该分支的 2000 * 8000 次调用)。但是以更复杂的重复代码为代价。然而,也许
branch prediction会在所有架构上可靠地启动,我们不应该担心这一点。
data.table
memcpy
在某些但不是所有地方的技巧和深度分支保存。它还开始逐列并行地进行子集化。但在这种情况下还不是因为它是新的并且仍在推出(
setkey
非常相似并且已经是平行的)。主线程处理
character
和
list
虽然从
SET_STRING_ELT
开始一一列(不是并行的)和
SET_VECTOR_ELT
在 R 中不是线程安全的。其他线程并行处理所有整数、实数、复数和原始列。然后它会以内存 io 的速度运行。
n = 2000
nc = 8000 # same size as your example (61MB), on my laptop
microbenchmark(m[s,], DF[s,],DT[s,])
Unit: milliseconds
expr min lq mean median uq max neval
m[s, ] 108.75182 112.11678 118.60111 114.58090 120.07952 168.6079 100
DF[s, ] 100.95019 105.88253 116.04507 110.84693 118.08092 163.9666 100
DT[s, ] 63.78959 69.07341 80.72039 72.69873 96.51802 136.2016 100
n = 2000
nc = 80000 # 10x bigger (610MB)
microbenchmark(m[s,], DF[s,],DT[s,])
Unit: milliseconds
expr min lq mean median uq max neval
m[s, ] 1990.3343 2010.1759 2055.9847 2032.9506 2057.2498 2733.278 100
DF[s, ] 1083.0373 1212.6633 1265.5346 1234.1558 1300.7502 2105.177 100
DT[s, ] 698.1295 830.3428 865.5918 862.5773 907.7225 1053.393 100
不过,我有 128MB 的 L4 缓存。我猜你的缓存较少。整个 61MB 都适合我的 L4 缓存,所以我并没有真正注意到这个大小的缓存效率低下。
$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 8
On-line CPU(s) list: 0-7
Thread(s) per core: 2
Core(s) per socket: 4
Socket(s): 1
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 70
Model name: Intel(R) Core(TM) i7-4980HQ CPU @ 2.80GHz
Stepping: 1
CPU MHz: 3345.343
CPU max MHz: 4000.0000
CPU min MHz: 800.0000
BogoMIPS: 5587.63
Virtualization: VT-x
L1d cache: 32K
L1i cache: 32K
L2 cache: 256K
L3 cache: 6144K
L4 cache: 131072K
NUMA node0 CPU(s): 0-7
关于r - 为什么采样矩阵行很慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42617883/
猫f1.txt阿曼维沙尔阿杰贾伊维杰拉胡尔曼尼什肖比特批评塔夫林现在输出应该符合上面给定的条件 最佳答案 您可以在文件读取循环中设置一个计数器并打印它, 计数=0 读取行时做 让我们数一数++ if
我正在尝试查找文件 1 和文件 2 中的共同行。如果公共(public)行存在,我想写入文件 2 中的行,否则打印文件 1 中的非公共(public)行。fin1 和 fin2 是这里的文件句柄。它读
我有这个 SQL 脚本: CREATE TABLE `table_1` ( `IDTable_1` int(11) NOT NULL, PRIMARY KEY (`IDTable_1`) );
我有 512 行要插入到数据库中。我想知道提交多个插入内容是否比提交一个大插入内容有任何优势。例如 1x 512 行插入 -- INSERT INTO mydb.mytable (id, phonen
如何从用户中选择user_id,SUB(row, row - 1),其中user_id=@userid我的表用户,id 为 1、3、4、10、11、23...(不是++) --id---------u
我曾尝试四处寻找解决此问题的最佳方法,但我找不到此类问题的任何先前示例。 我正在构建一个基于超本地化的互联网购物中心,该区域分为大约 3000 个区域。每个区域包含大约 300 个项目。它们是相似的项
preg_match('|phpVersion = (.*)\n|',$wampConfFileContents,$result); $phpVersion = str_replace('"','',
我正在尝试创建一个正则表达式,使用“搜索并替换全部”删除 200 个 txt 文件的第一行和最后 10 行 我尝试 (\s*^(\h*\S.*)){10} 删除包含的前 10 行空白,但效果不佳。 最
下面的代码从数据库中获取我需要的信息,但没有打印出所有信息。首先,我知道它从表中获取了所有正确的信息,因为我已经在 sql Developer 中尝试过查询。 public static void m
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我试图在两个表中插入记录,但出现异常。您能帮我解决这个问题吗? 首先我尝试了下面的代码。 await _testRepository.InsertAsync(test); await _xyzRepo
这个基本的 bootstrap CSS 显示 1 行 4 列: Text Text Text
如果我想从表中检索前 10 行,我将使用以下代码: SELECT * FROM Persons LIMIT 10 我想知道的是如何检索前 10 个结果之后的 10 个结果。 如果我在下面执行这段代码,
今天我开始使用 JexcelApi 并遇到了这个:当您尝试从特定位置获取元素时,不是像您通常期望的那样使用sheet.getCell(row,col),而是使用sheet.getCell(col,ro
我正在尝试在我的网站上开发一个用户个人资料系统,其中包含用户之前发布的 3 个帖子。我可以让它选择前 3 条记录,但它只会显示其中一条。我是不是因为凌晨 2 点就想编码而变得愚蠢? query($q)
我在互联网上寻找答案,但找不到任何答案。 (我可能问错了?)我有一个看起来像这样的表: 我一直在使用查询: SELECT title, date, SUM(money) FROM payments W
我有以下查询,我想从数据库中获取 100 个项目,但 host_id 多次出现在 urls 表中,我想每个 host_id 从该表中最多获取 10 个唯一行。 select * from urls j
我的数据库表中有超过 500 行具有特定日期。 查询特定日期的行。 select * from msgtable where cdate='18/07/2012' 这将返回 500 行。 如何逐行查询
我想使用 sed 从某一行开始打印 n 行、跳过 n 行、打印 n 行等,直到文本文件结束。例如在第 4 行声明,打印 5-9,跳过 10-14,打印 15-19 等 来自文件 1 2 3 4 5 6
我目前正在执行验证过程来检查用户的旧密码,但问题是我无法理解为什么我的查询返回零行,而预期它有 1 行。另一件事是,即使我不将密码文本转换为 md5,哈希密码仍然得到正确的答案,但我不知道为什么会发生
我是一名优秀的程序员,十分优秀!