chore: move utils/root_cache.zig -> cache/RootCache.zig#170
Conversation
Summary of ChangesHello @spiral-ladder, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request focuses on improving the project's internal structure by relocating the Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request refactors RootCache by moving it to a new file and making it a top-level struct. The changes are a good step towards better code organization. However, there are a few violations of the repository's style guide that should be addressed. Specifically, the new file name does not follow the snake_case convention, the init and deinit functions use heap allocation which is discouraged after startup, and the functions are missing required assertions. My review includes suggestions to align the code with the style guide.
| pub fn init(allocator: Allocator, cached_state: *const CachedBeaconStateAllForks) !*RootCache { | ||
| const instance = try allocator.create(RootCache); | ||
| errdefer allocator.destroy(instance); | ||
| const state = cached_state.state; | ||
| instance.* = RootCache{ | ||
| .allocator = allocator, | ||
| .current_justified_checkpoint = state.currentJustifiedCheckpoint().*, | ||
| .previous_justified_checkpoint = state.previousJustifiedCheckpoint().*, | ||
| .state = state, | ||
| .block_root_epoch_cache = std.AutoHashMap(Epoch, Root).init(allocator), | ||
| .block_root_slot_cache = std.AutoHashMap(Slot, Root).init(allocator), | ||
| }; | ||
|
|
||
| return instance; | ||
| } | ||
|
|
||
| pub fn getBlockRoot(self: *RootCache, epoch: Epoch) !Root { | ||
| if (self.block_root_epoch_cache.get(epoch)) |root| { | ||
| return root; | ||
| } else { | ||
| const root = try getBlockRootFn(self.state, epoch); | ||
| try self.block_root_epoch_cache.put(epoch, root); | ||
| return root; | ||
| } | ||
| } | ||
|
|
||
| pub fn getBlockRootAtSlot(self: *RootCache, slot: Slot) !Root { | ||
| if (self.block_root_slot_cache.get(slot)) |root| { | ||
| return root; | ||
| } else { | ||
| const root = try getBlockRootAtSlotFn(self.state, slot); | ||
| try self.block_root_slot_cache.put(slot, root); | ||
| return root; | ||
| } | ||
| } | ||
|
|
||
| pub fn deinit(self: *RootCache) void { | ||
| self.block_root_epoch_cache.deinit(); | ||
| self.block_root_slot_cache.deinit(); | ||
| self.allocator.destroy(self); | ||
| } |
There was a problem hiding this comment.
The current implementation of init allocates RootCache on the heap, and deinit frees it. This pattern of dynamic memory allocation during runtime is contrary to the style guide, which states, 'No memory may be dynamically allocated (or freed and reallocated) after initialization.' (lines 93-94).
To adhere to the guide and also follow the preference for in-place initialization of large structs (lines 314-321), RootCache should be allocated on the stack by the caller and initialized in-place.
This requires changing init to accept a pointer to a RootCache instance and deinit to no longer deallocate the instance itself.
pub fn init(self: *RootCache, allocator: Allocator, cached_state: *const CachedBeaconStateAllForks) void {
const state = cached_state.state;
self.* = RootCache{
.allocator = allocator,
.current_justified_checkpoint = state.currentJustifiedCheckpoint().*,
.previous_justified_checkpoint = state.previousJustifiedCheckpoint().*,
.state = state,
.block_root_epoch_cache = std.AutoHashMap(Epoch, Root).init(allocator),
.block_root_slot_cache = std.AutoHashMap(Slot, Root).init(allocator),
};
}
pub fn getBlockRoot(self: *RootCache, epoch: Epoch) !Root {
if (self.block_root_epoch_cache.get(epoch)) |root| {
return root;
} else {
const root = try getBlockRootFn(self.state, epoch);
try self.block_root_epoch_cache.put(epoch, root);
return root;
}
}
pub fn getBlockRootAtSlot(self: *RootCache, slot: Slot) !Root {
if (self.block_root_slot_cache.get(slot)) |root| {
return root;
} else {
const root = try getBlockRootAtSlotFn(self.state, slot);
try self.block_root_slot_cache.put(slot, root);
return root;
}
}
pub fn deinit(self: *RootCache) void {
self.block_root_epoch_cache.deinit();
self.block_root_slot_cache.deinit();
}
References
- All memory must be statically allocated at startup. No memory may be dynamically allocated (or freed and reallocated) after initialization. This avoids unpredictable behavior that can significantly affect performance, and avoids use-after-free. (link)
- Construct larger structs in-place by passing an out pointer during initialization. This eliminates intermediate copy-move allocations, which can lead to undesirable stack growth. (link)
| @@ -0,0 +1,64 @@ | |||
| const RootCache = @This(); | |||
There was a problem hiding this comment.
The filename RootCache.zig does not follow the snake_case convention specified in the style guide. Please rename it to root_cache.zig.
This change will also require updating the import paths in src/state_transition/block/process_attestation_altair.zig and src/state_transition/slot/upgrade_state_to_altair.zig.
References
- Use
snake_casefor file names. We don't use Zig'sCamelCase.zigstyle for "struct" files to keep the convention simple and consistent. (link)
| pub fn getBlockRoot(self: *RootCache, epoch: Epoch) !Root { | ||
| if (self.block_root_epoch_cache.get(epoch)) |root| { | ||
| return root; | ||
| } else { | ||
| const root = try getBlockRootFn(self.state, epoch); | ||
| try self.block_root_epoch_cache.put(epoch, root); | ||
| return root; | ||
| } | ||
| } |
There was a problem hiding this comment.
The functions in this file are missing assertions. The style guide mandates asserting all function arguments, preconditions, and invariants, with an average of at least two assertions per function (lines 51-55). Please add assertions to all functions in this file. For example, you should assert that self is not null.
pub fn getBlockRoot(self: *RootCache, epoch: Epoch) !Root {
std.debug.assert(self != null);
if (self.block_root_epoch_cache.get(epoch)) |root| {
return root;
} else {
const root = try getBlockRootFn(self.state, epoch);
try self.block_root_epoch_cache.put(epoch, root);
return root;
}
}
References
- Assert all function arguments and return values, pre/postconditions and invariants. A function must not operate blindly on data it has not checked. The assertion density of the code must average a minimum of two assertions per function. (link)
twoeths
left a comment
There was a problem hiding this comment.
may want to wait for the TreeView BeaconState because it returns Checkpoint TreeView for currentJustifiedCheckpoint() api https://github.com/ChainSafe/lodestar-z/pull/168/changes#diff-f3c8638ffff8c55ffd2c70324cc540954a42662eefe344195c145adb099b1f33R466
looks good to me other than that
9fd5e20 to
0a4f65c
Compare
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
part of #169
Also make it a top-level struct.