Skip to content

Commit a047e40

Browse files
authored
Merge branch 'master' into upgrade-atime
2 parents e6d4fd1 + b07be17 commit a047e40

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+676
-614
lines changed

.ci/atime/tests.R

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ for (extra.arg in extra.args.6107){
1515
tmp_csv = tempfile()
1616
fwrite(DT, tmp_csv)
1717
},
18+
FasterIO = "60a01fa65191c44d7997de1843e9a1dfe5be9f72", # First commit of the PR (https://github.com/Rdatatable/data.table/pull/6925/commits) that reduced time usage
1819
Slow = "e9087ce9860bac77c51467b19e92cf4b72ca78c7", # Parent of the merge commit (https://github.com/Rdatatable/data.table/commit/a77e8c22e44e904835d7b34b047df2eff069d1f2) of the PR (https://github.com/Rdatatable/data.table/pull/6107) that fixes the issue
1920
Fast = "a77e8c22e44e904835d7b34b047df2eff069d1f2") # Merge commit of the PR (https://github.com/Rdatatable/data.table/pull/6107) that fixes the issue
2021
this.test$expr = str2lang(sprintf("data.table::fread(tmp_csv, %s)", extra.arg))
@@ -130,6 +131,18 @@ test.list <- atime::atime_test_list(
130131
paste0('useDynLib(', new.Package_))
131132
},
132133

