Skip to main content

BlockPagedLayout

Struct BlockPagedLayout 

Source
#[non_exhaustive]
pub struct BlockPagedLayout { pub page_size: u32, pub num_pages: u64, pub paged_axis: u32, pub num_seqs: u32, pub kv_role: KvRole, pub layer_index: Option<u32>, pub block_table_index_type: BlockTableIndexType, }
Expand description

Descriptor for the block-paged indirect layout.

Block-paged stores a KV-cache tensor whose paged axis is divided into fixed-size pages drawn from a shared page pool, with a block table mapping each logical page position to a physical page ID. It is the interchange form of a PagedAttention KV cache.

This layout is defined only for rank-3 tensors in this version of the specification: [total_tokens, num_heads, head_dim].

Three buffers are always required:

BufferNameDescription
0page_poolFlat pool of fixed-size pages.
1block_tableConcatenated per-sequence page-ID lists.
2seq_ptrCSR-style offset array into block_table.

When the tensor carries quantization parameters, additional buffers appear at indices 3 and above per docs/spec/quantization.md § Buffer Table Placement Rules.

See docs/spec/layouts/block-paged.md for the full normative definition.

§Examples

use hurray_core::layout::{BlockPagedLayout, BlockTableIndexType, KvRole, LayoutDescriptor};

let layout = BlockPagedLayout::new(16, 128, 0, 4, KvRole::Key, Some(3), BlockTableIndexType::U32);
assert_eq!(layout.page_size, 16);
assert_eq!(layout.num_pages, 128);
assert_eq!(layout.num_seqs, 4);
assert_eq!(layout.layer_index, Some(3));

let desc = LayoutDescriptor::BlockPaged(layout);
assert_eq!(desc.tag(), 0x0A);
assert_eq!(desc.buffer_count().map(|n| n.get()), Some(3));

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§page_size: u32

Tokens per page. MUST be >= 1.

Common values are 16 and 32 (matching vLLM’s default block_size).

§num_pages: u64

Number of physical pages in page_pool.

§paged_axis: u32

The axis subdivided into pages. MUST be 0 in this spec version.

Kept as a field rather than a constant so that the wire format remains forward-compatible if a future spec version adds non-zero paged axes.

§num_seqs: u32

Number of sequences in the batch. MAY be 0 for an empty batch.

§kv_role: KvRole

KV-cache role for this tensor (key, value, or fused/generic).

§layer_index: Option<u32>

Transformer layer index, or None if the tensor is not layer-scoped.

Wire value 0xFFFFFFFF maps to None; all other values map to Some(n).

§block_table_index_type: BlockTableIndexType

Element type for both the block_table (buffer 1) and seq_ptr (buffer 2).

U32 is the default and addresses up to 4 billion pages. U64 is required for larger pools.

Implementations§

Source§

impl BlockPagedLayout

Source

pub fn new( page_size: u32, num_pages: u64, paged_axis: u32, num_seqs: u32, kv_role: KvRole, layer_index: Option<u32>, block_table_index_type: BlockTableIndexType, ) -> Self

Creates a new BlockPagedLayout.

§Arguments
  • page_size — tokens per page (MUST be validated >= 1 later via validate_against_shape).
  • num_pages — number of physical pages in the pool.
  • paged_axis — the axis subdivided into pages (MUST be 0 in this version).
  • num_seqs — number of sequences in the batch.
  • kv_role — KV-cache role for this tensor.
  • layer_index — transformer layer index (None = not layer-scoped).
  • block_table_index_type — element type for block_table and seq_ptr buffers.

This constructor does not validate page_size >= 1 or paged_axis == 0 because the tensor shape is not available at construction time. Call [LayoutDescriptor::validate_against_shape] to perform all invariant checks once the shape is known.

§Examples
use hurray_core::layout::{BlockPagedLayout, BlockTableIndexType, KvRole};

// Layer-3 key cache: page_size=16, 64 pages, 2 sequences, uint32 indices.
let layout = BlockPagedLayout::new(
    16, 64, 0, 2,
    KvRole::Key,
    Some(3),
    BlockTableIndexType::U32,
);
assert_eq!(layout.page_size, 16);
assert_eq!(layout.layer_index, Some(3));
Source

pub fn validate_quantization_compatibility( &self, scheme_tag: u8, quant_axis: u32, quant_block_size: u32, ) -> Result<()>

Validates that a quantization scheme is compatible with this block-paged layout.

Per docs/spec/layouts/block-paged.md § Quantization Compatibility:

  • Per-tensor (scheme_tag = 0x01): always compatible.
  • Per-channel (scheme_tag = 0x02): MUST NOT be applied on axis 0 (the paged / token axis). quant_axis must be 1 (num_heads) or 2 (head_dim).
  • Per-block-affine (scheme_tag = 0x03): quant_axis MUST be 0 and quant_block_size MUST equal page_size.
  • All other schemes: not validated here; returns Ok(()).

Call this at the typed-quantization layer once you have a decoded crate::QuantizationDescriptor. The raw-bytes descriptor layer cannot perform this check.

§Arguments
  • scheme_tag — the scheme_tag byte from the quantization descriptor header.
  • quant_axis — the axis field from the quantization descriptor (relevant for per-channel and per-block-affine).
  • quant_block_size — the block_size field from the per-block-affine descriptor (ignored for all other schemes).
§Errors

Returns crate::Error::InvalidQuantization if the scheme/layout combination violates the spec rules above.

§Examples
use hurray_core::layout::{BlockPagedLayout, BlockTableIndexType, KvRole};

let layout = BlockPagedLayout::new(16, 64, 0, 2, KvRole::Key, Some(0), BlockTableIndexType::U32);

// Per-tensor (0x01) is always valid.
assert!(layout.validate_quantization_compatibility(0x01, 0, 0).is_ok());

// Per-channel on axis 1 (num_heads) is valid.
assert!(layout.validate_quantization_compatibility(0x02, 1, 0).is_ok());

// Per-channel on axis 0 (paged axis) is forbidden.
assert!(layout.validate_quantization_compatibility(0x02, 0, 0).is_err());

// Per-block-affine with axis=0 and block_size==page_size is valid.
assert!(layout.validate_quantization_compatibility(0x03, 0, 16).is_ok());

// Per-block-affine with block_size != page_size is forbidden.
assert!(layout.validate_quantization_compatibility(0x03, 0, 32).is_err());

Trait Implementations§

Source§

impl Clone for BlockPagedLayout

Source§

fn clone(&self) -> BlockPagedLayout

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for BlockPagedLayout

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Eq for BlockPagedLayout

Source§

impl Hash for BlockPagedLayout

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl PartialEq for BlockPagedLayout

Source§

fn eq(&self, other: &BlockPagedLayout) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl StructuralPartialEq for BlockPagedLayout

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.