-
Notifications
You must be signed in to change notification settings - Fork 174
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
feat: introduce DecoratorSpan to reduce MastNode size #1621
base: next
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,7 +11,7 @@ use vm_core::{ | |
}, | ||
Decorator, DecoratorList, Operation, | ||
}; | ||
|
||
use vm_core::mast::DecoratorSpan; | ||
use super::{GlobalProcedureIndex, Procedure}; | ||
use crate::AssemblyError; | ||
|
||
|
@@ -456,14 +456,18 @@ impl MastForestBuilder { | |
} | ||
|
||
pub fn set_before_enter(&mut self, node_id: MastNodeId, decorator_ids: Vec<DecoratorId>) { | ||
self.mast_forest[node_id].set_before_enter(decorator_ids); | ||
// Does this need to have a Vec<DecoratorSpan>? | ||
let span = DecoratorSpan::new_collection(decorator_ids).into_iter().next().unwrap(); | ||
self.mast_forest[node_id].set_before_enter(span); | ||
|
||
let new_node_fingerprint = self.fingerprint_for_node(&self[node_id]); | ||
self.hash_by_node_id.insert(node_id, new_node_fingerprint); | ||
} | ||
|
||
pub fn set_after_exit(&mut self, node_id: MastNodeId, decorator_ids: Vec<DecoratorId>) { | ||
self.mast_forest[node_id].set_after_exit(decorator_ids); | ||
// Does this need to have a Vec<DecoratorSpan>? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure having a collection of spans is needed, but new_collection method creates a collection because it seems that it may be possible to have non-contiguous decorator spans. See this comment: https://github.com/0xPolygonMiden/miden-vm/pull/1621/files#r1912588483 |
||
let span = DecoratorSpan::new_collection(decorator_ids).into_iter().next().unwrap(); | ||
self.mast_forest[node_id].set_after_exit(span); | ||
|
||
let new_node_fingerprint = self.fingerprint_for_node(&self[node_id]); | ||
self.hash_by_node_id.insert(node_id, new_node_fingerprint); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,9 +7,9 @@ use vm_core::{ | |
crypto::hash::RpoDigest, | ||
debuginfo::SourceSpan, | ||
mast::{DecoratorId, MastNodeId}, | ||
DecoratorList, Felt, Kernel, Operation, Program, | ||
Felt, Kernel, Operation, Program, | ||
}; | ||
|
||
use vm_core::mast::DecoratorSpan; | ||
use crate::{ | ||
ast::{self, Export, InvocationTarget, InvokeKind, ModuleKind, QualifiedProcedureName}, | ||
diagnostics::Report, | ||
|
@@ -642,10 +642,13 @@ impl Assembler { | |
)?; | ||
|
||
if let Some(decorator_ids) = block_builder.drain_decorators() { | ||
// This just picks off the first decorator span, but should there be more than one? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again not sure if there will ever be a case for more than one span here |
||
let span = DecoratorSpan::new_collection(decorator_ids).into_iter().next().unwrap(); | ||
|
||
// Attach the decorators before the first instance of the repeated node | ||
let mut first_repeat_node = | ||
block_builder.mast_forest_builder_mut()[repeat_node_id].clone(); | ||
first_repeat_node.set_before_enter(decorator_ids); | ||
first_repeat_node.set_before_enter(span); | ||
let first_repeat_node_id = block_builder | ||
.mast_forest_builder_mut() | ||
.ensure_node(first_repeat_node)?; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,10 +2,7 @@ use alloc::{collections::BTreeMap, vec::Vec}; | |
|
||
use miden_crypto::{hash::blake::Blake3Digest, utils::collections::KvMap}; | ||
|
||
use crate::mast::{ | ||
DecoratorId, MastForest, MastForestError, MastNode, MastNodeFingerprint, MastNodeId, | ||
MultiMastForestIteratorItem, MultiMastForestNodeIter, | ||
}; | ||
use crate::mast::{DecoratorId, DecoratorSpan, MastForest, MastForestError, MastNode, MastNodeFingerprint, MastNodeId, MultiMastForestIteratorItem, MultiMastForestNodeIter}; | ||
|
||
#[cfg(test)] | ||
mod tests; | ||
|
@@ -151,6 +148,8 @@ impl MastForestMerger { | |
let new_decorator_id = if let Some(existing_decorator) = | ||
self.decorators_by_hash.get(&merging_decorator_hash) | ||
{ | ||
// This implies that in some cases it is possible to have intersecting decorator sets | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This implies that in some cases it is possible to have intersecting decorator sets, meaning a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I think the original proposal didn't account for this. We definitely don't want to split a
Option 1 could make sense if we expect vast majority of decorators to be unique anyway - so, duplicating a small number of them may not be an issue. On the other hand, option 2 is a more generic approach and is probably less work. So, my intuition is that option 2 is probably a better approach here (assuming in fact is is not more complicated to implement than options 1). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bobbinth I don't fully understand Option 2. But just to understand the picture better. The only place where I found a potential of overlap is this code checking for existing decorators when merging. If they are not contiguous and we need another list to track that, then the initial goal of reducing memory footprint will not be met. (We were trying to replace a Vec with a smaller structure. Or does Option 2 simply push the memory burden away from If we have DecoratorSpan -> Vec(oldDecoratorID, newDecoratorId) -> DecoratorID I don't understand where the middle part would live, and why would this be better than what is currently in main. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is not exactly correct: only unique decorators get uniq decorator IDs. That is, if we want to add two identical decorators to the same
The main idea of option 2 is to have a vector of decorator IDs which could contain duplicate IDs. So, pub struct MastForest {
nodes: Vec<MastNode>,
roots: Vec<MastNodeId>,
decorator_ids: Vec<DecoratorId>, // new filed, open to use a different name for it
decorators: Vec<Decorator>,
advice_map: AdviceMap,
} Then, To illustrate this, let's say we have a
And so, when we want to look up decorators for node 1, we look at its decorator span that says that we need to grab the first two IDs from the This does increase the size of the |
||
// meaning a DecoratorSpan will potentially need to be split up | ||
*existing_decorator | ||
} else { | ||
let new_decorator_id = self.mast_forest.add_decorator(merging_decorator.clone())?; | ||
|
@@ -263,8 +262,18 @@ impl MastForestMerger { | |
) | ||
}) | ||
}; | ||
let map_decorators = |decorators: &[DecoratorId]| -> Result<Vec<_>, MastForestError> { | ||
decorators.iter().map(map_decorator_id).collect() | ||
|
||
let remap_decorator_span = |decorator_span: &DecoratorSpan| -> Result<DecoratorSpan, MastForestError>{ | ||
let new_decorator_ids = decorator_span | ||
.iter() | ||
.map(|decorator_id| map_decorator_id(&decorator_id)) | ||
.collect::<Result<Vec<_>, _>>()?; | ||
let new_spans = DecoratorSpan::new_collection(new_decorator_ids); | ||
|
||
new_spans | ||
.into_iter() | ||
.next() | ||
.ok_or_else(|| MastForestError::DecoratorSpanMissing(decorator_span.offset, decorator_span.num_decorators)) | ||
}; | ||
|
||
let map_node_id = |node_id: MastNodeId| { | ||
|
@@ -311,9 +320,11 @@ impl MastForestMerger { | |
basic_block_node | ||
.decorators() | ||
.iter() | ||
.map(|(idx, decorator_id)| match map_decorator_id(decorator_id) { | ||
Ok(mapped_decorator) => Ok((*idx, mapped_decorator)), | ||
Err(err) => Err(err), | ||
.map(|(idx, decorator_span)| { | ||
match remap_decorator_span(decorator_span) { | ||
Ok(mapped_decorator) => Ok((*idx, mapped_decorator)), | ||
Err(err) => Err(err), | ||
} | ||
}) | ||
.collect::<Result<Vec<_>, _>>()?, | ||
), | ||
|
@@ -327,8 +338,8 @@ impl MastForestMerger { | |
// Decorators must be handled specially for basic block nodes. | ||
// For other node types we can handle it centrally. | ||
if !mapped_node.is_basic_block() { | ||
mapped_node.set_before_enter(map_decorators(node.before_enter())?); | ||
mapped_node.set_after_exit(map_decorators(node.after_exit())?); | ||
mapped_node.set_before_enter(remap_decorator_span(node.before_enter())?); | ||
mapped_node.set_after_exit(remap_decorator_span(node.after_exit())?); | ||
} | ||
|
||
Ok(mapped_node) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure having a collection of spans is needed, but
new_collection
method creates a collection because it seems that it may be possible to have non-contiguous decorator spans. See this comment: https://github.com/0xPolygonMiden/miden-vm/pull/1621/files#r1912588483