gpt4 book ai didi

r - 更快的替代 file.exists()

转载 作者:行者123 更新时间:2023-12-05 00:09:57 26 4
gpt4 key购买 nike

我维护了一个 R 包,它需要单独检查大量小文件的存在。多次调用file.exists()产生明显的缓慢(benchmarking results here)。不幸的是,情况限制使我无法调用 file.exists()一次以矢量化方式处理整批文件,我相信这会快得多。有没有更快的方法来检查单个文件是否存在?也许在C?这种方式在我的系统上似乎并没有更快(与产生 these benchmarks 的系统相同):

library(inline)
library(microbenchmark)

body <- "
FILE *fp = fopen(CHAR(STRING_ELT(r_path, 0)), \"r\");
SEXP result = PROTECT(allocVector(INTSXP, 1));
INTEGER(result)[0] = fp == NULL? 0 : 1;
UNPROTECT(1);
return result;
"

file_exists_c <- cfunction(sig = signature(r_path = "character"), body = body)

tmp <- tempfile()

microbenchmark(
c = file_exists_c(tmp),
r = file.exists(tmp)
)
#> Unit: microseconds
#> expr min lq mean median uq max neval
#> c 4.912 5.0230 5.42443 5.0605 5.1240 25.264 100
#> r 3.972 4.0525 4.32615 4.1835 4.2675 11.750 100

file.create(tmp)
#> [1] TRUE

microbenchmark(
c = file_exists_c(tmp),
r = file.exists(tmp)
)
#> Unit: microseconds
#> expr min lq mean median uq max neval
#> c 16.212 16.6245 17.04727 16.7645 16.9860 32.207 100
#> r 6.242 6.4175 7.16057 7.2830 7.4605 26.781 100

创建于 2019-12-06 由 reprex package (v0.3.0)

编辑: access() access()确实似乎更快,但不是很多。

library(inline)
library(microbenchmark)

body <- "
SEXP result = PROTECT(allocVector(INTSXP, 1));
INTEGER(result)[0] = access(CHAR(STRING_ELT(r_path, 0)), 0)? 0 : 1;
UNPROTECT(1);
return result;
"

file_exists_c <- cfunction(
sig = signature(r_path = "character"),
body = body,
includes = "#include <unistd.h>"
)

tmp <- tempfile()

microbenchmark(
c = file_exists_c(tmp),
r = file.exists(tmp)
)
#> Unit: microseconds
#> expr min lq mean median uq max neval
#> c 1.033 1.048 1.21334 1.0745 1.0910 13.793 100
#> r 1.051 1.068 1.19280 1.0930 1.1175 10.048 100

file.create(tmp)
#> [1] TRUE

microbenchmark(
c = file_exists_c(tmp),
r = file.exists(tmp)
)
#> Unit: microseconds
#> expr min lq mean median uq max neval
#> c 1.073 1.0910 1.33543 1.1285 1.1500 16.676 100
#> r 1.172 1.1965 1.32934 1.2335 1.2695 9.916 100

创建于 2019-12-07 由 reprex package (v0.3.0)

最佳答案

这是file.exists的全部内容源代码(撰写本文时):

https://github.com/wch/r-source/blob/bfe73ecd848198cb9b68427cec7e70c40f96bd72/src/main/platform.c#L1375-L1404

SEXP attribute_hidden do_fileexists(SEXP call, SEXP op, SEXP args, SEXP rho)
{
SEXP file, ans;
int i, nfile;
checkArity(op, args);
if (!isString(file = CAR(args)))
error(_("invalid '%s' argument"), "file");
nfile = LENGTH(file);
ans = PROTECT(allocVector(LGLSXP, nfile));
for (i = 0; i < nfile; i++) {
LOGICAL(ans)[i] = 0;
if (STRING_ELT(file, i) != NA_STRING) {
#ifdef Win32
/* Package XML sends arbitrarily long strings to file.exists! */
size_t len = strlen(CHAR(STRING_ELT(file, i)));
if (len > MAX_PATH)
LOGICAL(ans)[i] = FALSE;
else
LOGICAL(ans)[i] =
R_WFileExists(filenameToWchar(STRING_ELT(file, i), TRUE));
#else
// returns NULL if not translatable
const char *p = translateCharFP2(STRING_ELT(file, i));
LOGICAL(ans)[i] = p && R_FileExists(p);
#endif
} else LOGICAL(ans)[i] = FALSE;
}
UNPROTECT(1); /* ans */
return ans;
}

至于 R_FileExists , 它在这里:

https://github.com/wch/r-source/blob/bfe73ecd848198cb9b68427cec7e70c40f96bd72/src/main/sysutils.c#L60-L79
#ifdef Win32
Rboolean R_FileExists(const char *path)
{
struct _stati64 sb;
return _stati64(R_ExpandFileName(path), &sb) == 0;
}
#else
Rboolean R_FileExists(const char *path)
{
struct stat sb;
return stat(R_ExpandFileName(path), &sb) == 0;
}

( R_ExpandFileName 只是在做 path.expand )。它依赖于 stat系统实用程序:

https://en.wikipedia.org/wiki/Stat_(system_call)

https://pubs.opengroup.org/onlinepubs/007908799/xsh/sysstat.h.html

它是为矢量化输入而构建的,因此如前所述,最好使用 file.exists(vector_of_files)比重复运行 file.exists(single_file) .

据我所知(诚然,我不是这里的系统实用程序方面的专家),任何效率提升都以稳健性为代价。

关于r - 更快的替代 file.exists(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59222945/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com