为什么标准 R 中值函数比简单的 C++ 替代函数慢得多?

2023-12-01

我对中位数进行了以下实现C++并将其用于R via Rcpp:

// [[Rcpp::export]]
double median2(std::vector<double> x){
  double median;
  size_t size = x.size();
  sort(x.begin(), x.end());
  if (size  % 2 == 0){
      median = (x[size / 2 - 1] + x[size / 2]) / 2.0;
  }
  else {
      median = x[size / 2];
  }
  return median;
}

如果我随后将性能与标准内置 R 中值函数进行比较,我可以通过以下方式得到以下结果microbenchmark

> x = rnorm(100)
> microbenchmark(median(x),median2(x))
Unit: microseconds
       expr    min     lq     mean median     uq     max neval
  median(x) 25.469 26.990 34.96888 28.130 29.081 518.126   100
 median2(x)  1.140  1.521  2.47486  1.901  2.281  47.897   100

为什么标准中值函数慢得多?这不是我所期望的......


正如@joran 所指出的,您的代码非常专业,一般来说,通用性较低的函数、算法等......通常性能更高。看一眼median.default:

median.default
# function (x, na.rm = FALSE) 
# {
#   if (is.factor(x) || is.data.frame(x)) 
#     stop("need numeric data")
#   if (length(names(x))) 
#     names(x) <- NULL
#   if (na.rm) 
#     x <- x[!is.na(x)]
#   else if (any(is.na(x))) 
#     return(x[FALSE][NA])
#   n <- length(x)
#   if (n == 0L) 
#     return(x[FALSE][NA])
#   half <- (n + 1L)%/%2L
#   if (n%%2L == 1L) 
#     sort(x, partial = half)[half]
#   else mean(sort(x, partial = half + 0L:1L)[half + 0L:1L])
# }

有多种操作可以适应缺失值的可能性,这些操作肯定会影响函数的整体执行时间。由于您的函数不会复制此行为,因此它可以消除大量计算,但因此不会为具有缺失值的向量提供相同的结果:

median(c(1, 2, NA))
#[1] NA

median2(c(1, 2, NA))
#[1] 2

其他几个可能不存在的因素as much的效果作为处理NAs,但值得指出的是:

  • median以及它使用的一些函数都是 S3 泛型,因此在方法分派上花费了少量时间
  • median不仅仅适用于整数和数值向量;它还会处理Date, POSIXt,可能还有一堆其他类,并正确保留属性:

median(Sys.Date() + 0:4)
#[1] "2016-01-15"

median(Sys.time() + (0:4) * 3600 * 24)
#[1] "2016-01-15 11:14:31 EST"

Edit:我应该提到下面的函数会导致原始向量被排序 since NumericVectors 是代理对象。如果你想避免这种情况,你可以Rcpp::clone输入向量并对克隆进行操作,或使用您的原始签名(带有std::vector<double>),这隐含地需要从转换中复制SEXP to std::vector.

另请注意,您可以通过使用NumericVector代替std::vector<double>:

#include <Rcpp.h>

// [[Rcpp::export]]
double cpp_med(Rcpp::NumericVector x){
  std::size_t size = x.size();
  std::sort(x.begin(), x.end());
  if (size  % 2 == 0) return (x[size / 2 - 1] + x[size / 2]) / 2.0;
  return x[size / 2];
}

microbenchmark::microbenchmark(
  median(x),
  median2(x),
  cpp_med(x),
  times = 200L
)
# Unit: microseconds
#       expr    min      lq      mean  median      uq     max neval
#  median(x) 74.787 81.6485 110.09870 92.5665 129.757 293.810   200
# median2(x)  6.474  7.9665  13.90126 11.0570  14.844 151.817   200
# cpp_med(x)  5.737  7.4285  11.25318  9.0270  13.405  52.184   200

Yakk 在上面的评论中提出了一个很好的观点——Jerry Coffin 也对此进行了阐述——关于进行完整排序的低效率。这是使用重写std::nth_element,以更大的向量为基准:

#include <Rcpp.h>

// [[Rcpp::export]]
double cpp_med2(Rcpp::NumericVector xx) {
  Rcpp::NumericVector x = Rcpp::clone(xx);
  std::size_t n = x.size() / 2;
  std::nth_element(x.begin(), x.begin() + n, x.end());

  if (x.size() % 2) return x[n]; 
  return (x[n] + *std::max_element(x.begin(), x.begin() + n)) / 2.;
}

