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

Shreds incur extra copies in several places #1142

Open
steviez opened this issue May 2, 2024 · 1 comment
Open

Shreds incur extra copies in several places #1142

steviez opened this issue May 2, 2024 · 1 comment

Comments

@steviez
Copy link

steviez commented May 2, 2024

Problem

The shred object currently contains owned memory via a Vec<u8>. In several places, shreds are very temporarily created as an intermediate. Given the owned memory, we have extra copies:

  1. Shred insertion into Blockstore - copy buffers from a PacketBatch to individual Shreds:

    let shred = Shred::new_from_serialized_shred(shred.to_vec()).ok()?;

  2. Shred fetch from Blockstore via Blockstore::get_slot_entries_in_block()

    Some(pinnable_slice) => Ok(Some(pinnable_slice.as_ref().to_vec())),

    let data: Vec<_> = shreds.iter().map(Shred::data).collect::<Result<_, _>>()?;
    let data: Vec<_> = data.into_iter().flatten().copied().collect();

    agave/ledger/src/blockstore.rs

    Lines 3579 to 3584 in 12d009e

    .and_then(|payload| {
    bincode::deserialize::<Vec<Entry>>(&payload).map_err(|e| {
    BlockstoreError::InvalidShredData(Box::new(bincode::ErrorKind::Custom(
    format!("could not reconstruct entries: {e:?}"),
    )))
    })

For 1., the shreds have a short, common lifetime, that ends shortly after this copy. One minor exception is for any duplicate shreds that may be found (the duplicates have some further processing).

For 2., the copy into common buffer (2nd snippet) and bincode deserialize (3rd snippet) are necessary. However, the initial copy to owned memory (1st snippet) is seemingly not necessary. Note that shred fetch occurs for both ReplayStage and CompletedDataSetsService, so the effects of this one are doubled.

Looking at MNB metrics:

  1. I see about ~2700 packets / second coming through (8k on recv-window-insert-shreds.num_packets and datapoint reported every 3 seconds)
  2. I see about ~1500 shreds / second (600 for shred_insert_is_full.last_index * 2.5 blocks / second).
    • Again, for below calculation, double this number because of ReplayStage and CompletedDataSetsService

So, in total, this is about ~5700 allocations and ~6.75 MB of memory copy that could be avoided, per second

Proposed Solution

Rework Shred to allow owned or borrowed memory, such as what Cow provides

@steviez
Copy link
Author

steviez commented May 2, 2024

At some point in the near future, I'll gather some data with perf so we can get a better idea of how much the previously mentioned numbers contribute to overall process numbers. Doing so might help us attach some sense of priority (ie if these are 0.01% of all allocations, we might decide we have bigger fish to fry at the moment).

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

No branches or pull requests

1 participant