R/Rcpp: 如何有效地将 2 字节十六进制值转换为整数?
R/Rcpp: How can I convert 2-byte Hex values to integers efficiently?
我想将包含 2 字节十六进制数字(小端)的原始类型向量转换为 R 中的整数向量(例如 ff ff -> 0xffff = 65535)。一种方法是从原始向量中提取偶数和奇数元素,并粘贴到字符中,然后转换为整数,如下所示:
> a <- c(as.raw(255), as.raw(254), as.raw(253), as.raw(252))
> a
[1] ff fe fd fc
> even_elem <- a[seq(2,length(a),2)]
> odd_elem <- a[seq(1,length(a),2)]
> as.integer(paste0("0x", even_elem, odd_elem))
[1] 65279 64765
> c(0xfeff, 0xfcfd)
[1] 65279 64765
问题是我想对具有 >10^8 个元素的向量执行此操作。如果我使用上述方法执行此操作,则需要几分钟。我想要更有效率的东西。我想我可以尝试使用 Rcpp 来加快速度,所以我写了一段 cpp 代码(我是 Rcpp/c++ 的新手),
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
IntegerVector raw2intC(CharacterVector vec){
int n = vec.size();
int m;
Rcpp::IntegerVector x(n/2);
for (int i = 0; i < n/2; i++) {
std::string h1 = Rcpp::as<std::string>(vec[i*2]);
std::string h2 = Rcpp::as<std::string>(vec[i*2 + 1]);
h2 += h1;
std::stringstream ss;
ss << std::hex << h2;
ss >> m;
x[i] = m;
}
return(x);
}
和一个 R 脚本。
raw2intR <- function(obj){
val <- raw2intC(obj)
val
}
此 Rcpp 代码有效,微基准测试的结果令人鼓舞。
> microbenchmark(raw2intR(a), as.integer(paste0("0x", even_elem, odd_elem)))
Unit: microseconds
expr min lq mean median uq max
raw2intR(a) 4.953 5.9130 7.68194 7.4800 8.4585 42.658
as.integer(...) 36.297 40.4275 44.06539 42.8565 44.9420 147.110
> identical(raw2intR(a), as.integer(paste0("0x", even_elem, odd_elem)))
[1] TRUE
但是,当使用更大的向量进行测试时,R 和 Rcpp 解决方案之间的执行时间没有太大差异。事实上,R 解决方案稍微快一些。
> b <- raw(1000000)
> even_elem <- b[seq(2,length(a),2)]
> odd_elem <- b[seq(1,length(a),2)]
> microbenchmark(raw2intR(b), as.integer(paste0("0x", even_elem, odd_elem)), times=10)
Unit: milliseconds
expr min lq mean median uq
raw2intR(b) 309.4139 309.7920 316.6345 313.6219 321.5353
as.integer(...) 274.3523 279.6978 287.5415 288.1744 291.1616
> identical(raw2intR(b), as.integer(paste0("0x", even_elem, odd_elem)))
[1] TRUE
如何加快这项任务?我希望实现 10 倍的改进。
感谢您的建议。
您可以直接使用 readBin
告诉 R 将这些原始值解释为整数,而不是构建字符串以转换回数字。例如
a <- as.raw(c(255, 254, 253, 252))
readBin(a, "integer", n=length(a)/2, size=2, signed=FALSE)
# [1] 65279 64765
我想将包含 2 字节十六进制数字(小端)的原始类型向量转换为 R 中的整数向量(例如 ff ff -> 0xffff = 65535)。一种方法是从原始向量中提取偶数和奇数元素,并粘贴到字符中,然后转换为整数,如下所示:
> a <- c(as.raw(255), as.raw(254), as.raw(253), as.raw(252))
> a
[1] ff fe fd fc
> even_elem <- a[seq(2,length(a),2)]
> odd_elem <- a[seq(1,length(a),2)]
> as.integer(paste0("0x", even_elem, odd_elem))
[1] 65279 64765
> c(0xfeff, 0xfcfd)
[1] 65279 64765
问题是我想对具有 >10^8 个元素的向量执行此操作。如果我使用上述方法执行此操作,则需要几分钟。我想要更有效率的东西。我想我可以尝试使用 Rcpp 来加快速度,所以我写了一段 cpp 代码(我是 Rcpp/c++ 的新手),
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
IntegerVector raw2intC(CharacterVector vec){
int n = vec.size();
int m;
Rcpp::IntegerVector x(n/2);
for (int i = 0; i < n/2; i++) {
std::string h1 = Rcpp::as<std::string>(vec[i*2]);
std::string h2 = Rcpp::as<std::string>(vec[i*2 + 1]);
h2 += h1;
std::stringstream ss;
ss << std::hex << h2;
ss >> m;
x[i] = m;
}
return(x);
}
和一个 R 脚本。
raw2intR <- function(obj){
val <- raw2intC(obj)
val
}
此 Rcpp 代码有效,微基准测试的结果令人鼓舞。
> microbenchmark(raw2intR(a), as.integer(paste0("0x", even_elem, odd_elem)))
Unit: microseconds
expr min lq mean median uq max
raw2intR(a) 4.953 5.9130 7.68194 7.4800 8.4585 42.658
as.integer(...) 36.297 40.4275 44.06539 42.8565 44.9420 147.110
> identical(raw2intR(a), as.integer(paste0("0x", even_elem, odd_elem)))
[1] TRUE
但是,当使用更大的向量进行测试时,R 和 Rcpp 解决方案之间的执行时间没有太大差异。事实上,R 解决方案稍微快一些。
> b <- raw(1000000)
> even_elem <- b[seq(2,length(a),2)]
> odd_elem <- b[seq(1,length(a),2)]
> microbenchmark(raw2intR(b), as.integer(paste0("0x", even_elem, odd_elem)), times=10)
Unit: milliseconds
expr min lq mean median uq
raw2intR(b) 309.4139 309.7920 316.6345 313.6219 321.5353
as.integer(...) 274.3523 279.6978 287.5415 288.1744 291.1616
> identical(raw2intR(b), as.integer(paste0("0x", even_elem, odd_elem)))
[1] TRUE
如何加快这项任务?我希望实现 10 倍的改进。
感谢您的建议。
您可以直接使用 readBin
告诉 R 将这些原始值解释为整数,而不是构建字符串以转换回数字。例如
a <- as.raw(c(255, 254, 253, 252))
readBin(a, "integer", n=length(a)/2, size=2, signed=FALSE)
# [1] 65279 64765