set.seed(123)
xx <- rnorm(10e5)

all.equal(cpp_med2(xx), median(xx))
all.equal(median2(xx), median(xx))

microbenchmark::microbenchmark(
  cpp_med2(xx), median2(xx), 
  median(xx), times = 200L
)
# Unit: milliseconds
#         expr      min       lq     mean   median       uq       max neval
# cpp_med2(xx) 10.89060 11.34894 13.15313 12.72861 13.56161  33.92103   200
#  median2(xx) 84.29518 85.47184 88.57361 86.05363 87.70065 228.07301   200
#   median(xx) 46.18976 48.36627 58.77436 49.31659 53.46830 250.66939   200
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么标准 R 中值函数比简单的 C++ 替代函数慢得多? 的相关文章

  • 是否允许将类模板类型参数键入相同的名称?

    这似乎可以在 MSVC 中按预期编译甚至工作 但它是合法的 C 代码吗 它是否能保证执行此处所期望的操作 即将模板类型导出到结构体的同名用户 template
  • 在Application_AquireRequestState事件中用POST数据重写Url

    我有一个在其中注册路线的代码Application AcquireRequestState应用程序的事件 注册路由后 我会在 Http 运行时缓存中设置一个标志 这样我就不会再次执行路由注册代码 在此事件中注册路线有特定原因Applicat
  • C# 中四舍五入到偶数

    我没有看到 Math Round 的预期结果 return Math Round 99 96535789 2 MidpointRounding ToEven returning 99 97 据我了解 MidpointRounding ToE
  • 如何在ggplot2中使用希腊符号?

    我的类别需要用希腊字母命名 我在用ggplot2 并且它与数据配合得很好 不幸的是 我无法弄清楚如何将这些希腊符号放在 x 轴上 在刻度线处 并使它们出现在图例中 有什么办法可以做到吗 更新 我看了一下link https github c
  • R 的 dplyr 切片中的奇怪行为

    打电话时slice df i 在 R 的 dplyr 包中 如果我要求的行索引不存在 nrows lt i 它似乎返回除组中的第一行之外的所有行 就像我调用的那样slice df 1 例如 library dplyr c1 lt c a b
  • 序列化和反序列化 Visual Studio 解决方案文件 - 或以编程方式编辑?

    我想以编程方式添加和删除项目 解决方案文件夹和其他项目 例如解决方案的资源文件 但我不确定最好的方法是什么 对于那些不知道的人 高度简化 解决方案文件 sln 通常如下所示 Microsoft Visual Studio Solution
  • 手动将 ClientBase 集合类型从 Array[] 更改为 List<>

    我将自己的 WCF 代理与 Client Base 一起使用 我想做一些类似于 svc util 中的 ct 属性的操作 并告诉代理返回 List 集合类型 我不能使用 List 因为实体由 nhibernate 管理 所以我必须使用 IL
  • 无法加载文件或程序集“EntityFramework,版本=6.0.0.0”

    我究竟做错了什么 我该如何解决这个问题 我有一个包含多个项目的解决方案 它是一个 MVC NET 4 5 Web 应用程序 在调试模式下启动后调用其中一个项目时 出现此错误 导致此错误的项目具有以下参考 两个都是版本6 0 0 0 应用程序
  • 防止GDB中的PLT(过程链接表)断点

    在最新版本的 GDB 中 在库函数调用上设置断点会导致多个实际断点 调用过程链接表 PLT 实际的函数调用 这意味着当调用库函数时 我们每次都会经历两次中断 在以前的 GDB 版本中 只会创建 2 因此您只能得到一次中断 那么问题来了 是否
  • 以编程方式更新 ClickOnce 应用程序的部署清单会导致缺少 4.0 中所需的 <兼容框架> 元素

    我正在致力于自动化 NET 4 0 ClickOnce WPF 应用程序的安装程序 该应用程序需要在应用程序配置文件 我经历了寻找必须遵循的具体步骤的棘手过程Mage exe http msdn microsoft com en us li
  • R Shiny - 修复了 Shiny 仪表板中的侧边栏和主标题

    我有一个简化的闪亮仪表板 请参阅下面的代码 我想修复侧边栏和主标题 因此 在其他帖子的帮助下 我编写了一个 CSS 文件来解决该问题 sidebar color FFF position fixed width 220px white sp
  • dropdownlist DataTextField 由属性组成?

    有没有一种方法可以通过 C 使 asp net 中的下拉列表的 datatextfield 属性由对象的多个属性组成 public class MyObject public int Id get set public string Nam
  • 从事务范围调用 WCF 服务方法

    我有这样的代码 using TransactionScope scope TransactionScopeFactory CreateTransactionScope some methodes calls for which scope
  • .NET JIT 编译的代码缓存在哪里?

    NET 程序首先被编译为 MSIL 代码 当它被执行时 JIT编译器会将其编译为本机机器代码 我想知道 这些JIT编译的机器代码存储在哪里 它只存储在进程的地址空间中吗 但由于程序的第二次启动比第一次快得多 我认为即使在执行完成后 该本机代
  • Rglpk - 梦幻足球阵容优化器 - For 循环输出的 Rbind

    我有一个使用 Rgplk 的梦幻足球阵容优化器 它使用for循环生成多个最佳阵容 其数量由用户输入 代码如下 Lineups lt list for i in 1 Lineup no matrix lt rbind as numeric D
  • 从 C 线程调用 Python 代码

    我对从 C 或 C 线程调用 Python 代码时如何确保线程安全感到非常困惑 The Python 文档 http docs python org c api init html non python created threads似乎是
  • 如何将对象转换为传递给函数的类型?

    这不会编译 但我想做的只是将对象转换为传递给函数的 t public void My Func Object input Type t t object ab TypeDescriptor GetConverter t ConvertFro
  • g++ C++0x 枚举类编译器警告

    我一直在将可怕的 C 类型安全伪枚举重构为新的 C 0x 类型安全枚举 因为它们是way更具可读性 不管怎样 我在导出的类中使用它们 所以我明确地将它们标记为导出 enum class attribute visibility defaul
  • (R 错误)错误:cons 内存耗尽(达到限制?)

    我正在处理大数据 并且有一个 70GB 的 JSON 文件 我正在使用 jsonlite 库将文件加载到内存中 我尝试过 AWS EC2 x1 16large 机器 976 GB RAM 来执行此负载 但 R 因错误而中断 Error co
  • 无法使 Polly 超时策略覆盖 HttpClient 默认超时

    我正在使用 Polly 重试策略 并且正如预期的那样 在重试过程中HttpClient达到 100 秒超时 我尝试了几种不同的方法来合并 Polly 超时策略 将超时移至每次重试而不是总计 但 100 秒超时仍然会触发 我读过大约 5 个

