Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support lgalloc for columnar #31230

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

antiguru
Copy link
Member

Adds support for allocating memory for columnar's aligned type from
lgalloc. Adds a flag enable_lgalloc_columnar to control the behavior.

Checklist

  • This PR has adequate test coverage / QA involvement has been duly considered. (trigger-ci for additional test/nightly runs)
  • This PR has an associated up-to-date design doc, is a design doc (template), or is sufficiently small to not require a design.
  • If this PR evolves an existing $T ⇔ Proto$T mapping (possibly in a backwards-incompatible way), then it is tagged with a T-proto label.
  • If this PR will require changes to cloud orchestration or tests, there is a companion cloud PR to account for those changes that is tagged with the release-blocker label (example).
  • If this PR includes major user-facing behavior changes, I have pinged the relevant PM to schedule a changelog post.

@antiguru antiguru requested review from a team and benesch as code owners January 29, 2025 14:58
Adds support for allocating memory for columnar's aligned type from
lgalloc. Adds a flag `enable_lgalloc_columnar` to control the behavior.

Signed-off-by: Moritz Hoffmann <[email protected]>

# Conflicts:
#	src/compute/src/logging/differential.rs
#	src/compute/src/logging/initialize.rs
#	src/compute/src/logging/reachability.rs

Signed-off-by: Moritz Hoffmann <[email protected]>
Comment on lines +245 to +254
/// Allocate a region of memory with a capacity of at least `len` that is aligned to 8 bytes
/// and zeroed.
#[inline]
pub(crate) fn alloc_aligned_zeroed(len: usize) -> Region<u64> {
if enable_lgalloc_columnar() {
Region::new_auto_zeroed(len)
} else {
Region::new_heap_zeroed(len)
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Mind adding to this comment that it's aligned to 8 bytes because it's a Region of u64s?

Comment on lines +281 to +287
assert!(align <= 8);
let length = u64::cast_from(bytes.len());
let length_slice = bytemuck::cast_slice(std::slice::from_ref(&length));
writer.write_all(length_slice).unwrap();
writer.write_all(bytes).unwrap();
let padding = usize::cast_from((8 - (length % 8)) % 8);
writer.write_all(&[0; 8][..padding]).unwrap();
Copy link
Member

Choose a reason for hiding this comment

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

I'm not totally up-to-date on the encoded layout of a Column, but my read is this layouts data like:

<u64 encoded in native endianness><buffer whose length is the u64><optional padding>

If that's what it's supposed to do then it LGTM!

Comment on lines +58 to +59
pub const ENABLE_LGALLOC_COLUMNAR: Config<bool> = Config::new(
"enable_lgalloc_columnar",
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Call this enable_columnar_lgalloc for consistency with the columniation flag?

pub const ENABLE_LGALLOC_COLUMNAR: Config<bool> = Config::new(
"enable_lgalloc_columnar",
true,
"Enable allocating alignd regions in columnar from lgalloc.",
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"Enable allocating alignd regions in columnar from lgalloc.",
"Enable allocating aligned regions in columnar from lgalloc.",

}
}
// Fall-through
Region::Heap(vec![0; capacity])
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Region::Heap(vec![0; capacity])
Self::new_heap_zeroed(capacity)

use timely::bytes::arc::Bytes;
use timely::container::PushInto;
use timely::dataflow::channels::ContainerBytes;
use timely::Container;

use crate::containers::alloc_aligned_zeroed;
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't this import redundant, since it's importing from the current module?

pub fn new_mmap_zeroed(capacity: usize) -> Result<Region<u64>, lgalloc::AllocError> {
lgalloc::allocate::<u64>(capacity).map(|(ptr, capacity, handle)| {
// SAFETY: `allocate` returns a valid memory block
unsafe { ptr.as_ptr().write_bytes(0, capacity) }
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems like something that could introduce performance regressions once we start switching more things from columnation to columnar. Is the zeroing required for columnar to work?

use timely::container::PushInto;
use timely::container::{ContainerBuilder, LengthPreservingContainerBuilder};

use super::Column;

thread_local! {
static ENABLE_LGALLOC_COLUMNAR: std::cell::RefCell<bool> = const { std::cell::RefCell::new(false) };
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
static ENABLE_LGALLOC_COLUMNAR: std::cell::RefCell<bool> = const { std::cell::RefCell::new(false) };
static ENABLE_LGALLOC_COLUMNAR: std::cell::RefCell<bool> = std::cell::RefCell::new(false);

Copy link
Contributor

Choose a reason for hiding this comment

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

You could also use an AtomicBool here, to rule out the possibility of panics.

Comment on lines +282 to +287
let length = u64::cast_from(bytes.len());
let length_slice = bytemuck::cast_slice(std::slice::from_ref(&length));
writer.write_all(length_slice).unwrap();
writer.write_all(bytes).unwrap();
let padding = usize::cast_from((8 - (length % 8)) % 8);
writer.write_all(&[0; 8][..padding]).unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a columnar method we could invoke so we don't have to worry about this code diverging from the columnar implementation?

Comment on lines +136 to +139
// SAFETY: casting `elements` to a `*const [T]` is safe since the caller guarantees that
// `slice` is initialized, and `MaybeUninit` is guaranteed to have the same layout as `T`.
// The pointer obtained is valid since it refers to memory owned by `elements` which is a
// reference and thus guaranteed to be valid for reads.
Copy link
Contributor

Choose a reason for hiding this comment

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

This safety argument talks about *const and reads, but we're casting to a *mut and deference that for writes. I think a similar argument still holds, based on that elements is mutably borrowed here, so we can produce a &mut into it.

Copy link
Contributor

@frankmcsherry frankmcsherry left a comment

Choose a reason for hiding this comment

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

Seems good, though there are a few moments I'm not entirely clear on. But structurally the PR seems ok!

@@ -342,6 +344,48 @@ impl<T> Region<T> {
}
}

impl Region<u64> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this exist because of columnar's u64 bias? If so, is this great news and we love it, or is this a work around and we're not happy? Mostly trying to grok what's going on here and for what reason, which I imagine is clearer to you than it is to me.

@@ -120,6 +128,22 @@ impl<T> Deref for Array<T> {
}
}

impl<T> DerefMut for Array<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
// TODO: Use `slice_assume_init_ref` once stable.
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably slice_assume_init_mut rather than _ref, I think?

Copy link
Contributor

Choose a reason for hiding this comment

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

If nothing else, we'll want to have the unsafe justification for _mut instead, I think. Maybe a nit, but "have the right explanation when we use unsafe" seems like a great place to pick nits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants