GH-43349: [R] Fix altrep string columns from readr - #43351

Merged
jonkeane merged 6 commits into
apache:mainfrom
jonkeane:lazy_readr
Jul 27, 2024
Merged

GH-43349: [R] Fix altrep string columns from readr#43351
jonkeane merged 6 commits into
apache:mainfrom
jonkeane:lazy_readr

Conversation

@jonkeane

@jonkeanejonkeane commented Jul 21, 2024

Copy link
Copy Markdown
Member

Rationale for this change

To resolve the reverse dependency issue with parquetize

What changes are included in this PR?

One step towards resolving the issue

Are these changes tested?

yes

Are there any user-facing changes?

no

@jonkeane
jonkeane requested a review from thisisnic as a code ownerJuly 21, 2024 02:46
@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #43349has been automatically assigned in GitHub to PR creator.

Comment threadr/src/arrow_cpp11.h Outdated
for (R_xlen_t i = 0; i < n; i++, ++p_x) {
SEXP s = *p_x;
if (s != NA_STRING) {
if (s != NA_STRING && ALTREP(s)) {

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not actually work yet. With this

test_that("We handle non-UTF strings", {
x<- iconv("Veitingastaðir", to="latin1")
df<-tibble::tibble(
chr=x,
fct= as.factor(x)
)
names(df) <- iconv(paste(x, names(df), sep="_"), to="latin1")
df_struct<-tibble::tibble(a=df)
raw_schema<-list(utf8(), dictionary(int8(), utf8()))
names(raw_schema) <- names(df)
# Confirm setup
expect_identical(Encoding(x), "latin1")
expect_identical(Encoding(names(df)), c("latin1", "latin1"))
expect_identical(Encoding(df[[1]]), "latin1")
expect_identical(Encoding(levels(df[[2]])), "latin1")
# Array
expect_identical(as.vector(Array$create(x)), x)
fail.

Before #43173 this line was:

if (s != NA_STRING && !IS_UTF8(s) && !IS_ASCII(s)) {

I suspect what's going on is that we caught vroom altrep vectors with this if and called SET_STRING_ELT so we didn't get the No Set_elt found for ALTSTRING error. But which ALTREP(s) we detect the non-utf strings here too and attempt to SET_STRING_ELT when we shouldn't.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any thoughts @nealrichardson ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I don't understand what's happening in here. L144 says that this should no longer be altrep by the time we get here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think that STRING_PTR_RO() should materialize an ALTREP character vector, otherwise you can't iterate over the CHARSXP pointers.

Also, ALTREP(s) does not seem to make sense to me, that's a CHARSXP (not a STRSXP), so it cannot be ALTREP, unless I am misremembering something.

Are you trying to catch the non-UTF-8 strings here? Rf_translateCharUTF8() does not do anything (returns the same const char *) if the string is UTF-8, so you could always call it, and only call SET_STRING_ELT() if the returned pointer is different?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think that STRING_PTR_RO() should materialize an ALTREP character vector, otherwise you can't iterate over the CHARSXP pointers.

Hmm, so maybe the actual problem here is that that isn't working on the ALTREP vectors coming from vroom? Here is the error we started seeing when we changed this condition from s != NA_STRING && !IS_UTF8(s) && !IS_ASCII(s) to s != NA_STRING:

Error in Table__from_dots(dots, schema, option_use_threads()): No Set_elt found for ALTSTRING class [class: vroom_chr, pkg: vroom]

Also, ALTREP(s) does not seem to make sense to me, that's a CHARSXP (not a STRSXP), so it cannot be ALTREP, unless I am misremembering something.

Yeah, I agree that ALTREP(s) here is not right.

Are you trying to catch the non-UTF-8 strings here? Rf_translateCharUTF8() does not do anything (returns the same const char *) if the string is UTF-8, so you could always call it,

I will admit I'm not 100% certain what is going on here, but I catching non-UTF-8 strings here is what I thought it was doing.

and only call SET_STRING_ELT() if the returned pointer is different?

Oh interesting. Forgive my C++ naïveness, but would this be something like (i.e. does != check if the pointer is different>):

SEXP new_s = Rf_mkCharCE(Rf_translateCharUTF8(s);
if (new_s != s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(Rf_translateCharUTF8(new_s), CE_UTF8));
}

@gaborcsardigaborcsardiJul 22, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that C++ looks correct, except you don't need to translate twice:

SEXP new_s = Rf_mkCharCE(Rf_translateCharUTF8(s);
if (new_s != s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(new_s));
}

However, I think the issue is that if x is ALTREP, then it does not matter if it is materialized of not, it is still ALTREP, so you still cannot change it with SET_STRING_ELT.

To avoid this, you'd need to call Rf_duplicate() on x. That should create a non-altrep copy of it.

You can always call Rf_duplicate(), or you can call it when you encounter a non-utf8 string. Note that you need to PROTECT() the result of Rf_duplicate()

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aaah thanks for those pointers! That looked like it worked, I'm going to trigger a benchmark run to see if there are any unintended consequences there (Hopefully no, but even if so, we might need to accept them!)

Comment threadr/DESCRIPTION Outdated
lubridate,
pillar,
pkgload,
readr,

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure the test is worth adding to suggests, though we will get rid of that annoying xref error if we do.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting committer review Awaiting committer review labels Jul 21, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 23, 2024
@jonkeane

Copy link
Copy Markdown
MemberAuthor

@ursabot please benchmark

@ursabot

Copy link
Copy Markdown

Benchmark runs are scheduled for commit 2a0da1e. Watch https://buildkite.com/apache-arrow and https://conbench.ursa.dev for updates. A comment will be posted here when the runs are complete.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting change review Awaiting change review labels Jul 23, 2024
Comment threadr/src/arrow_cpp11.h Outdated
// ensure that x is not actually altrep first
if (ALTREP(x)) {
x = PROTECT(Rf_duplicate(x));
UNPROTECT(1);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure this isn't right, doesn't the UNPROTECT need to go after you're done using the thing you PROTECTed?

I'm also not sure how the C-level PROTECT stuff interacts with unwind_protect.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, I'm surprised I don't get any stack imbalance issues with it. I do get them if I don't have UNPROTECT or I call it unconditionally. But anyway I have a slightly different version incoming.

@conbench-apache-arrow

Copy link
Copy Markdown

Thanks for your patience. Conbench analyzed the 4 benchmarking runs that have been run so far on PR commit 2a0da1e.

There were 38 benchmark results with an error:

There were 11 benchmark results indicating a performance regression:

The full Conbench report has more details.

@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 23, 2024
@jonkeane

Copy link
Copy Markdown
MemberAuthor

@github-actions crossbow submit -g r

@github-actions

Copy link
Copy Markdown

Revision: 206e94d

Submitted crossbow builds: ursacomputing/crossbow @ actions-6de7c6d005

TaskStatus
r-binary-packagesGitHub Actions
test-r-arrow-backwards-compatibilityGitHub Actions
test-r-clang-sanitizerGitHub Actions
test-r-depsource-bundledAzure
test-r-depsource-systemGitHub Actions
test-r-dev-duckdbGitHub Actions
test-r-devdocsGitHub Actions
test-r-gcc-11GitHub Actions
test-r-gcc-12GitHub Actions
test-r-install-localGitHub Actions
test-r-install-local-minsizerelGitHub Actions
test-r-linux-as-cranGitHub Actions
test-r-linux-rchkGitHub Actions
test-r-linux-valgrindGitHub Actions
test-r-minimal-buildAzure
test-r-offline-maximalGitHub Actions
test-r-offline-minimalAzure
test-r-rhub-debian-gcc-devel-lto-latestAzure
test-r-rhub-debian-gcc-release-custom-ccacheAzure
test-r-rhub-ubuntu-release-latestAzure
test-r-rocker-r-ver-latestAzure
test-r-rstudio-r-base-4.1-opensuse155Azure
test-r-rstudio-r-base-4.2-focalAzure
test-r-ubuntu-22.04GitHub Actions
test-r-versionsGitHub Actions
test-ubuntu-r-sanitizerGitHub Actions

Comment threadr/src/arrow_cpp11.h
// ensure that x is not actually altrep first
bool was_altrep = ALTREP(x);
if (was_altrep) {
x = PROTECT(Rf_duplicate(x));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment about why we have to duplicate?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'll expand what I have there

Comment threadr/src/arrow_cpp11.h
@@ -152,6 +157,9 @@ inline SEXP utf8_strings(SEXP x) {
SET_STRING_ELT(x, i, Rf_mkCharCE(Rf_translateCharUTF8(s), CE_UTF8));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we want to check whether Rf_translateCharUTF8() actually modified anything? Or do we trust that SET_STRING_ELT is a no-op in that case? I would imagine that in most cases, we already have ascii/utf-8 strings, so this whole function should be basically free. That should be easily verified by microbenchmarking.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this from above?

SEXP new_s = Rf_translateCharUTF8(s);
if (new_s == s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(new_s, CE_UTF8));
}

Yeah, that's probably good. It'll also make this slightly more inline with what was there before (checking that not utf8)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried this, but after getting it to work with a bit of type faffing, doing the translate first we get errors with our utf string tests again.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting change review Awaiting change review labels Jul 24, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes awaiting change review Awaiting change review labels Jul 25, 2024
@github-actionsgithub-actionsBot added the awaiting changes Awaiting changes label Jul 25, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 27, 2024
@jonkeane
jonkeane merged commit 187197c into apache:mainJul 27, 2024
@jonkeanejonkeane removed the awaiting change review Awaiting change review label Jul 27, 2024
jonkeane added a commit that referenced this pull request Jul 27, 2024
### Rationale for this change
To resolve the reverse dependency issue with `parquetize`
### What changes are included in this PR?
One step towards resolving the issue
### Are these changes tested?
yes
### Are there any user-facing changes?
no
* GitHub Issue: #43349
Authored-by: Jonathan Keane <jkeane@gmail.com>
Signed-off-by: Jonathan Keane <jkeane@gmail.com>
@conbench-apache-arrow

Copy link
Copy Markdown

After merging your PR, Conbench analyzed the 4 benchmarking runs that have been run so far on merge-commit 187197c.

There were no benchmark performance regressions. 🎉

The full Conbench report has more details. It also includes information about 28 possible false positives for unstable benchmarks that are known to sometimes produce them.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jonkeane@ursabot@gaborcsardi@nealrichardson
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

GH-43349: [R] Fix altrep string columns from readr - #43351

Merged
jonkeane merged 6 commits into
apache:mainfrom
jonkeane:lazy_readr
Jul 27, 2024
Merged

GH-43349: [R] Fix altrep string columns from readr#43351
jonkeane merged 6 commits into
apache:mainfrom
jonkeane:lazy_readr

Conversation

@jonkeane

@jonkeanejonkeane commented Jul 21, 2024

Copy link
Copy Markdown
Member

Rationale for this change

To resolve the reverse dependency issue with parquetize

What changes are included in this PR?

One step towards resolving the issue

Are these changes tested?

yes

Are there any user-facing changes?

no

@jonkeane
jonkeane requested a review from thisisnic as a code ownerJuly 21, 2024 02:46
@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #43349has been automatically assigned in GitHub to PR creator.

Comment threadr/src/arrow_cpp11.h Outdated
for (R_xlen_t i = 0; i < n; i++, ++p_x) {
SEXP s = *p_x;
if (s != NA_STRING) {
if (s != NA_STRING && ALTREP(s)) {

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not actually work yet. With this

test_that("We handle non-UTF strings", {
x<- iconv("Veitingastaðir", to="latin1")
df<-tibble::tibble(
chr=x,
fct= as.factor(x)
)
names(df) <- iconv(paste(x, names(df), sep="_"), to="latin1")
df_struct<-tibble::tibble(a=df)
raw_schema<-list(utf8(), dictionary(int8(), utf8()))
names(raw_schema) <- names(df)
# Confirm setup
expect_identical(Encoding(x), "latin1")
expect_identical(Encoding(names(df)), c("latin1", "latin1"))
expect_identical(Encoding(df[[1]]), "latin1")
expect_identical(Encoding(levels(df[[2]])), "latin1")
# Array
expect_identical(as.vector(Array$create(x)), x)
fail.

Before #43173 this line was:

if (s != NA_STRING && !IS_UTF8(s) && !IS_ASCII(s)) {

I suspect what's going on is that we caught vroom altrep vectors with this if and called SET_STRING_ELT so we didn't get the No Set_elt found for ALTSTRING error. But which ALTREP(s) we detect the non-utf strings here too and attempt to SET_STRING_ELT when we shouldn't.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any thoughts @nealrichardson ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I don't understand what's happening in here. L144 says that this should no longer be altrep by the time we get here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think that STRING_PTR_RO() should materialize an ALTREP character vector, otherwise you can't iterate over the CHARSXP pointers.

Also, ALTREP(s) does not seem to make sense to me, that's a CHARSXP (not a STRSXP), so it cannot be ALTREP, unless I am misremembering something.

Are you trying to catch the non-UTF-8 strings here? Rf_translateCharUTF8() does not do anything (returns the same const char *) if the string is UTF-8, so you could always call it, and only call SET_STRING_ELT() if the returned pointer is different?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think that STRING_PTR_RO() should materialize an ALTREP character vector, otherwise you can't iterate over the CHARSXP pointers.

Hmm, so maybe the actual problem here is that that isn't working on the ALTREP vectors coming from vroom? Here is the error we started seeing when we changed this condition from s != NA_STRING && !IS_UTF8(s) && !IS_ASCII(s) to s != NA_STRING:

Error in Table__from_dots(dots, schema, option_use_threads()): No Set_elt found for ALTSTRING class [class: vroom_chr, pkg: vroom]

Also, ALTREP(s) does not seem to make sense to me, that's a CHARSXP (not a STRSXP), so it cannot be ALTREP, unless I am misremembering something.

Yeah, I agree that ALTREP(s) here is not right.

Are you trying to catch the non-UTF-8 strings here? Rf_translateCharUTF8() does not do anything (returns the same const char *) if the string is UTF-8, so you could always call it,

I will admit I'm not 100% certain what is going on here, but I catching non-UTF-8 strings here is what I thought it was doing.

and only call SET_STRING_ELT() if the returned pointer is different?

Oh interesting. Forgive my C++ naïveness, but would this be something like (i.e. does != check if the pointer is different>):

SEXP new_s = Rf_mkCharCE(Rf_translateCharUTF8(s);
if (new_s != s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(Rf_translateCharUTF8(new_s), CE_UTF8));
}

@gaborcsardigaborcsardiJul 22, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that C++ looks correct, except you don't need to translate twice:

SEXP new_s = Rf_mkCharCE(Rf_translateCharUTF8(s);
if (new_s != s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(new_s));
}

However, I think the issue is that if x is ALTREP, then it does not matter if it is materialized of not, it is still ALTREP, so you still cannot change it with SET_STRING_ELT.

To avoid this, you'd need to call Rf_duplicate() on x. That should create a non-altrep copy of it.

You can always call Rf_duplicate(), or you can call it when you encounter a non-utf8 string. Note that you need to PROTECT() the result of Rf_duplicate()

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aaah thanks for those pointers! That looked like it worked, I'm going to trigger a benchmark run to see if there are any unintended consequences there (Hopefully no, but even if so, we might need to accept them!)

Comment threadr/DESCRIPTION Outdated
lubridate,
pillar,
pkgload,
readr,

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure the test is worth adding to suggests, though we will get rid of that annoying xref error if we do.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting committer review Awaiting committer review labels Jul 21, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 23, 2024
@jonkeane

Copy link
Copy Markdown
MemberAuthor

@ursabot please benchmark

@ursabot

Copy link
Copy Markdown

Benchmark runs are scheduled for commit 2a0da1e. Watch https://buildkite.com/apache-arrow and https://conbench.ursa.dev for updates. A comment will be posted here when the runs are complete.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting change review Awaiting change review labels Jul 23, 2024
Comment threadr/src/arrow_cpp11.h Outdated
// ensure that x is not actually altrep first
if (ALTREP(x)) {
x = PROTECT(Rf_duplicate(x));
UNPROTECT(1);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure this isn't right, doesn't the UNPROTECT need to go after you're done using the thing you PROTECTed?

I'm also not sure how the C-level PROTECT stuff interacts with unwind_protect.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, I'm surprised I don't get any stack imbalance issues with it. I do get them if I don't have UNPROTECT or I call it unconditionally. But anyway I have a slightly different version incoming.

@conbench-apache-arrow

Copy link
Copy Markdown

Thanks for your patience. Conbench analyzed the 4 benchmarking runs that have been run so far on PR commit 2a0da1e.

There were 38 benchmark results with an error:

There were 11 benchmark results indicating a performance regression:

The full Conbench report has more details.

@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 23, 2024
@jonkeane

Copy link
Copy Markdown
MemberAuthor

@github-actions crossbow submit -g r

@github-actions

Copy link
Copy Markdown

Revision: 206e94d

Submitted crossbow builds: ursacomputing/crossbow @ actions-6de7c6d005

TaskStatus
r-binary-packagesGitHub Actions
test-r-arrow-backwards-compatibilityGitHub Actions
test-r-clang-sanitizerGitHub Actions
test-r-depsource-bundledAzure
test-r-depsource-systemGitHub Actions
test-r-dev-duckdbGitHub Actions
test-r-devdocsGitHub Actions
test-r-gcc-11GitHub Actions
test-r-gcc-12GitHub Actions
test-r-install-localGitHub Actions
test-r-install-local-minsizerelGitHub Actions
test-r-linux-as-cranGitHub Actions
test-r-linux-rchkGitHub Actions
test-r-linux-valgrindGitHub Actions
test-r-minimal-buildAzure
test-r-offline-maximalGitHub Actions
test-r-offline-minimalAzure
test-r-rhub-debian-gcc-devel-lto-latestAzure
test-r-rhub-debian-gcc-release-custom-ccacheAzure
test-r-rhub-ubuntu-release-latestAzure
test-r-rocker-r-ver-latestAzure
test-r-rstudio-r-base-4.1-opensuse155Azure
test-r-rstudio-r-base-4.2-focalAzure
test-r-ubuntu-22.04GitHub Actions
test-r-versionsGitHub Actions
test-ubuntu-r-sanitizerGitHub Actions

Comment threadr/src/arrow_cpp11.h
// ensure that x is not actually altrep first
bool was_altrep = ALTREP(x);
if (was_altrep) {
x = PROTECT(Rf_duplicate(x));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment about why we have to duplicate?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'll expand what I have there

Comment threadr/src/arrow_cpp11.h
@@ -152,6 +157,9 @@ inline SEXP utf8_strings(SEXP x) {
SET_STRING_ELT(x, i, Rf_mkCharCE(Rf_translateCharUTF8(s), CE_UTF8));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we want to check whether Rf_translateCharUTF8() actually modified anything? Or do we trust that SET_STRING_ELT is a no-op in that case? I would imagine that in most cases, we already have ascii/utf-8 strings, so this whole function should be basically free. That should be easily verified by microbenchmarking.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this from above?

SEXP new_s = Rf_translateCharUTF8(s);
if (new_s == s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(new_s, CE_UTF8));
}

Yeah, that's probably good. It'll also make this slightly more inline with what was there before (checking that not utf8)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried this, but after getting it to work with a bit of type faffing, doing the translate first we get errors with our utf string tests again.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting change review Awaiting change review labels Jul 24, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes awaiting change review Awaiting change review labels Jul 25, 2024
@github-actionsgithub-actionsBot added the awaiting changes Awaiting changes label Jul 25, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 27, 2024
@jonkeane
jonkeane merged commit 187197c into apache:mainJul 27, 2024
@jonkeanejonkeane removed the awaiting change review Awaiting change review label Jul 27, 2024
jonkeane added a commit that referenced this pull request Jul 27, 2024
### Rationale for this change
To resolve the reverse dependency issue with `parquetize`
### What changes are included in this PR?
One step towards resolving the issue
### Are these changes tested?
yes
### Are there any user-facing changes?
no
* GitHub Issue: #43349
Authored-by: Jonathan Keane <jkeane@gmail.com>
Signed-off-by: Jonathan Keane <jkeane@gmail.com>
@conbench-apache-arrow

Copy link
Copy Markdown

After merging your PR, Conbench analyzed the 4 benchmarking runs that have been run so far on merge-commit 187197c.

There were no benchmark performance regressions. 🎉

The full Conbench report has more details. It also includes information about 28 possible false positives for unstable benchmarks that are known to sometimes produce them.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jonkeane@ursabot@gaborcsardi@nealrichardson
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

GH-43349: [R] Fix altrep string columns from readr - #43351

Merged
jonkeane merged 6 commits into
apache:mainfrom
jonkeane:lazy_readr
Jul 27, 2024
Merged

GH-43349: [R] Fix altrep string columns from readr#43351
jonkeane merged 6 commits into
apache:mainfrom
jonkeane:lazy_readr

Conversation

@jonkeane

@jonkeanejonkeane commented Jul 21, 2024

Copy link
Copy Markdown
Member

Rationale for this change

To resolve the reverse dependency issue with parquetize

What changes are included in this PR?

One step towards resolving the issue

Are these changes tested?

yes

Are there any user-facing changes?

no

@jonkeane
jonkeane requested a review from thisisnic as a code ownerJuly 21, 2024 02:46
@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #43349has been automatically assigned in GitHub to PR creator.

Comment threadr/src/arrow_cpp11.h Outdated
for (R_xlen_t i = 0; i < n; i++, ++p_x) {
SEXP s = *p_x;
if (s != NA_STRING) {
if (s != NA_STRING && ALTREP(s)) {

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not actually work yet. With this

test_that("We handle non-UTF strings", {
x<- iconv("Veitingastaðir", to="latin1")
df<-tibble::tibble(
chr=x,
fct= as.factor(x)
)
names(df) <- iconv(paste(x, names(df), sep="_"), to="latin1")
df_struct<-tibble::tibble(a=df)
raw_schema<-list(utf8(), dictionary(int8(), utf8()))
names(raw_schema) <- names(df)
# Confirm setup
expect_identical(Encoding(x), "latin1")
expect_identical(Encoding(names(df)), c("latin1", "latin1"))
expect_identical(Encoding(df[[1]]), "latin1")
expect_identical(Encoding(levels(df[[2]])), "latin1")
# Array
expect_identical(as.vector(Array$create(x)), x)
fail.

Before #43173 this line was:

if (s != NA_STRING && !IS_UTF8(s) && !IS_ASCII(s)) {

I suspect what's going on is that we caught vroom altrep vectors with this if and called SET_STRING_ELT so we didn't get the No Set_elt found for ALTSTRING error. But which ALTREP(s) we detect the non-utf strings here too and attempt to SET_STRING_ELT when we shouldn't.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any thoughts @nealrichardson ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I don't understand what's happening in here. L144 says that this should no longer be altrep by the time we get here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think that STRING_PTR_RO() should materialize an ALTREP character vector, otherwise you can't iterate over the CHARSXP pointers.

Also, ALTREP(s) does not seem to make sense to me, that's a CHARSXP (not a STRSXP), so it cannot be ALTREP, unless I am misremembering something.

Are you trying to catch the non-UTF-8 strings here? Rf_translateCharUTF8() does not do anything (returns the same const char *) if the string is UTF-8, so you could always call it, and only call SET_STRING_ELT() if the returned pointer is different?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think that STRING_PTR_RO() should materialize an ALTREP character vector, otherwise you can't iterate over the CHARSXP pointers.

Hmm, so maybe the actual problem here is that that isn't working on the ALTREP vectors coming from vroom? Here is the error we started seeing when we changed this condition from s != NA_STRING && !IS_UTF8(s) && !IS_ASCII(s) to s != NA_STRING:

Error in Table__from_dots(dots, schema, option_use_threads()): No Set_elt found for ALTSTRING class [class: vroom_chr, pkg: vroom]

Also, ALTREP(s) does not seem to make sense to me, that's a CHARSXP (not a STRSXP), so it cannot be ALTREP, unless I am misremembering something.

Yeah, I agree that ALTREP(s) here is not right.

Are you trying to catch the non-UTF-8 strings here? Rf_translateCharUTF8() does not do anything (returns the same const char *) if the string is UTF-8, so you could always call it,

I will admit I'm not 100% certain what is going on here, but I catching non-UTF-8 strings here is what I thought it was doing.

and only call SET_STRING_ELT() if the returned pointer is different?

Oh interesting. Forgive my C++ naïveness, but would this be something like (i.e. does != check if the pointer is different>):

SEXP new_s = Rf_mkCharCE(Rf_translateCharUTF8(s);
if (new_s != s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(Rf_translateCharUTF8(new_s), CE_UTF8));
}

@gaborcsardigaborcsardiJul 22, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that C++ looks correct, except you don't need to translate twice:

SEXP new_s = Rf_mkCharCE(Rf_translateCharUTF8(s);
if (new_s != s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(new_s));
}

However, I think the issue is that if x is ALTREP, then it does not matter if it is materialized of not, it is still ALTREP, so you still cannot change it with SET_STRING_ELT.

To avoid this, you'd need to call Rf_duplicate() on x. That should create a non-altrep copy of it.

You can always call Rf_duplicate(), or you can call it when you encounter a non-utf8 string. Note that you need to PROTECT() the result of Rf_duplicate()

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aaah thanks for those pointers! That looked like it worked, I'm going to trigger a benchmark run to see if there are any unintended consequences there (Hopefully no, but even if so, we might need to accept them!)

Comment threadr/DESCRIPTION Outdated
lubridate,
pillar,
pkgload,
readr,

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure the test is worth adding to suggests, though we will get rid of that annoying xref error if we do.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting committer review Awaiting committer review labels Jul 21, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 23, 2024
@jonkeane

Copy link
Copy Markdown
MemberAuthor

@ursabot please benchmark

@ursabot

Copy link
Copy Markdown

Benchmark runs are scheduled for commit 2a0da1e. Watch https://buildkite.com/apache-arrow and https://conbench.ursa.dev for updates. A comment will be posted here when the runs are complete.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting change review Awaiting change review labels Jul 23, 2024
Comment threadr/src/arrow_cpp11.h Outdated
// ensure that x is not actually altrep first
if (ALTREP(x)) {
x = PROTECT(Rf_duplicate(x));
UNPROTECT(1);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure this isn't right, doesn't the UNPROTECT need to go after you're done using the thing you PROTECTed?

I'm also not sure how the C-level PROTECT stuff interacts with unwind_protect.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, I'm surprised I don't get any stack imbalance issues with it. I do get them if I don't have UNPROTECT or I call it unconditionally. But anyway I have a slightly different version incoming.

@conbench-apache-arrow

Copy link
Copy Markdown

Thanks for your patience. Conbench analyzed the 4 benchmarking runs that have been run so far on PR commit 2a0da1e.

There were 38 benchmark results with an error:

There were 11 benchmark results indicating a performance regression:

The full Conbench report has more details.

@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 23, 2024
@jonkeane

Copy link
Copy Markdown
MemberAuthor

@github-actions crossbow submit -g r

@github-actions

Copy link
Copy Markdown

Revision: 206e94d

Submitted crossbow builds: ursacomputing/crossbow @ actions-6de7c6d005

TaskStatus
r-binary-packagesGitHub Actions
test-r-arrow-backwards-compatibilityGitHub Actions
test-r-clang-sanitizerGitHub Actions
test-r-depsource-bundledAzure
test-r-depsource-systemGitHub Actions
test-r-dev-duckdbGitHub Actions
test-r-devdocsGitHub Actions
test-r-gcc-11GitHub Actions
test-r-gcc-12GitHub Actions
test-r-install-localGitHub Actions
test-r-install-local-minsizerelGitHub Actions
test-r-linux-as-cranGitHub Actions
test-r-linux-rchkGitHub Actions
test-r-linux-valgrindGitHub Actions
test-r-minimal-buildAzure
test-r-offline-maximalGitHub Actions
test-r-offline-minimalAzure
test-r-rhub-debian-gcc-devel-lto-latestAzure
test-r-rhub-debian-gcc-release-custom-ccacheAzure
test-r-rhub-ubuntu-release-latestAzure
test-r-rocker-r-ver-latestAzure
test-r-rstudio-r-base-4.1-opensuse155Azure
test-r-rstudio-r-base-4.2-focalAzure
test-r-ubuntu-22.04GitHub Actions
test-r-versionsGitHub Actions
test-ubuntu-r-sanitizerGitHub Actions

Comment threadr/src/arrow_cpp11.h
// ensure that x is not actually altrep first
bool was_altrep = ALTREP(x);
if (was_altrep) {
x = PROTECT(Rf_duplicate(x));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment about why we have to duplicate?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'll expand what I have there

Comment threadr/src/arrow_cpp11.h
@@ -152,6 +157,9 @@ inline SEXP utf8_strings(SEXP x) {
SET_STRING_ELT(x, i, Rf_mkCharCE(Rf_translateCharUTF8(s), CE_UTF8));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we want to check whether Rf_translateCharUTF8() actually modified anything? Or do we trust that SET_STRING_ELT is a no-op in that case? I would imagine that in most cases, we already have ascii/utf-8 strings, so this whole function should be basically free. That should be easily verified by microbenchmarking.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this from above?

SEXP new_s = Rf_translateCharUTF8(s);
if (new_s == s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(new_s, CE_UTF8));
}

Yeah, that's probably good. It'll also make this slightly more inline with what was there before (checking that not utf8)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried this, but after getting it to work with a bit of type faffing, doing the translate first we get errors with our utf string tests again.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting change review Awaiting change review labels Jul 24, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes awaiting change review Awaiting change review labels Jul 25, 2024
@github-actionsgithub-actionsBot added the awaiting changes Awaiting changes label Jul 25, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 27, 2024
@jonkeane
jonkeane merged commit 187197c into apache:mainJul 27, 2024
@jonkeanejonkeane removed the awaiting change review Awaiting change review label Jul 27, 2024
jonkeane added a commit that referenced this pull request Jul 27, 2024
### Rationale for this change
To resolve the reverse dependency issue with `parquetize`
### What changes are included in this PR?
One step towards resolving the issue
### Are these changes tested?
yes
### Are there any user-facing changes?
no
* GitHub Issue: #43349
Authored-by: Jonathan Keane <jkeane@gmail.com>
Signed-off-by: Jonathan Keane <jkeane@gmail.com>
@conbench-apache-arrow

Copy link
Copy Markdown

After merging your PR, Conbench analyzed the 4 benchmarking runs that have been run so far on merge-commit 187197c.

There were no benchmark performance regressions. 🎉

The full Conbench report has more details. It also includes information about 28 possible false positives for unstable benchmarks that are known to sometimes produce them.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jonkeane@ursabot@gaborcsardi@nealrichardson
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

GH-43349: [R] Fix altrep string columns from readr - #43351

Merged
jonkeane merged 6 commits into
apache:mainfrom
jonkeane:lazy_readr
Jul 27, 2024
Merged

GH-43349: [R] Fix altrep string columns from readr#43351
jonkeane merged 6 commits into
apache:mainfrom
jonkeane:lazy_readr

Conversation

@jonkeane

@jonkeanejonkeane commented Jul 21, 2024

Copy link
Copy Markdown
Member

Rationale for this change

To resolve the reverse dependency issue with parquetize

What changes are included in this PR?

One step towards resolving the issue

Are these changes tested?

yes

Are there any user-facing changes?

no

@jonkeane
jonkeane requested a review from thisisnic as a code ownerJuly 21, 2024 02:46
@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #43349has been automatically assigned in GitHub to PR creator.

Comment threadr/src/arrow_cpp11.h Outdated
for (R_xlen_t i = 0; i < n; i++, ++p_x) {
SEXP s = *p_x;
if (s != NA_STRING) {
if (s != NA_STRING && ALTREP(s)) {

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not actually work yet. With this

test_that("We handle non-UTF strings", {
x<- iconv("Veitingastaðir", to="latin1")
df<-tibble::tibble(
chr=x,
fct= as.factor(x)
)
names(df) <- iconv(paste(x, names(df), sep="_"), to="latin1")
df_struct<-tibble::tibble(a=df)
raw_schema<-list(utf8(), dictionary(int8(), utf8()))
names(raw_schema) <- names(df)
# Confirm setup
expect_identical(Encoding(x), "latin1")
expect_identical(Encoding(names(df)), c("latin1", "latin1"))
expect_identical(Encoding(df[[1]]), "latin1")
expect_identical(Encoding(levels(df[[2]])), "latin1")
# Array
expect_identical(as.vector(Array$create(x)), x)
fail.

Before #43173 this line was:

if (s != NA_STRING && !IS_UTF8(s) && !IS_ASCII(s)) {

I suspect what's going on is that we caught vroom altrep vectors with this if and called SET_STRING_ELT so we didn't get the No Set_elt found for ALTSTRING error. But which ALTREP(s) we detect the non-utf strings here too and attempt to SET_STRING_ELT when we shouldn't.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any thoughts @nealrichardson ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I don't understand what's happening in here. L144 says that this should no longer be altrep by the time we get here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think that STRING_PTR_RO() should materialize an ALTREP character vector, otherwise you can't iterate over the CHARSXP pointers.

Also, ALTREP(s) does not seem to make sense to me, that's a CHARSXP (not a STRSXP), so it cannot be ALTREP, unless I am misremembering something.

Are you trying to catch the non-UTF-8 strings here? Rf_translateCharUTF8() does not do anything (returns the same const char *) if the string is UTF-8, so you could always call it, and only call SET_STRING_ELT() if the returned pointer is different?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think that STRING_PTR_RO() should materialize an ALTREP character vector, otherwise you can't iterate over the CHARSXP pointers.

Hmm, so maybe the actual problem here is that that isn't working on the ALTREP vectors coming from vroom? Here is the error we started seeing when we changed this condition from s != NA_STRING && !IS_UTF8(s) && !IS_ASCII(s) to s != NA_STRING:

Error in Table__from_dots(dots, schema, option_use_threads()): No Set_elt found for ALTSTRING class [class: vroom_chr, pkg: vroom]

Also, ALTREP(s) does not seem to make sense to me, that's a CHARSXP (not a STRSXP), so it cannot be ALTREP, unless I am misremembering something.

Yeah, I agree that ALTREP(s) here is not right.

Are you trying to catch the non-UTF-8 strings here? Rf_translateCharUTF8() does not do anything (returns the same const char *) if the string is UTF-8, so you could always call it,

I will admit I'm not 100% certain what is going on here, but I catching non-UTF-8 strings here is what I thought it was doing.

and only call SET_STRING_ELT() if the returned pointer is different?

Oh interesting. Forgive my C++ naïveness, but would this be something like (i.e. does != check if the pointer is different>):

SEXP new_s = Rf_mkCharCE(Rf_translateCharUTF8(s);
if (new_s != s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(Rf_translateCharUTF8(new_s), CE_UTF8));
}

@gaborcsardigaborcsardiJul 22, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that C++ looks correct, except you don't need to translate twice:

SEXP new_s = Rf_mkCharCE(Rf_translateCharUTF8(s);
if (new_s != s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(new_s));
}

However, I think the issue is that if x is ALTREP, then it does not matter if it is materialized of not, it is still ALTREP, so you still cannot change it with SET_STRING_ELT.

To avoid this, you'd need to call Rf_duplicate() on x. That should create a non-altrep copy of it.

You can always call Rf_duplicate(), or you can call it when you encounter a non-utf8 string. Note that you need to PROTECT() the result of Rf_duplicate()

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aaah thanks for those pointers! That looked like it worked, I'm going to trigger a benchmark run to see if there are any unintended consequences there (Hopefully no, but even if so, we might need to accept them!)

Comment threadr/DESCRIPTION Outdated
lubridate,
pillar,
pkgload,
readr,

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure the test is worth adding to suggests, though we will get rid of that annoying xref error if we do.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting committer review Awaiting committer review labels Jul 21, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 23, 2024
@jonkeane

Copy link
Copy Markdown
MemberAuthor

@ursabot please benchmark

@ursabot

Copy link
Copy Markdown

Benchmark runs are scheduled for commit 2a0da1e. Watch https://buildkite.com/apache-arrow and https://conbench.ursa.dev for updates. A comment will be posted here when the runs are complete.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting change review Awaiting change review labels Jul 23, 2024
Comment threadr/src/arrow_cpp11.h Outdated
// ensure that x is not actually altrep first
if (ALTREP(x)) {
x = PROTECT(Rf_duplicate(x));
UNPROTECT(1);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure this isn't right, doesn't the UNPROTECT need to go after you're done using the thing you PROTECTed?

I'm also not sure how the C-level PROTECT stuff interacts with unwind_protect.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, I'm surprised I don't get any stack imbalance issues with it. I do get them if I don't have UNPROTECT or I call it unconditionally. But anyway I have a slightly different version incoming.

@conbench-apache-arrow

Copy link
Copy Markdown

Thanks for your patience. Conbench analyzed the 4 benchmarking runs that have been run so far on PR commit 2a0da1e.

There were 38 benchmark results with an error:

There were 11 benchmark results indicating a performance regression:

The full Conbench report has more details.

@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 23, 2024
@jonkeane

Copy link
Copy Markdown
MemberAuthor

@github-actions crossbow submit -g r

@github-actions

Copy link
Copy Markdown

Revision: 206e94d

Submitted crossbow builds: ursacomputing/crossbow @ actions-6de7c6d005

TaskStatus
r-binary-packagesGitHub Actions
test-r-arrow-backwards-compatibilityGitHub Actions
test-r-clang-sanitizerGitHub Actions
test-r-depsource-bundledAzure
test-r-depsource-systemGitHub Actions
test-r-dev-duckdbGitHub Actions
test-r-devdocsGitHub Actions
test-r-gcc-11GitHub Actions
test-r-gcc-12GitHub Actions
test-r-install-localGitHub Actions
test-r-install-local-minsizerelGitHub Actions
test-r-linux-as-cranGitHub Actions
test-r-linux-rchkGitHub Actions
test-r-linux-valgrindGitHub Actions
test-r-minimal-buildAzure
test-r-offline-maximalGitHub Actions
test-r-offline-minimalAzure
test-r-rhub-debian-gcc-devel-lto-latestAzure
test-r-rhub-debian-gcc-release-custom-ccacheAzure
test-r-rhub-ubuntu-release-latestAzure
test-r-rocker-r-ver-latestAzure
test-r-rstudio-r-base-4.1-opensuse155Azure
test-r-rstudio-r-base-4.2-focalAzure
test-r-ubuntu-22.04GitHub Actions
test-r-versionsGitHub Actions
test-ubuntu-r-sanitizerGitHub Actions

Comment threadr/src/arrow_cpp11.h
// ensure that x is not actually altrep first
bool was_altrep = ALTREP(x);
if (was_altrep) {
x = PROTECT(Rf_duplicate(x));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment about why we have to duplicate?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'll expand what I have there

Comment threadr/src/arrow_cpp11.h
@@ -152,6 +157,9 @@ inline SEXP utf8_strings(SEXP x) {
SET_STRING_ELT(x, i, Rf_mkCharCE(Rf_translateCharUTF8(s), CE_UTF8));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we want to check whether Rf_translateCharUTF8() actually modified anything? Or do we trust that SET_STRING_ELT is a no-op in that case? I would imagine that in most cases, we already have ascii/utf-8 strings, so this whole function should be basically free. That should be easily verified by microbenchmarking.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this from above?

SEXP new_s = Rf_translateCharUTF8(s);
if (new_s == s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(new_s, CE_UTF8));
}

Yeah, that's probably good. It'll also make this slightly more inline with what was there before (checking that not utf8)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried this, but after getting it to work with a bit of type faffing, doing the translate first we get errors with our utf string tests again.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting change review Awaiting change review labels Jul 24, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes awaiting change review Awaiting change review labels Jul 25, 2024
@github-actionsgithub-actionsBot added the awaiting changes Awaiting changes label Jul 25, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 27, 2024
@jonkeane
jonkeane merged commit 187197c into apache:mainJul 27, 2024
@jonkeanejonkeane removed the awaiting change review Awaiting change review label Jul 27, 2024
jonkeane added a commit that referenced this pull request Jul 27, 2024
### Rationale for this change
To resolve the reverse dependency issue with `parquetize`
### What changes are included in this PR?
One step towards resolving the issue
### Are these changes tested?
yes
### Are there any user-facing changes?
no
* GitHub Issue: #43349
Authored-by: Jonathan Keane <jkeane@gmail.com>
Signed-off-by: Jonathan Keane <jkeane@gmail.com>
@conbench-apache-arrow

Copy link
Copy Markdown

After merging your PR, Conbench analyzed the 4 benchmarking runs that have been run so far on merge-commit 187197c.

There were no benchmark performance regressions. 🎉

The full Conbench report has more details. It also includes information about 28 possible false positives for unstable benchmarks that are known to sometimes produce them.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jonkeane@ursabot@gaborcsardi@nealrichardson
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

GH-43349: [R] Fix altrep string columns from readr - #43351

Merged
jonkeane merged 6 commits into
apache:mainfrom
jonkeane:lazy_readr
Jul 27, 2024
Merged

GH-43349: [R] Fix altrep string columns from readr#43351
jonkeane merged 6 commits into
apache:mainfrom
jonkeane:lazy_readr

Conversation

@jonkeane

@jonkeanejonkeane commented Jul 21, 2024

Copy link
Copy Markdown
Member

Rationale for this change

To resolve the reverse dependency issue with parquetize

What changes are included in this PR?

One step towards resolving the issue

Are these changes tested?

yes

Are there any user-facing changes?

no

@jonkeane
jonkeane requested a review from thisisnic as a code ownerJuly 21, 2024 02:46
@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #43349has been automatically assigned in GitHub to PR creator.

Comment threadr/src/arrow_cpp11.h Outdated
for (R_xlen_t i = 0; i < n; i++, ++p_x) {
SEXP s = *p_x;
if (s != NA_STRING) {
if (s != NA_STRING && ALTREP(s)) {

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not actually work yet. With this

test_that("We handle non-UTF strings", {
x<- iconv("Veitingastaðir", to="latin1")
df<-tibble::tibble(
chr=x,
fct= as.factor(x)
)
names(df) <- iconv(paste(x, names(df), sep="_"), to="latin1")
df_struct<-tibble::tibble(a=df)
raw_schema<-list(utf8(), dictionary(int8(), utf8()))
names(raw_schema) <- names(df)
# Confirm setup
expect_identical(Encoding(x), "latin1")
expect_identical(Encoding(names(df)), c("latin1", "latin1"))
expect_identical(Encoding(df[[1]]), "latin1")
expect_identical(Encoding(levels(df[[2]])), "latin1")
# Array
expect_identical(as.vector(Array$create(x)), x)
fail.

Before #43173 this line was:

if (s != NA_STRING && !IS_UTF8(s) && !IS_ASCII(s)) {

I suspect what's going on is that we caught vroom altrep vectors with this if and called SET_STRING_ELT so we didn't get the No Set_elt found for ALTSTRING error. But which ALTREP(s) we detect the non-utf strings here too and attempt to SET_STRING_ELT when we shouldn't.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any thoughts @nealrichardson ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I don't understand what's happening in here. L144 says that this should no longer be altrep by the time we get here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think that STRING_PTR_RO() should materialize an ALTREP character vector, otherwise you can't iterate over the CHARSXP pointers.

Also, ALTREP(s) does not seem to make sense to me, that's a CHARSXP (not a STRSXP), so it cannot be ALTREP, unless I am misremembering something.

Are you trying to catch the non-UTF-8 strings here? Rf_translateCharUTF8() does not do anything (returns the same const char *) if the string is UTF-8, so you could always call it, and only call SET_STRING_ELT() if the returned pointer is different?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think that STRING_PTR_RO() should materialize an ALTREP character vector, otherwise you can't iterate over the CHARSXP pointers.

Hmm, so maybe the actual problem here is that that isn't working on the ALTREP vectors coming from vroom? Here is the error we started seeing when we changed this condition from s != NA_STRING && !IS_UTF8(s) && !IS_ASCII(s) to s != NA_STRING:

Error in Table__from_dots(dots, schema, option_use_threads()): No Set_elt found for ALTSTRING class [class: vroom_chr, pkg: vroom]

Also, ALTREP(s) does not seem to make sense to me, that's a CHARSXP (not a STRSXP), so it cannot be ALTREP, unless I am misremembering something.

Yeah, I agree that ALTREP(s) here is not right.

Are you trying to catch the non-UTF-8 strings here? Rf_translateCharUTF8() does not do anything (returns the same const char *) if the string is UTF-8, so you could always call it,

I will admit I'm not 100% certain what is going on here, but I catching non-UTF-8 strings here is what I thought it was doing.

and only call SET_STRING_ELT() if the returned pointer is different?

Oh interesting. Forgive my C++ naïveness, but would this be something like (i.e. does != check if the pointer is different>):

SEXP new_s = Rf_mkCharCE(Rf_translateCharUTF8(s);
if (new_s != s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(Rf_translateCharUTF8(new_s), CE_UTF8));
}

@gaborcsardigaborcsardiJul 22, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that C++ looks correct, except you don't need to translate twice:

SEXP new_s = Rf_mkCharCE(Rf_translateCharUTF8(s);
if (new_s != s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(new_s));
}

However, I think the issue is that if x is ALTREP, then it does not matter if it is materialized of not, it is still ALTREP, so you still cannot change it with SET_STRING_ELT.

To avoid this, you'd need to call Rf_duplicate() on x. That should create a non-altrep copy of it.

You can always call Rf_duplicate(), or you can call it when you encounter a non-utf8 string. Note that you need to PROTECT() the result of Rf_duplicate()

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aaah thanks for those pointers! That looked like it worked, I'm going to trigger a benchmark run to see if there are any unintended consequences there (Hopefully no, but even if so, we might need to accept them!)

Comment threadr/DESCRIPTION Outdated
lubridate,
pillar,
pkgload,
readr,

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure the test is worth adding to suggests, though we will get rid of that annoying xref error if we do.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting committer review Awaiting committer review labels Jul 21, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 23, 2024
@jonkeane

Copy link
Copy Markdown
MemberAuthor

@ursabot please benchmark

@ursabot

Copy link
Copy Markdown

Benchmark runs are scheduled for commit 2a0da1e. Watch https://buildkite.com/apache-arrow and https://conbench.ursa.dev for updates. A comment will be posted here when the runs are complete.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting change review Awaiting change review labels Jul 23, 2024
Comment threadr/src/arrow_cpp11.h Outdated
// ensure that x is not actually altrep first
if (ALTREP(x)) {
x = PROTECT(Rf_duplicate(x));
UNPROTECT(1);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure this isn't right, doesn't the UNPROTECT need to go after you're done using the thing you PROTECTed?

I'm also not sure how the C-level PROTECT stuff interacts with unwind_protect.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, I'm surprised I don't get any stack imbalance issues with it. I do get them if I don't have UNPROTECT or I call it unconditionally. But anyway I have a slightly different version incoming.

@conbench-apache-arrow

Copy link
Copy Markdown

Thanks for your patience. Conbench analyzed the 4 benchmarking runs that have been run so far on PR commit 2a0da1e.

There were 38 benchmark results with an error:

There were 11 benchmark results indicating a performance regression:

The full Conbench report has more details.

@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 23, 2024
@jonkeane

Copy link
Copy Markdown
MemberAuthor

@github-actions crossbow submit -g r

@github-actions

Copy link
Copy Markdown

Revision: 206e94d

Submitted crossbow builds: ursacomputing/crossbow @ actions-6de7c6d005

TaskStatus
r-binary-packagesGitHub Actions
test-r-arrow-backwards-compatibilityGitHub Actions
test-r-clang-sanitizerGitHub Actions
test-r-depsource-bundledAzure
test-r-depsource-systemGitHub Actions
test-r-dev-duckdbGitHub Actions
test-r-devdocsGitHub Actions
test-r-gcc-11GitHub Actions
test-r-gcc-12GitHub Actions
test-r-install-localGitHub Actions
test-r-install-local-minsizerelGitHub Actions
test-r-linux-as-cranGitHub Actions
test-r-linux-rchkGitHub Actions
test-r-linux-valgrindGitHub Actions
test-r-minimal-buildAzure
test-r-offline-maximalGitHub Actions
test-r-offline-minimalAzure
test-r-rhub-debian-gcc-devel-lto-latestAzure
test-r-rhub-debian-gcc-release-custom-ccacheAzure
test-r-rhub-ubuntu-release-latestAzure
test-r-rocker-r-ver-latestAzure
test-r-rstudio-r-base-4.1-opensuse155Azure
test-r-rstudio-r-base-4.2-focalAzure
test-r-ubuntu-22.04GitHub Actions
test-r-versionsGitHub Actions
test-ubuntu-r-sanitizerGitHub Actions

Comment threadr/src/arrow_cpp11.h
// ensure that x is not actually altrep first
bool was_altrep = ALTREP(x);
if (was_altrep) {
x = PROTECT(Rf_duplicate(x));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment about why we have to duplicate?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'll expand what I have there

Comment threadr/src/arrow_cpp11.h
@@ -152,6 +157,9 @@ inline SEXP utf8_strings(SEXP x) {
SET_STRING_ELT(x, i, Rf_mkCharCE(Rf_translateCharUTF8(s), CE_UTF8));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we want to check whether Rf_translateCharUTF8() actually modified anything? Or do we trust that SET_STRING_ELT is a no-op in that case? I would imagine that in most cases, we already have ascii/utf-8 strings, so this whole function should be basically free. That should be easily verified by microbenchmarking.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this from above?

SEXP new_s = Rf_translateCharUTF8(s);
if (new_s == s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(new_s, CE_UTF8));
}

Yeah, that's probably good. It'll also make this slightly more inline with what was there before (checking that not utf8)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried this, but after getting it to work with a bit of type faffing, doing the translate first we get errors with our utf string tests again.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting change review Awaiting change review labels Jul 24, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes awaiting change review Awaiting change review labels Jul 25, 2024
@github-actionsgithub-actionsBot added the awaiting changes Awaiting changes label Jul 25, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 27, 2024
@jonkeane
jonkeane merged commit 187197c into apache:mainJul 27, 2024
@jonkeanejonkeane removed the awaiting change review Awaiting change review label Jul 27, 2024
jonkeane added a commit that referenced this pull request Jul 27, 2024
### Rationale for this change
To resolve the reverse dependency issue with `parquetize`
### What changes are included in this PR?
One step towards resolving the issue
### Are these changes tested?
yes
### Are there any user-facing changes?
no
* GitHub Issue: #43349
Authored-by: Jonathan Keane <jkeane@gmail.com>
Signed-off-by: Jonathan Keane <jkeane@gmail.com>
@conbench-apache-arrow

Copy link
Copy Markdown

After merging your PR, Conbench analyzed the 4 benchmarking runs that have been run so far on merge-commit 187197c.

There were no benchmark performance regressions. 🎉

The full Conbench report has more details. It also includes information about 28 possible false positives for unstable benchmarks that are known to sometimes produce them.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jonkeane@ursabot@gaborcsardi@nealrichardson
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

GH-43349: [R] Fix altrep string columns from readr - #43351

Merged
jonkeane merged 6 commits into
apache:mainfrom
jonkeane:lazy_readr
Jul 27, 2024
Merged

GH-43349: [R] Fix altrep string columns from readr#43351
jonkeane merged 6 commits into
apache:mainfrom
jonkeane:lazy_readr

Conversation

@jonkeane

@jonkeanejonkeane commented Jul 21, 2024

Copy link
Copy Markdown
Member

Rationale for this change

To resolve the reverse dependency issue with parquetize

What changes are included in this PR?

One step towards resolving the issue

Are these changes tested?

yes

Are there any user-facing changes?

no

@jonkeane
jonkeane requested a review from thisisnic as a code ownerJuly 21, 2024 02:46
@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #43349has been automatically assigned in GitHub to PR creator.

Comment threadr/src/arrow_cpp11.h Outdated
for (R_xlen_t i = 0; i < n; i++, ++p_x) {
SEXP s = *p_x;
if (s != NA_STRING) {
if (s != NA_STRING && ALTREP(s)) {

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not actually work yet. With this

test_that("We handle non-UTF strings", {
x<- iconv("Veitingastaðir", to="latin1")
df<-tibble::tibble(
chr=x,
fct= as.factor(x)
)
names(df) <- iconv(paste(x, names(df), sep="_"), to="latin1")
df_struct<-tibble::tibble(a=df)
raw_schema<-list(utf8(), dictionary(int8(), utf8()))
names(raw_schema) <- names(df)
# Confirm setup
expect_identical(Encoding(x), "latin1")
expect_identical(Encoding(names(df)), c("latin1", "latin1"))
expect_identical(Encoding(df[[1]]), "latin1")
expect_identical(Encoding(levels(df[[2]])), "latin1")
# Array
expect_identical(as.vector(Array$create(x)), x)
fail.

Before #43173 this line was:

if (s != NA_STRING && !IS_UTF8(s) && !IS_ASCII(s)) {

I suspect what's going on is that we caught vroom altrep vectors with this if and called SET_STRING_ELT so we didn't get the No Set_elt found for ALTSTRING error. But which ALTREP(s) we detect the non-utf strings here too and attempt to SET_STRING_ELT when we shouldn't.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any thoughts @nealrichardson ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I don't understand what's happening in here. L144 says that this should no longer be altrep by the time we get here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think that STRING_PTR_RO() should materialize an ALTREP character vector, otherwise you can't iterate over the CHARSXP pointers.

Also, ALTREP(s) does not seem to make sense to me, that's a CHARSXP (not a STRSXP), so it cannot be ALTREP, unless I am misremembering something.

Are you trying to catch the non-UTF-8 strings here? Rf_translateCharUTF8() does not do anything (returns the same const char *) if the string is UTF-8, so you could always call it, and only call SET_STRING_ELT() if the returned pointer is different?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think that STRING_PTR_RO() should materialize an ALTREP character vector, otherwise you can't iterate over the CHARSXP pointers.

Hmm, so maybe the actual problem here is that that isn't working on the ALTREP vectors coming from vroom? Here is the error we started seeing when we changed this condition from s != NA_STRING && !IS_UTF8(s) && !IS_ASCII(s) to s != NA_STRING:

Error in Table__from_dots(dots, schema, option_use_threads()): No Set_elt found for ALTSTRING class [class: vroom_chr, pkg: vroom]

Also, ALTREP(s) does not seem to make sense to me, that's a CHARSXP (not a STRSXP), so it cannot be ALTREP, unless I am misremembering something.

Yeah, I agree that ALTREP(s) here is not right.

Are you trying to catch the non-UTF-8 strings here? Rf_translateCharUTF8() does not do anything (returns the same const char *) if the string is UTF-8, so you could always call it,

I will admit I'm not 100% certain what is going on here, but I catching non-UTF-8 strings here is what I thought it was doing.

and only call SET_STRING_ELT() if the returned pointer is different?

Oh interesting. Forgive my C++ naïveness, but would this be something like (i.e. does != check if the pointer is different>):

SEXP new_s = Rf_mkCharCE(Rf_translateCharUTF8(s);
if (new_s != s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(Rf_translateCharUTF8(new_s), CE_UTF8));
}

@gaborcsardigaborcsardiJul 22, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that C++ looks correct, except you don't need to translate twice:

SEXP new_s = Rf_mkCharCE(Rf_translateCharUTF8(s);
if (new_s != s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(new_s));
}

However, I think the issue is that if x is ALTREP, then it does not matter if it is materialized of not, it is still ALTREP, so you still cannot change it with SET_STRING_ELT.

To avoid this, you'd need to call Rf_duplicate() on x. That should create a non-altrep copy of it.

You can always call Rf_duplicate(), or you can call it when you encounter a non-utf8 string. Note that you need to PROTECT() the result of Rf_duplicate()

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aaah thanks for those pointers! That looked like it worked, I'm going to trigger a benchmark run to see if there are any unintended consequences there (Hopefully no, but even if so, we might need to accept them!)

Comment threadr/DESCRIPTION Outdated
lubridate,
pillar,
pkgload,
readr,

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure the test is worth adding to suggests, though we will get rid of that annoying xref error if we do.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting committer review Awaiting committer review labels Jul 21, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 23, 2024
@jonkeane

Copy link
Copy Markdown
MemberAuthor

@ursabot please benchmark

@ursabot

Copy link
Copy Markdown

Benchmark runs are scheduled for commit 2a0da1e. Watch https://buildkite.com/apache-arrow and https://conbench.ursa.dev for updates. A comment will be posted here when the runs are complete.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting change review Awaiting change review labels Jul 23, 2024
Comment threadr/src/arrow_cpp11.h Outdated
// ensure that x is not actually altrep first
if (ALTREP(x)) {
x = PROTECT(Rf_duplicate(x));
UNPROTECT(1);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure this isn't right, doesn't the UNPROTECT need to go after you're done using the thing you PROTECTed?

I'm also not sure how the C-level PROTECT stuff interacts with unwind_protect.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, I'm surprised I don't get any stack imbalance issues with it. I do get them if I don't have UNPROTECT or I call it unconditionally. But anyway I have a slightly different version incoming.

@conbench-apache-arrow

Copy link
Copy Markdown

Thanks for your patience. Conbench analyzed the 4 benchmarking runs that have been run so far on PR commit 2a0da1e.

There were 38 benchmark results with an error:

There were 11 benchmark results indicating a performance regression:

The full Conbench report has more details.

@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 23, 2024
@jonkeane

Copy link
Copy Markdown
MemberAuthor

@github-actions crossbow submit -g r

@github-actions

Copy link
Copy Markdown

Revision: 206e94d

Submitted crossbow builds: ursacomputing/crossbow @ actions-6de7c6d005

TaskStatus
r-binary-packagesGitHub Actions
test-r-arrow-backwards-compatibilityGitHub Actions
test-r-clang-sanitizerGitHub Actions
test-r-depsource-bundledAzure
test-r-depsource-systemGitHub Actions
test-r-dev-duckdbGitHub Actions
test-r-devdocsGitHub Actions
test-r-gcc-11GitHub Actions
test-r-gcc-12GitHub Actions
test-r-install-localGitHub Actions
test-r-install-local-minsizerelGitHub Actions
test-r-linux-as-cranGitHub Actions
test-r-linux-rchkGitHub Actions
test-r-linux-valgrindGitHub Actions
test-r-minimal-buildAzure
test-r-offline-maximalGitHub Actions
test-r-offline-minimalAzure
test-r-rhub-debian-gcc-devel-lto-latestAzure
test-r-rhub-debian-gcc-release-custom-ccacheAzure
test-r-rhub-ubuntu-release-latestAzure
test-r-rocker-r-ver-latestAzure
test-r-rstudio-r-base-4.1-opensuse155Azure
test-r-rstudio-r-base-4.2-focalAzure
test-r-ubuntu-22.04GitHub Actions
test-r-versionsGitHub Actions
test-ubuntu-r-sanitizerGitHub Actions

Comment threadr/src/arrow_cpp11.h
// ensure that x is not actually altrep first
bool was_altrep = ALTREP(x);
if (was_altrep) {
x = PROTECT(Rf_duplicate(x));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment about why we have to duplicate?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'll expand what I have there

Comment threadr/src/arrow_cpp11.h
@@ -152,6 +157,9 @@ inline SEXP utf8_strings(SEXP x) {
SET_STRING_ELT(x, i, Rf_mkCharCE(Rf_translateCharUTF8(s), CE_UTF8));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we want to check whether Rf_translateCharUTF8() actually modified anything? Or do we trust that SET_STRING_ELT is a no-op in that case? I would imagine that in most cases, we already have ascii/utf-8 strings, so this whole function should be basically free. That should be easily verified by microbenchmarking.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this from above?

SEXP new_s = Rf_translateCharUTF8(s);
if (new_s == s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(new_s, CE_UTF8));
}

Yeah, that's probably good. It'll also make this slightly more inline with what was there before (checking that not utf8)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried this, but after getting it to work with a bit of type faffing, doing the translate first we get errors with our utf string tests again.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting change review Awaiting change review labels Jul 24, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes awaiting change review Awaiting change review labels Jul 25, 2024
@github-actionsgithub-actionsBot added the awaiting changes Awaiting changes label Jul 25, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 27, 2024
@jonkeane
jonkeane merged commit 187197c into apache:mainJul 27, 2024
@jonkeanejonkeane removed the awaiting change review Awaiting change review label Jul 27, 2024
jonkeane added a commit that referenced this pull request Jul 27, 2024
### Rationale for this change
To resolve the reverse dependency issue with `parquetize`
### What changes are included in this PR?
One step towards resolving the issue
### Are these changes tested?
yes
### Are there any user-facing changes?
no
* GitHub Issue: #43349
Authored-by: Jonathan Keane <jkeane@gmail.com>
Signed-off-by: Jonathan Keane <jkeane@gmail.com>
@conbench-apache-arrow

Copy link
Copy Markdown

After merging your PR, Conbench analyzed the 4 benchmarking runs that have been run so far on merge-commit 187197c.

There were no benchmark performance regressions. 🎉

The full Conbench report has more details. It also includes information about 28 possible false positives for unstable benchmarks that are known to sometimes produce them.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jonkeane@ursabot@gaborcsardi@nealrichardson
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

GH-43349: [R] Fix altrep string columns from readr - #43351

Merged
jonkeane merged 6 commits into
apache:mainfrom
jonkeane:lazy_readr
Jul 27, 2024
Merged

GH-43349: [R] Fix altrep string columns from readr#43351
jonkeane merged 6 commits into
apache:mainfrom
jonkeane:lazy_readr

Conversation

@jonkeane

@jonkeanejonkeane commented Jul 21, 2024

Copy link
Copy Markdown
Member

Rationale for this change

To resolve the reverse dependency issue with parquetize

What changes are included in this PR?

One step towards resolving the issue

Are these changes tested?

yes

Are there any user-facing changes?

no

@jonkeane
jonkeane requested a review from thisisnic as a code ownerJuly 21, 2024 02:46
@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #43349has been automatically assigned in GitHub to PR creator.

Comment threadr/src/arrow_cpp11.h Outdated
for (R_xlen_t i = 0; i < n; i++, ++p_x) {
SEXP s = *p_x;
if (s != NA_STRING) {
if (s != NA_STRING && ALTREP(s)) {

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not actually work yet. With this

test_that("We handle non-UTF strings", {
x<- iconv("Veitingastaðir", to="latin1")
df<-tibble::tibble(
chr=x,
fct= as.factor(x)
)
names(df) <- iconv(paste(x, names(df), sep="_"), to="latin1")
df_struct<-tibble::tibble(a=df)
raw_schema<-list(utf8(), dictionary(int8(), utf8()))
names(raw_schema) <- names(df)
# Confirm setup
expect_identical(Encoding(x), "latin1")
expect_identical(Encoding(names(df)), c("latin1", "latin1"))
expect_identical(Encoding(df[[1]]), "latin1")
expect_identical(Encoding(levels(df[[2]])), "latin1")
# Array
expect_identical(as.vector(Array$create(x)), x)
fail.

Before #43173 this line was:

if (s != NA_STRING && !IS_UTF8(s) && !IS_ASCII(s)) {

I suspect what's going on is that we caught vroom altrep vectors with this if and called SET_STRING_ELT so we didn't get the No Set_elt found for ALTSTRING error. But which ALTREP(s) we detect the non-utf strings here too and attempt to SET_STRING_ELT when we shouldn't.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any thoughts @nealrichardson ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I don't understand what's happening in here. L144 says that this should no longer be altrep by the time we get here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think that STRING_PTR_RO() should materialize an ALTREP character vector, otherwise you can't iterate over the CHARSXP pointers.

Also, ALTREP(s) does not seem to make sense to me, that's a CHARSXP (not a STRSXP), so it cannot be ALTREP, unless I am misremembering something.

Are you trying to catch the non-UTF-8 strings here? Rf_translateCharUTF8() does not do anything (returns the same const char *) if the string is UTF-8, so you could always call it, and only call SET_STRING_ELT() if the returned pointer is different?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think that STRING_PTR_RO() should materialize an ALTREP character vector, otherwise you can't iterate over the CHARSXP pointers.

Hmm, so maybe the actual problem here is that that isn't working on the ALTREP vectors coming from vroom? Here is the error we started seeing when we changed this condition from s != NA_STRING && !IS_UTF8(s) && !IS_ASCII(s) to s != NA_STRING:

Error in Table__from_dots(dots, schema, option_use_threads()): No Set_elt found for ALTSTRING class [class: vroom_chr, pkg: vroom]

Also, ALTREP(s) does not seem to make sense to me, that's a CHARSXP (not a STRSXP), so it cannot be ALTREP, unless I am misremembering something.

Yeah, I agree that ALTREP(s) here is not right.

Are you trying to catch the non-UTF-8 strings here? Rf_translateCharUTF8() does not do anything (returns the same const char *) if the string is UTF-8, so you could always call it,

I will admit I'm not 100% certain what is going on here, but I catching non-UTF-8 strings here is what I thought it was doing.

and only call SET_STRING_ELT() if the returned pointer is different?

Oh interesting. Forgive my C++ naïveness, but would this be something like (i.e. does != check if the pointer is different>):

SEXP new_s = Rf_mkCharCE(Rf_translateCharUTF8(s);
if (new_s != s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(Rf_translateCharUTF8(new_s), CE_UTF8));
}

@gaborcsardigaborcsardiJul 22, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that C++ looks correct, except you don't need to translate twice:

SEXP new_s = Rf_mkCharCE(Rf_translateCharUTF8(s);
if (new_s != s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(new_s));
}

However, I think the issue is that if x is ALTREP, then it does not matter if it is materialized of not, it is still ALTREP, so you still cannot change it with SET_STRING_ELT.

To avoid this, you'd need to call Rf_duplicate() on x. That should create a non-altrep copy of it.

You can always call Rf_duplicate(), or you can call it when you encounter a non-utf8 string. Note that you need to PROTECT() the result of Rf_duplicate()

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aaah thanks for those pointers! That looked like it worked, I'm going to trigger a benchmark run to see if there are any unintended consequences there (Hopefully no, but even if so, we might need to accept them!)

Comment threadr/DESCRIPTION Outdated
lubridate,
pillar,
pkgload,
readr,

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure the test is worth adding to suggests, though we will get rid of that annoying xref error if we do.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting committer review Awaiting committer review labels Jul 21, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 23, 2024
@jonkeane

Copy link
Copy Markdown
MemberAuthor

@ursabot please benchmark

@ursabot

Copy link
Copy Markdown

Benchmark runs are scheduled for commit 2a0da1e. Watch https://buildkite.com/apache-arrow and https://conbench.ursa.dev for updates. A comment will be posted here when the runs are complete.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting change review Awaiting change review labels Jul 23, 2024
Comment threadr/src/arrow_cpp11.h Outdated
// ensure that x is not actually altrep first
if (ALTREP(x)) {
x = PROTECT(Rf_duplicate(x));
UNPROTECT(1);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure this isn't right, doesn't the UNPROTECT need to go after you're done using the thing you PROTECTed?

I'm also not sure how the C-level PROTECT stuff interacts with unwind_protect.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, I'm surprised I don't get any stack imbalance issues with it. I do get them if I don't have UNPROTECT or I call it unconditionally. But anyway I have a slightly different version incoming.

@conbench-apache-arrow

Copy link
Copy Markdown

Thanks for your patience. Conbench analyzed the 4 benchmarking runs that have been run so far on PR commit 2a0da1e.

There were 38 benchmark results with an error:

There were 11 benchmark results indicating a performance regression:

The full Conbench report has more details.

@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 23, 2024
@jonkeane

Copy link
Copy Markdown
MemberAuthor

@github-actions crossbow submit -g r

@github-actions

Copy link
Copy Markdown

Revision: 206e94d

Submitted crossbow builds: ursacomputing/crossbow @ actions-6de7c6d005

TaskStatus
r-binary-packagesGitHub Actions
test-r-arrow-backwards-compatibilityGitHub Actions
test-r-clang-sanitizerGitHub Actions
test-r-depsource-bundledAzure
test-r-depsource-systemGitHub Actions
test-r-dev-duckdbGitHub Actions
test-r-devdocsGitHub Actions
test-r-gcc-11GitHub Actions
test-r-gcc-12GitHub Actions
test-r-install-localGitHub Actions
test-r-install-local-minsizerelGitHub Actions
test-r-linux-as-cranGitHub Actions
test-r-linux-rchkGitHub Actions
test-r-linux-valgrindGitHub Actions
test-r-minimal-buildAzure
test-r-offline-maximalGitHub Actions
test-r-offline-minimalAzure
test-r-rhub-debian-gcc-devel-lto-latestAzure
test-r-rhub-debian-gcc-release-custom-ccacheAzure
test-r-rhub-ubuntu-release-latestAzure
test-r-rocker-r-ver-latestAzure
test-r-rstudio-r-base-4.1-opensuse155Azure
test-r-rstudio-r-base-4.2-focalAzure
test-r-ubuntu-22.04GitHub Actions
test-r-versionsGitHub Actions
test-ubuntu-r-sanitizerGitHub Actions

Comment threadr/src/arrow_cpp11.h
// ensure that x is not actually altrep first
bool was_altrep = ALTREP(x);
if (was_altrep) {
x = PROTECT(Rf_duplicate(x));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment about why we have to duplicate?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'll expand what I have there

Comment threadr/src/arrow_cpp11.h
@@ -152,6 +157,9 @@ inline SEXP utf8_strings(SEXP x) {
SET_STRING_ELT(x, i, Rf_mkCharCE(Rf_translateCharUTF8(s), CE_UTF8));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we want to check whether Rf_translateCharUTF8() actually modified anything? Or do we trust that SET_STRING_ELT is a no-op in that case? I would imagine that in most cases, we already have ascii/utf-8 strings, so this whole function should be basically free. That should be easily verified by microbenchmarking.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this from above?

SEXP new_s = Rf_translateCharUTF8(s);
if (new_s == s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(new_s, CE_UTF8));
}

Yeah, that's probably good. It'll also make this slightly more inline with what was there before (checking that not utf8)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried this, but after getting it to work with a bit of type faffing, doing the translate first we get errors with our utf string tests again.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting change review Awaiting change review labels Jul 24, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes awaiting change review Awaiting change review labels Jul 25, 2024
@github-actionsgithub-actionsBot added the awaiting changes Awaiting changes label Jul 25, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 27, 2024
@jonkeane
jonkeane merged commit 187197c into apache:mainJul 27, 2024
@jonkeanejonkeane removed the awaiting change review Awaiting change review label Jul 27, 2024
jonkeane added a commit that referenced this pull request Jul 27, 2024
### Rationale for this change
To resolve the reverse dependency issue with `parquetize`
### What changes are included in this PR?
One step towards resolving the issue
### Are these changes tested?
yes
### Are there any user-facing changes?
no
* GitHub Issue: #43349
Authored-by: Jonathan Keane <jkeane@gmail.com>
Signed-off-by: Jonathan Keane <jkeane@gmail.com>
@conbench-apache-arrow

Copy link
Copy Markdown

After merging your PR, Conbench analyzed the 4 benchmarking runs that have been run so far on merge-commit 187197c.

There were no benchmark performance regressions. 🎉

The full Conbench report has more details. It also includes information about 28 possible false positives for unstable benchmarks that are known to sometimes produce them.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jonkeane@ursabot@gaborcsardi@nealrichardson
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

GH-43349: [R] Fix altrep string columns from readr - #43351

Merged
jonkeane merged 6 commits into
apache:mainfrom
jonkeane:lazy_readr
Jul 27, 2024
Merged

GH-43349: [R] Fix altrep string columns from readr#43351
jonkeane merged 6 commits into
apache:mainfrom
jonkeane:lazy_readr

Conversation

@jonkeane

@jonkeanejonkeane commented Jul 21, 2024

Copy link
Copy Markdown
Member

Rationale for this change

To resolve the reverse dependency issue with parquetize

What changes are included in this PR?

One step towards resolving the issue

Are these changes tested?

yes

Are there any user-facing changes?

no

@jonkeane
jonkeane requested a review from thisisnic as a code ownerJuly 21, 2024 02:46
@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #43349has been automatically assigned in GitHub to PR creator.

Comment threadr/src/arrow_cpp11.h Outdated
for (R_xlen_t i = 0; i < n; i++, ++p_x) {
SEXP s = *p_x;
if (s != NA_STRING) {
if (s != NA_STRING && ALTREP(s)) {

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not actually work yet. With this

test_that("We handle non-UTF strings", {
x<- iconv("Veitingastaðir", to="latin1")
df<-tibble::tibble(
chr=x,
fct= as.factor(x)
)
names(df) <- iconv(paste(x, names(df), sep="_"), to="latin1")
df_struct<-tibble::tibble(a=df)
raw_schema<-list(utf8(), dictionary(int8(), utf8()))
names(raw_schema) <- names(df)
# Confirm setup
expect_identical(Encoding(x), "latin1")
expect_identical(Encoding(names(df)), c("latin1", "latin1"))
expect_identical(Encoding(df[[1]]), "latin1")
expect_identical(Encoding(levels(df[[2]])), "latin1")
# Array
expect_identical(as.vector(Array$create(x)), x)
fail.

Before #43173 this line was:

if (s != NA_STRING && !IS_UTF8(s) && !IS_ASCII(s)) {

I suspect what's going on is that we caught vroom altrep vectors with this if and called SET_STRING_ELT so we didn't get the No Set_elt found for ALTSTRING error. But which ALTREP(s) we detect the non-utf strings here too and attempt to SET_STRING_ELT when we shouldn't.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any thoughts @nealrichardson ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I don't understand what's happening in here. L144 says that this should no longer be altrep by the time we get here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think that STRING_PTR_RO() should materialize an ALTREP character vector, otherwise you can't iterate over the CHARSXP pointers.

Also, ALTREP(s) does not seem to make sense to me, that's a CHARSXP (not a STRSXP), so it cannot be ALTREP, unless I am misremembering something.

Are you trying to catch the non-UTF-8 strings here? Rf_translateCharUTF8() does not do anything (returns the same const char *) if the string is UTF-8, so you could always call it, and only call SET_STRING_ELT() if the returned pointer is different?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think that STRING_PTR_RO() should materialize an ALTREP character vector, otherwise you can't iterate over the CHARSXP pointers.

Hmm, so maybe the actual problem here is that that isn't working on the ALTREP vectors coming from vroom? Here is the error we started seeing when we changed this condition from s != NA_STRING && !IS_UTF8(s) && !IS_ASCII(s) to s != NA_STRING:

Error in Table__from_dots(dots, schema, option_use_threads()): No Set_elt found for ALTSTRING class [class: vroom_chr, pkg: vroom]

Also, ALTREP(s) does not seem to make sense to me, that's a CHARSXP (not a STRSXP), so it cannot be ALTREP, unless I am misremembering something.

Yeah, I agree that ALTREP(s) here is not right.

Are you trying to catch the non-UTF-8 strings here? Rf_translateCharUTF8() does not do anything (returns the same const char *) if the string is UTF-8, so you could always call it,

I will admit I'm not 100% certain what is going on here, but I catching non-UTF-8 strings here is what I thought it was doing.

and only call SET_STRING_ELT() if the returned pointer is different?

Oh interesting. Forgive my C++ naïveness, but would this be something like (i.e. does != check if the pointer is different>):

SEXP new_s = Rf_mkCharCE(Rf_translateCharUTF8(s);
if (new_s != s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(Rf_translateCharUTF8(new_s), CE_UTF8));
}

@gaborcsardigaborcsardiJul 22, 2024

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that C++ looks correct, except you don't need to translate twice:

SEXP new_s = Rf_mkCharCE(Rf_translateCharUTF8(s);
if (new_s != s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(new_s));
}

However, I think the issue is that if x is ALTREP, then it does not matter if it is materialized of not, it is still ALTREP, so you still cannot change it with SET_STRING_ELT.

To avoid this, you'd need to call Rf_duplicate() on x. That should create a non-altrep copy of it.

You can always call Rf_duplicate(), or you can call it when you encounter a non-utf8 string. Note that you need to PROTECT() the result of Rf_duplicate()

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aaah thanks for those pointers! That looked like it worked, I'm going to trigger a benchmark run to see if there are any unintended consequences there (Hopefully no, but even if so, we might need to accept them!)

Comment threadr/DESCRIPTION Outdated
lubridate,
pillar,
pkgload,
readr,

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure the test is worth adding to suggests, though we will get rid of that annoying xref error if we do.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting committer review Awaiting committer review labels Jul 21, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 23, 2024
@jonkeane

Copy link
Copy Markdown
MemberAuthor

@ursabot please benchmark

@ursabot

Copy link
Copy Markdown

Benchmark runs are scheduled for commit 2a0da1e. Watch https://buildkite.com/apache-arrow and https://conbench.ursa.dev for updates. A comment will be posted here when the runs are complete.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting change review Awaiting change review labels Jul 23, 2024
Comment threadr/src/arrow_cpp11.h Outdated
// ensure that x is not actually altrep first
if (ALTREP(x)) {
x = PROTECT(Rf_duplicate(x));
UNPROTECT(1);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure this isn't right, doesn't the UNPROTECT need to go after you're done using the thing you PROTECTed?

I'm also not sure how the C-level PROTECT stuff interacts with unwind_protect.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, I'm surprised I don't get any stack imbalance issues with it. I do get them if I don't have UNPROTECT or I call it unconditionally. But anyway I have a slightly different version incoming.

@conbench-apache-arrow

Copy link
Copy Markdown

Thanks for your patience. Conbench analyzed the 4 benchmarking runs that have been run so far on PR commit 2a0da1e.

There were 38 benchmark results with an error:

There were 11 benchmark results indicating a performance regression:

The full Conbench report has more details.

@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 23, 2024
@jonkeane

Copy link
Copy Markdown
MemberAuthor

@github-actions crossbow submit -g r

@github-actions

Copy link
Copy Markdown

Revision: 206e94d

Submitted crossbow builds: ursacomputing/crossbow @ actions-6de7c6d005

TaskStatus
r-binary-packagesGitHub Actions
test-r-arrow-backwards-compatibilityGitHub Actions
test-r-clang-sanitizerGitHub Actions
test-r-depsource-bundledAzure
test-r-depsource-systemGitHub Actions
test-r-dev-duckdbGitHub Actions
test-r-devdocsGitHub Actions
test-r-gcc-11GitHub Actions
test-r-gcc-12GitHub Actions
test-r-install-localGitHub Actions
test-r-install-local-minsizerelGitHub Actions
test-r-linux-as-cranGitHub Actions
test-r-linux-rchkGitHub Actions
test-r-linux-valgrindGitHub Actions
test-r-minimal-buildAzure
test-r-offline-maximalGitHub Actions
test-r-offline-minimalAzure
test-r-rhub-debian-gcc-devel-lto-latestAzure
test-r-rhub-debian-gcc-release-custom-ccacheAzure
test-r-rhub-ubuntu-release-latestAzure
test-r-rocker-r-ver-latestAzure
test-r-rstudio-r-base-4.1-opensuse155Azure
test-r-rstudio-r-base-4.2-focalAzure
test-r-ubuntu-22.04GitHub Actions
test-r-versionsGitHub Actions
test-ubuntu-r-sanitizerGitHub Actions

Comment threadr/src/arrow_cpp11.h
// ensure that x is not actually altrep first
bool was_altrep = ALTREP(x);
if (was_altrep) {
x = PROTECT(Rf_duplicate(x));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment about why we have to duplicate?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'll expand what I have there

Comment threadr/src/arrow_cpp11.h
@@ -152,6 +157,9 @@ inline SEXP utf8_strings(SEXP x) {
SET_STRING_ELT(x, i, Rf_mkCharCE(Rf_translateCharUTF8(s), CE_UTF8));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we want to check whether Rf_translateCharUTF8() actually modified anything? Or do we trust that SET_STRING_ELT is a no-op in that case? I would imagine that in most cases, we already have ascii/utf-8 strings, so this whole function should be basically free. That should be easily verified by microbenchmarking.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this from above?

SEXP new_s = Rf_translateCharUTF8(s);
if (new_s == s) {
SET_STRING_ELT(x, i, Rf_mkCharCE(new_s, CE_UTF8));
}

Yeah, that's probably good. It'll also make this slightly more inline with what was there before (checking that not utf8)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried this, but after getting it to work with a bit of type faffing, doing the translate first we get errors with our utf string tests again.

@github-actionsgithub-actionsBot added awaiting changes Awaiting changes and removed awaiting change review Awaiting change review labels Jul 24, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes awaiting change review Awaiting change review labels Jul 25, 2024
@github-actionsgithub-actionsBot added the awaiting changes Awaiting changes label Jul 25, 2024
@github-actionsgithub-actionsBot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 27, 2024
@jonkeane
jonkeane merged commit 187197c into apache:mainJul 27, 2024
@jonkeanejonkeane removed the awaiting change review Awaiting change review label Jul 27, 2024
jonkeane added a commit that referenced this pull request Jul 27, 2024
### Rationale for this change
To resolve the reverse dependency issue with `parquetize`
### What changes are included in this PR?
One step towards resolving the issue
### Are these changes tested?
yes
### Are there any user-facing changes?
no
* GitHub Issue: #43349
Authored-by: Jonathan Keane <jkeane@gmail.com>
Signed-off-by: Jonathan Keane <jkeane@gmail.com>
@conbench-apache-arrow

Copy link
Copy Markdown

After merging your PR, Conbench analyzed the 4 benchmarking runs that have been run so far on merge-commit 187197c.

There were no benchmark performance regressions. 🎉

The full Conbench report has more details. It also includes information about 28 possible false positives for unstable benchmarks that are known to sometimes produce them.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jonkeane@ursabot@gaborcsardi@nealrichardson