随机推荐

  • 我如何使用Python脚本从网站获取pdf链接

    我经常需要从网站下载 pdf 文件 但有时它们不在一页上 他们在分页中划分了链接 我必须单击每一页才能获取链接 我正在学习 python 我想编写一些脚本 我可以在其中放置 web url 并从该网站提取 pdf 链接 我是 python
  • 确保在 StringTemplate 中转义 HTML 实体的最佳方法是什么

    假设有以下字符串模板 给出了 Java Bean 对象的列表 ul people p li p name p email li ul 即人员列表可能包含Person您可能有能力或没有能力增强 扩展的对象 class Person publi
  • 如何使用 ToString() 格式化可为空的 DateTime?

    如何转换可为空的日期时间dt2到格式化字符串 DateTime dt DateTime Now Console WriteLine dt ToString yyyy MM dd hh mm ss works DateTime dt2 Dat
  • 将 JSON 对象转换为另一个对象 - 格式化它

    我想将 JSON 格式的输出转换为另一种格式 我怎样才能做到这一点 例子 旧的 JSON data id e49e183e 9325 4e62 8eda 7e63fb7cdbbd name test id ac310894 d808 447
  • 大于运算符的优化

    信用表和有效交易表有2008年以来的百万条记录数据 我们正在进行迁移 所以我需要找出2017年之后不再使用的credittypeids periodseq 1055 这样就不需要迁移它们 这是查询 gt 部分会导致巨大的成本 请提出替代方案
  • 二维矩阵中各个列的最小-最大归一化

    我有一个包含 4 列 属性和 150 行的数据集 我想使用最小 最大标准化来标准化这些数据 到目前为止 我的代码是 minData min min data1 maxData max max data1 minmaxeddata data1
  • C# 是静态类型还是大部分是静态类型?

    我正在阅读JoeDuffy书中他说 C is a mostly statically typed 我读过的大多数将 C 描述为强类型语言的文章和书籍都有效地使用 强类型 来表示静态类型 真相在哪里 什么是mostly意思是 也许指的是很少有
  • Perl LibXML findvalues(...) 连接值

    我正在尝试使用 LibXML 从 XML 文件中提取节点值 当我打电话时findvalue相同元素类型的所有节点都被连接起来 我对使用 LibXML 完全陌生 而且对 Perl 也不是最熟悉 不幸的是 xml 不是最好的 如何提取单个节点
  • 快速检查值是否为数组类型(任何类型)

    如果值是数组 如何在 Swift 中检查 问题是类型数组Int显然不能转换为类型数组Any 假设我有一个数组myArray类型的Int并执行以下命令 if let array myArray as Any return true 它不会返回
  • 未解决的依赖 sbt 与 play 框架

    由于我是 Stack Overflow 的新手 请耐心等待 我正在开发一个使用 Play 2 5 的项目 该项目正是网站上的入门示例 因为我必须使用 ebean 所以我按照在 plugins sbt 中设置 ebean 的步骤进行操作 如下
  • 使用 Eclipse mac 找不到 ndk-build 和命令

    我正在使用 Eclipse for android ndk 在 mac 中开发 android 应用程序 我已经在 Eclipse 中给出了 NDK 路径 最后 当我给出此命令使用 android ndk 构建我的应用程序时 但出现以下错误
  • Pygame 没有移动我的矩形,我不明白为什么?

    我不知道为什么 但我的角色在显示中 但无法移动 它位于精灵组中 并且一直在更新 class player pygame sprite Sprite def init self pygame sprite Sprite init self s
  • 防止升级用户时出现 CoreData 崩溃

    大约一年半前 我开发了一个应用程序 现在我又回来了 这是我第一次接触 Swift 的项目 显然从那时起 无论是语言还是我的 Swift 能力都发生了很多变化 昨天 我第一次更新了我的单个 CoreData 模型以添加可选的字符串属性 我进行
  • Tomcat 7 数据源注入机制

    我正在尝试创建简单的网络应用程序 并停留在数据源注入上 似乎有几个问题 那么我就从我的困惑开始吧 据我了解 有两种 至少 方法将 DataSource 注入 Servlet web xml 资源 web xml sample
  • Java WSDL DHL 类

    我已经从 DHL WSDL 创建了 Java 类https cig dhl de cig wsdls com dpdhl wsdl geschaeftskundenversand api 2 2 geschaeftskundenversan
  • 如何在 PHP 中获取有用的错误消息?

    我经常尝试运行 PHP 脚本 但只是得到一个空白屏幕 无错误信息 只是一个空屏幕 原因可能是一个简单的语法错误 错误的括号 缺少分号 或者函数调用失败 或者完全是其他原因 很难找出哪里出了问题 我最终注释掉了代码 在各处输入 echo 语句
  • 在 OS X 10.9 (Mavericks) 上安装 Java

    我已经安装了JDK on Mac OS X v10 8 山狮 当我升级到Mac OS X v10 9 小牛队 然后跑了java version在终端中 它显示 不存在 Java 运行时 请求安装 然后我在 Mac 上手动安装了 JDK 1
  • CurrentUtcDateTime 不存在 - 实体框架和 MySql

    我在 Entity Framework 4 1 和 MySql Connector Net 6 4 3 中遇到规范函数问题 根据 Microsoft 的说法 所有数据库提供程序都可以从 LINQ 生成的 SQL 中理解规范函数并将其转换为本
  • 让 Linq 对象变得“脏”的最干净的方法是什么?

    我有一个 Linq To SQL 对象obj类型的MyClass我已经通过我的数据上下文加载了 现在我想强制该对象保存 即使没有字段实际更改 以便保存操作可以在幕后触发一些触发器 让我的数据上下文认为最简单的方法是什么obj是脏的 所以调用
  • 为什么标准 R 中值函数比简单的 C++ 替代函数慢得多?

    我对中位数进行了以下实现C 并将其用于R via Rcpp Rcpp export double median2 std vector