134+
# Constant overhead improvement https://github.com/Rdatatable/data.table/pull/6925
135+
# Test case adapted from https://github.com/Rdatatable/data.table/pull/7022#discussion_r2107900643
136+
"fread disk overhead improved in #6925" = atime::atime_test(
137+
N = 2^seq(0, 20), # smaller N because we are doing multiple fread calls.
138+
setup = {
139+
fwrite(iris[1], iris.csv <- tempfile())
140+
},
141+
expr = replicate(N, data.table::fread(iris.csv)),
142+
Fast = "60a01fa65191c44d7997de1843e9a1dfe5be9f72", # First commit of the PR (https://github.com/Rdatatable/data.table/pull/6925/commits) that reduced time usage
143+
Slow = "e25ea80b793165094cea87d946d2bab5628f70a6" # Parent of the first commit (https://github.com/Rdatatable/data.table/commit/60a01fa65191c44d7997de1843e9a1dfe5be9f72)
144+
),
145+
133146
# Performance regression discussed in https://github.com/Rdatatable/data.table/issues/4311
134147
# Test case adapted from https://github.com/Rdatatable/data.table/pull/4440#issuecomment-632842980 which is the fix PR.
135148
"shallow regression fixed in #4440" = atime::atime_test(
@@ -179,8 +192,9 @@ test.list <- atime::atime_test_list(
179192
# Fixed in https://github.com/Rdatatable/data.table/pull/4558
180193
"DT[by] fixed in #4558" = atime::atime_test(
181194
setup = {
195+
N9 <- as.integer(N * 0.9)
182196
d <- data.table(
183-
id = sample(c(seq.int(N * 0.9), sample(N * 0.9, N * 0.1, TRUE))),
197+
id = sample(c(seq.int(N9), sample(N9, N-N9, TRUE))),
184198
v1 = sample(5L, N, TRUE),
185199
v2 = sample(5L, N, TRUE)
186200
)
@@ -253,5 +267,15 @@ test.list <- atime::atime_test_list(
253267
Before = "f339aa64c426a9cd7cf2fcb13d91fc4ed353cd31", # Parent of the first commit https://github.com/Rdatatable/data.table/commit/fcc10d73a20837d0f1ad3278ee9168473afa5ff1 in the PR https://github.com/Rdatatable/data.table/pull/6393/commits with major change to fwrite with gzip.
254268
PR = "3630413ae493a5a61b06c50e80d166924d2ef89a"), # Close-to-last merge commit in the PR.
255269

256-
tests=extra.test.list)
270+
# Test case created directly using the atime code below (not adapted from any other benchmark), based on the PR, Removes unnecessary data.table call from as.data.table.array https://github.com/Rdatatable/data.table/pull/7010
271+
"as.data.table.array improved in #7010" = atime::atime_test(
272+
setup = {
273+
dims = c(N, 1, 1)
274+
arr = array(seq_len(prod(dims)), dim=dims)
275+
},
276+
expr = data.table:::as.data.table.array(arr, na.rm=FALSE),
277+
Slow = "73d79edf8ff8c55163e90631072192301056e336", # Parent of the first commit in the PR (https://github.com/Rdatatable/data.table/commit/8397dc3c993b61a07a81c786ca68c22bc589befc)
278+
Fast = "8397dc3c993b61a07a81c786ca68c22bc589befc"), # Commit in the PR (https://github.com/Rdatatable/data.table/pull/7019/commits) that removes inefficiency
279+
280+
tests=extra.test.list)
257281
# nolint end: undesirable_operator_linter.

.ci/linters/cocci/malloc_return_value_cast.cocci

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,9 @@ expression E;
44
@@
55
- (T)
66
malloc(E)
7+
8+
- (T)
9+
calloc(_, E)
10+
11+
- (T)
12+
realloc(_, E)

.github/workflows/performance-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ jobs:
2020
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
2121
repo_token: ${{ secrets.GITHUB_TOKEN }}
2222
steps:
23-
- uses: Anirban166/[email protected]
23+
- uses: Anirban166/[email protected]

NEWS.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414

1515
4. `as.Date()` method for `IDate` no longer coerces to `double` [#6922](https://github.com/Rdatatable/data.table/issues/6922). Thanks @MichaelChirico for the report and PR. The only effect should be on overly-strict tests that assert `Date` objects have `double` storage, which is not in general true, especially from R 4.5.0.
1616

17+
5. `as.data.table()` is slightly more efficient at converting arrays to data.tables, [#7019](https://github.com/Rdatatable/data.table/pull/7019). Thanks @eliocamp.
18+
19+
6. `between()` gains the argument `ignore_tzone=FALSE`. Normally, a difference in time zone between `lower` and `upper` will produce an error, and a difference in time zone between `x` and either of the others will produce a message. Setting `ignore_tzone=TRUE` bypasses the checks, allowing both comparisons to proceed without error or message about time zones.
20+
1721
### BUG FIXES
1822

1923
1. Custom binary operators from the `lubridate` package now work with objects of class `IDate` as with a `Date` subclass, [#6839](https://github.com/Rdatatable/data.table/issues/6839). Thanks @emallickhossain for the report and @aitap for the fix.
@@ -32,6 +36,8 @@
3236

3337
8. `fread()` no longer warns on certain systems on R 4.5.0+ where the file owner can't be resolved, [#6918](https://github.com/Rdatatable/data.table/issues/6918). Thanks @ProfFancyPants for the report and PR.
3438

39+
9. Joins to extended data.frames, e.g. `x[i, col := x.col1 + i.col2]` where `i` is a `tbl`, can use the `x.` and `i.` prefix forms, [#6998](https://github.com/Rdatatable/data.table/issues/6998). Thanks @MichaelChirico for the bug and PR.
40+
3541
### NOTES
3642

3743
1. Continued work to remove non-API C functions, [#6180](https://github.com/Rdatatable/data.table/issues/6180). Thanks Ivan Krylov for the PRs and for writing a clear and concise guide about the R API: https://aitap.codeberg.page/R-api/.
@@ -45,6 +51,12 @@
4551

4652
3. {data.table} now depends on R 3.4.0 (2017).
4753

54+
4. Changes to `fread()` output and errors:
55+
56+
+ When the size of the file exceeds the size of the address space, `fread()` now signals an informative error instead of trying to map its size modulo the address space.
57+
+ On non-Windows systems, `fread()` now prints the reason why the file couldn't be opened, which could also be due to it being too large to map.
58+
+ With `verbose=TRUE`, file sizes are now printed using correct binary SI prefixes (the sizes have always been reported as bytes denominated in powers of `2^10`, so e.g. `1024*1024` bytes was reported as `1 MB` where `1 MiB` or `1.05 MB` is correct).
59+
4860
## data.table [v1.17.0](https://github.com/Rdatatable/data.table/milestone/34) (20 Feb 2025)
4961

5062
### POTENTIALLY BREAKING CHANGES

R/as.data.table.R

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,9 @@ as.data.table.array = function(x, keep.rownames=FALSE, key=NULL, sorted=TRUE, va
9696
dnx = dimnames(x)
9797
# NULL dimnames will create integer keys, not character as in table method
9898
val = if (is.null(dnx)) {
99-
lapply(dx, seq.int)
99+
lapply(dx, seq_len)
100100
} else if (any(nulldnx <- vapply_1b(dnx, is.null))) {
101-
dnx[nulldnx] = lapply(dx[nulldnx], seq.int) #3636
101+
dnx[nulldnx] = lapply(dx[nulldnx], seq_len) #3636
102102
dnx
103103
} else dnx
104104
val = rev(val)
@@ -107,7 +107,8 @@ as.data.table.array = function(x, keep.rownames=FALSE, key=NULL, sorted=TRUE, va
107107
if (value.name %chin% names(val))
108108
stopf("Argument 'value.name' should not overlap with column names in result: %s", brackify(rev(names(val))))
109109
N = NULL
110-
ans = data.table(do.call(CJ, c(val, sorted=FALSE)), N=as.vector(x))
110+
ans = do.call(CJ, c(val, sorted=FALSE))
111+
set(ans, j="N", value=as.vector(x))
111112
if (isTRUE(na.rm))
112113
ans = ans[!is.na(N)]
113114
setnames(ans, "N", value.name)

R/between.R

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# is x[i] in between lower[i] and upper[i] ?
2-
between = function(x, lower, upper, incbounds=TRUE, NAbounds=TRUE, check=FALSE) {
2+
between = function(x, lower, upper, incbounds=TRUE, NAbounds=TRUE, check=FALSE, ignore_tzone=FALSE) {
33
if (is.logical(x)) stopf("between has been passed an argument x of type logical")
44
if (is.logical(lower)) lower = as.integer(lower) # typically NA (which is logical type)
55
if (is.logical(upper)) upper = as.integer(upper) # typically NA (which is logical type)
@@ -16,15 +16,12 @@ between = function(x, lower, upper, incbounds=TRUE, NAbounds=TRUE, check=FALSE)
1616
stopifnot(is.px(x), is.px(lower), is.px(upper)) # nocov # internal
1717
}
1818
# POSIX check timezone match
19-
if (is.px(x) && is.px(lower) && is.px(upper)) {
20-
tzs = sapply(list(x,lower,upper), function(x) {
21-
attr(x, "tzone", exact=TRUE) %||% ""
22-
})
19+
if (!ignore_tzone && is.px(x) && is.px(lower) && is.px(upper)) {
20+
tzs = vapply_1c(list(x, lower, upper), function(x) attr(x, "tzone", exact=TRUE) %||% "")
2321
# lower/upper should be more tightly linked than x/lower, so error
2422
# if the former don't match but only inform if they latter don't
2523
if (tzs[2L]!=tzs[3L]) {
2624
stopf("'between' lower= and upper= are both POSIXct but have different tzone attributes: %s. Please align their time zones.", brackify(tzs[2:3], quote=TRUE))
27-
# otherwise the check in between.c that lower<=upper can (correctly) fail for this reason
2825
}
2926
if (tzs[1L]!=tzs[2L]) {
3027
messagef("'between' arguments are all POSIXct but have mismatched tzone attributes: %s. The UTC times will be compared.", brackify(tzs, quote=TRUE))

R/data.table.R

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -677,10 +677,12 @@ replace_dot_alias = function(e) {
677677
}
678678
ansvals = chmatch(ansvars, nx)
679679
} else {
680-
if (is.data.table(i)) {
680+
if (is.data.frame(i)) {
681681
idotprefix = paste0("i.", names_i)
682682
xdotprefix = paste0("x.", names_x)
683-
} else idotprefix = xdotprefix = character(0L)
683+
} else {
684+
idotprefix = xdotprefix = character(0L)
685+
}
684686

685687
# j was substituted before dealing with i so that := can set allow.cartesian=FALSE (#800) (used above in i logic)
686688
if (is.null(jsub)) return(NULL)

inst/tests/tests.Rraw

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21133,3 +21133,27 @@ test(2315.1, tail(DT[order(i), i], 2L), 1:2)
2113321133
# wider range of numbers needed for further coverage
2113421134
DT[1L, i := 1000L]
2113521135
test(2315.2, tail(DT[order(i), i], 2L), c(1L, 1000L))
21136+
21137+
# issue #6898, test that tzone behavior changes with ignore_tzone=TRUE
21138+
tms = list(.POSIXct(1), .POSIXct(1.0, "UTC"))
21139+
test(2316.1, between(tms[[1]], tms[[1L]], tms[[2L]]), error = "different tzone attributes")
21140+
test(2316.2, between(tms[[1]], tms[[1L]], tms[[2L]], ignore_tzone=TRUE))
21141+
test(2316.3, between(tms[[1]], tms[[2L]], tms[[2L]]), message = "mismatched tzone attributes")
21142+
test(2316.4, between(tms[[1]], tms[[2L]], tms[[2L]], ignore_tzone=TRUE))
21143+
21144+
# tbl in i still allows 'i.' prefix reference for update join, #6998
21145+
DT1 = data.table(a=1, b=2)
21146+
DT2 = data.table(a=1, c=3)
21147+
DF1 = data.frame(a=1, d=4)
21148+
DF2 = data.frame(a=1, e=5)
21149+
class(DF2) = c("tbl_df", "tbl", "data.frame")
21150+
21151+
test(2317.1, DT1[DT2, on='a', c := i.c]$c, 3)
21152+
test(2317.2, DT1[DT2, on='a', c2 := x.a + i.c]$c2, 4)
21153+
test(2317.3, DT1[DT2, on='a', .(c = x.a + i.c)]$c, 4)
21154+
test(2317.4, DT1[DF1, on='a', d := i.d]$d, 4)
21155+
test(2317.5, DT1[DF1, on='a', d2 := x.a + i.d]$d2, 5)
21156+
test(2317.6, DT1[DF1, on='a', .(d = x.a + i.d)]$d, 5)
21157+
test(2317.7, DT1[DF2, on='a', e := i.e]$e, 5)
21158+
test(2317.8, DT1[DF2, on='a', e2 := x.a + i.e]$e2, 6)
21159+
test(2317.9, DT1[DF2, on='a', .(e = x.a + i.e)]$e, 6)

man/between.Rd

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ This can be changed by setting \code{NAbounds} to \code{NA}.
1616
the intervals provided in \code{lower,upper}.
1717
}
1818
\usage{
19-
between(x, lower, upper, incbounds=TRUE, NAbounds=TRUE, check=FALSE)
19+
between(x, lower, upper, incbounds=TRUE, NAbounds=TRUE, check=FALSE, ignore_tzone=FALSE)
2020
x \%between\% y
2121

2222
inrange(x, lower, upper, incbounds=TRUE)
@@ -35,6 +35,7 @@ interpreted as \code{lower} and \code{y[[2]]} as \code{upper}.}
3535
It is set to \code{TRUE} by default for infix notations.}
3636
\item{NAbounds}{ If \code{lower} (\code{upper}) contains an \code{NA} what should \code{lower<=x} (\code{x<=upper}) return? By default \code{TRUE} so that a missing bound is interpreted as unlimited. }
3737
\item{check}{ Produce error if \code{any(lower>upper)}? \code{FALSE} by default for efficiency, in particular type \code{character}. }
38+
\item{ignore_tzone}{ \code{TRUE} means skip timezone checks among \code{x}, \code{lower}, and \code{upper}. }
3839
}
3940
\details{
4041

src/assign.c

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ SEXP assign(SEXP dt, SEXP rows, SEXP cols, SEXP newcolnames, SEXP values)
400400
// FR #2077 - set able to add new cols by reference
401401
if (isString(cols)) {
402402
PROTECT(tmp = chmatch(cols, names, 0)); protecti++;
403-
buf = (int *) R_alloc(length(cols), sizeof(int));
403+
buf = (int *) R_alloc(length(cols), sizeof(*buf));
404404
int k=0;
405405
for (int i=0; i<length(cols); ++i) {
406406
if (INTEGER(tmp)[i] == 0) buf[k++] = i;
@@ -699,7 +699,7 @@ SEXP assign(SEXP dt, SEXP rows, SEXP cols, SEXP newcolnames, SEXP values)
699699
}
700700
if (ndelete) {
701701
// delete any columns assigned NULL (there was a 'continue' earlier in loop above)
702-
int *tt = (int *)R_alloc(ndelete, sizeof(int));
702+
int *tt = (int *)R_alloc(ndelete, sizeof(*tt));
703703
const int *colsd=INTEGER(cols), ncols=length(cols), ndt=length(dt);
704704
for (int i=0, k=0; i<ncols; ++i) { // find which ones to delete and put them in tt
705705
// Aside: a new column being assigned NULL (something odd to do) would have been warned above, added above, and now deleted. Just
@@ -1055,7 +1055,7 @@ const char *memrecycle(const SEXP target, const SEXP where, const int start, con
10551055
case RAWSXP: BODY(Rbyte, RAW, int, val!=0, td[i]=cval)
10561056
case LGLSXP:
10571057
if (mc) {
1058-
memcpy(td, LOGICAL_RO(source), slen*sizeof(int)); break;
1058+
memcpy(td, LOGICAL_RO(source), slen*sizeof(*td)); break;
10591059
} else BODY(int, LOGICAL, int, val, td[i]=cval)
10601060
case INTSXP: BODY(int, INTEGER, int, val==NA_INTEGER ? NA_LOGICAL : val!=0, td[i]=cval)
10611061
case REALSXP:
@@ -1072,7 +1072,7 @@ const char *memrecycle(const SEXP target, const SEXP where, const int start, con
10721072
case LGLSXP: // same as INTSXP ...
10731073
case INTSXP:
10741074
if (mc) {
1075-
memcpy(td, INTEGER_RO(source), slen*sizeof(int)); break;
1075+
memcpy(td, INTEGER_RO(source), slen*sizeof(*td)); break;
10761076
} else BODY(int, INTEGER, int, val, td[i]=cval)
10771077
case REALSXP:
10781078
if (sourceIsI64)
@@ -1092,7 +1092,7 @@ const char *memrecycle(const SEXP target, const SEXP where, const int start, con
10921092
case REALSXP:
10931093
if (sourceIsI64) {
10941094
if (mc) {
1095-
memcpy(td, (const int64_t *)REAL_RO(source), slen*sizeof(int64_t)); break;
1095+
memcpy(td, (const int64_t *)REAL_RO(source), slen*sizeof(*td)); break;
10961096
} else BODY(int64_t, REAL, int64_t, val, td[i]=cval)
10971097
} else BODY(double, REAL, int64_t, within_int64_repres(val) ? val : NA_INTEGER64, td[i]=cval)
10981098
case CPLXSXP: BODY(Rcomplex, COMPLEX, int64_t, ISNAN(val.r) ? NA_INTEGER64 : (int64_t)val.r, td[i]=cval)
@@ -1291,14 +1291,14 @@ void savetl(SEXP s)
12911291
internal_error(__func__, "reached maximum %d items for savetl", nalloc); // # nocov
12921292
}
12931293
nalloc = nalloc>(INT_MAX/2) ? INT_MAX : nalloc*2;
1294-
char *tmp = (char *)realloc(saveds, nalloc*sizeof(SEXP));
1294+
char *tmp = realloc(saveds, sizeof(SEXP)*nalloc);
12951295
if (tmp==NULL) {
12961296
// C spec states that if realloc() fails the original block is left untouched; it is not freed or moved. We rely on that here.
12971297
savetl_end(); // # nocov free(saveds) happens inside savetl_end
12981298
error(_("Failed to realloc saveds to %d items in savetl"), nalloc); // # nocov
12991299
}
13001300
saveds = (SEXP *)tmp;
1301-
tmp = (char *)realloc(savedtl, nalloc*sizeof(R_len_t));
1301+
tmp = realloc(savedtl, sizeof(R_len_t)*nalloc);
13021302
if (tmp==NULL) {
13031303
savetl_end(); // # nocov
13041304
error(_("Failed to realloc savedtl to %d items in savetl"), nalloc); // # nocov
@@ -1335,4 +1335,3 @@ SEXP setcharvec(SEXP x, SEXP which, SEXP newx)
13351335
}
13361336
return R_NilValue;
13371337
}
1338-

0 commit comments

Comments
 (0)