Skip to content

Commit b6b1b5e

Browse files
docs: add implementation details documentation and comprehensive tests
The commit adds detailed documentation about LessVM's internals and comprehensive test coverage for new features including SIMD vector operations, data structure management, and optimized memory handling.
1 parent 6c895d5 commit b6b1b5e

File tree

6 files changed

+957
-1
lines changed

6 files changed

+957
-1
lines changed

docs/implementation_details.md

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
# LessVM Implementation Details
2+
3+
This document provides details about the implementation of LessVM, focusing on recent changes and optimizations.
4+
5+
## Table of Contents
6+
7+
- [SIMD Vector Addition](#simd-vector-addition)
8+
- [Data Structure Store](#data-structure-store)
9+
- [Memory Management](#memory-management)
10+
- [Opcode Implementations](#opcode-implementations)
11+
- [Solana Account Operations](#solana-account-operations)
12+
- [Graph Operations](#graph-operations)
13+
- [OHLCV Operations](#ohlcv-operations)
14+
- [Hypergraph Operations](#hypergraph-operations)
15+
16+
## SIMD Vector Addition
17+
18+
The `vector_add` function uses SIMD (Single Instruction, Multiple Data) instructions to efficiently add multiple values at once. The implementation loads two vectors from the stack and adds them together.
19+
20+
```mermaid
21+
sequenceDiagram
22+
participant VM
23+
VM->>VM: _mm256_loadu_si256(stack[top] to values2)
24+
VM->>VM: stack.pop() x 4
25+
VM->>VM: _mm256_loadu_si256(stack[top] to values1)
26+
VM->>VM: result = _mm256_add_epi64(values1, values2)
27+
VM->>VM: stack.push(result) x 4
28+
```
29+
30+
The function works as follows:
31+
1. Load the second vector (values2) from the top of the stack
32+
2. Pop the first 4 values from the stack
33+
3. Load the first vector (values1) from the new top of the stack
34+
4. Add the two vectors together using SIMD instructions
35+
5. Push the result back onto the stack
36+
37+
This approach ensures that we're adding two different vectors together, rather than adding a vector to itself.
38+
39+
## Data Structure Store
40+
41+
The `DataStructureStore` manages various data structures used by the VM, including BTreeMaps, Tries, Graphs, OHLCV, and Hypergraphs.
42+
43+
```mermaid
44+
classDiagram
45+
class DataStructureStore {
46+
btrees: Vec<Option<BTreeMapDS>>
47+
tries: Vec<Option<TrieDS>>
48+
graphs: Vec<Option<GraphDS>>
49+
ohlcvs: Vec<Option<OHLCVDS>>
50+
hypergraphs: Vec<Option<HypergraphDS>>
51+
+new() DataStructureStore
52+
+ensure_capacity(ds_type: DataStructureType, id: usize) void
53+
}
54+
note for DataStructureStore "Stores different types of data structures."
55+
```
56+
57+
The `ensure_capacity` method ensures that the vectors have enough capacity to store a data structure at a specific index. If the index is beyond the current length of the vector, the vector is resized to accommodate the new index.
58+
59+
```mermaid
60+
sequenceDiagram
61+
participant VM
62+
participant DataStructureStore
63+
VM->>DataStructureStore: ensure_capacity(DataStructureType::BTreeMap, 5)
64+
alt Vector length < 6
65+
DataStructureStore->>DataStructureStore: resize_with(6, || None)
66+
else Vector length >= 6
67+
DataStructureStore->>DataStructureStore: Do nothing
68+
end
69+
```
70+
71+
## Memory Management
72+
73+
The memory management system has been optimized to use a more efficient growth strategy. Instead of doubling the capacity when more space is needed, the new implementation grows the memory by 50% or to the required size, whichever is larger.
74+
75+
```mermaid
76+
sequenceDiagram
77+
participant Memory
78+
participant Vec
79+
Memory->>Memory: ensure_capacity(required_size)
80+
alt required_size > data.len()
81+
Memory->>Memory: new_capacity = (data.len() * 3 / 2).max(required_size)
82+
Memory->>Vec: resize(new_capacity, 0)
83+
else required_size <= data.len()
84+
Memory->>Memory: Do nothing
85+
end
86+
```
87+
88+
This approach reduces memory waste while still providing amortized constant-time operations.
89+
90+
## Opcode Implementations
91+
92+
### Solana Account Operations
93+
94+
The following Solana account operations have been implemented:
95+
96+
- `GetBalance`: Gets the balance (lamports) of an account
97+
- `GetOwner`: Gets the owner of an account
98+
- `IsWritable`: Checks if an account is writable
99+
- `IsSigner`: Checks if an account is a signer
100+
101+
```mermaid
102+
sequenceDiagram
103+
participant VM
104+
participant Account
105+
VM->>VM: Pop account_idx from stack
106+
VM->>VM: Check if account_idx is valid
107+
alt Valid account_idx
108+
VM->>Account: Get account information
109+
Account->>VM: Return account information
110+
VM->>VM: Push result to stack
111+
else Invalid account_idx
112+
VM->>VM: Return InvalidAccount error
113+
end
114+
```
115+
116+
### Graph Operations
117+
118+
The following graph operations have been implemented:
119+
120+
- `GraphAddEdge`: Adds an edge to a graph
121+
- `GraphGetNode`: Gets the value of a node
122+
- `GraphSetNode`: Sets the value of a node
123+
- `GraphGetNeighbors`: Gets the neighbors of a node
124+
- `GraphBfs`: Performs a breadth-first search starting from a node
125+
- `GraphClear`: Clears a graph
126+
127+
```mermaid
128+
sequenceDiagram
129+
participant VM
130+
participant DataStructureStore
131+
participant GraphDS
132+
VM->>VM: Pop weight, to, from, and id from stack
133+
VM->>DataStructureStore: Access graph with id
134+
alt Graph exists
135+
DataStructureStore->>GraphDS: add_edge(from, to, weight)
136+
GraphDS-->>DataStructureStore: Result
137+
else Graph does not exist
138+
DataStructureStore-->>VM: Error: InvalidDataStructureOperation
139+
end
140+
```
141+
142+
### OHLCV Operations
143+
144+
The following OHLCV (Open-High-Low-Close-Volume) operations have been implemented:
145+
146+
- `OhlcvAddBar`: Adds a bar to an OHLCV
147+
- `OhlcvGetBar`: Gets a bar from an OHLCV
148+
- `OhlcvSma`: Calculates the Simple Moving Average (SMA) of an OHLCV
149+
150+
```mermaid
151+
sequenceDiagram
152+
participant VM
153+
participant DataStructureStore
154+
participant OHLCVDS
155+
VM->>VM: Pop timestamp, open, high, low, close, volume, and id from stack
156+
VM->>DataStructureStore: Access ohlcv with id
157+
alt OHLCV exists
158+
DataStructureStore->>OHLCVDS: add_entry(timestamp, open, high, low, close, volume)
159+
OHLCVDS-->>DataStructureStore: Result
160+
else OHLCV does not exist
161+
DataStructureStore-->>VM: Error: InvalidDataStructureOperation
162+
end
163+
```
164+
165+
### Hypergraph Operations
166+
167+
The following hypergraph operations have been implemented:
168+
169+
- `HyperAddNode`: Adds a node to a hypergraph
170+
- `HyperAddEdge`: Adds an edge to a hypergraph
171+
- `HyperAddNodeToEdge`: Adds a node to an edge in a hypergraph
172+
173+
```mermaid
174+
sequenceDiagram
175+
participant VM
176+
participant DataStructureStore
177+
participant HypergraphDS
178+
VM->>VM: Pop node_id, edge_id, and id from stack
179+
VM->>DataStructureStore: Access hypergraph with id
180+
alt Hypergraph exists
181+
DataStructureStore->>HypergraphDS: add_node_to_edge(edge_id, node_id)
182+
HypergraphDS-->>DataStructureStore: Result
183+
else Hypergraph does not exist
184+
DataStructureStore-->>VM: Error: InvalidDataStructureOperation
185+
end
186+
```
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
use super::super::*;
2+
use super::super::data_structures::*;
3+
use solana_program::clock::Epoch;
4+
use solana_program::pubkey::Pubkey;
5+
use solana_program::account_info::AccountInfo;
6+
7+
fn create_test_account(lamports: u64) -> (Pubkey, Vec<u8>, AccountInfo<'static>) {
8+
let key = Pubkey::new_unique();
9+
let mut lamports = lamports;
10+
let mut data = vec![0; 32];
11+
12+
AccountInfo::new(
13+
&key,
14+
true,
15+
true,
16+
&mut lamports,
17+
&mut data,
18+
&Pubkey::new_unique(),
19+
false,
20+
Epoch::default(),
21+
)
22+
}
23+
24+
#[test]
25+
fn test_data_structure_store_new() {
26+
let program_id = Pubkey::new_unique();
27+
let (_, _, account) = create_test_account(1000000);
28+
let accounts = vec![account];
29+
30+
let vm = VM::new(&program_id, &accounts, &[]);
31+
32+
// Verify the data structures are initialized with the correct capacity
33+
assert_eq!(vm.data_structures.btrees.capacity(), MAX_DATA_STRUCTURES);
34+
assert_eq!(vm.data_structures.tries.capacity(), MAX_DATA_STRUCTURES);
35+
assert_eq!(vm.data_structures.graphs.capacity(), MAX_DATA_STRUCTURES);
36+
assert_eq!(vm.data_structures.ohlcvs.capacity(), MAX_DATA_STRUCTURES);
37+
assert_eq!(vm.data_structures.hypergraphs.capacity(), MAX_DATA_STRUCTURES);
38+
39+
// Verify the data structures are initially empty
40+
assert_eq!(vm.data_structures.btrees.len(), 0);
41+
assert_eq!(vm.data_structures.tries.len(), 0);
42+
assert_eq!(vm.data_structures.graphs.len(), 0);
43+
assert_eq!(vm.data_structures.ohlcvs.len(), 0);
44+
assert_eq!(vm.data_structures.hypergraphs.len(), 0);
45+
}
46+
47+
#[test]
48+
fn test_ensure_capacity_btree() {
49+
let program_id = Pubkey::new_unique();
50+
let (_, _, account) = create_test_account(1000000);
51+
let accounts = vec![account];
52+
53+
let mut vm = VM::new(&program_id, &accounts, &[]);
54+
55+
// Initially the btrees vector is empty
56+
assert_eq!(vm.data_structures.btrees.len(), 0);
57+
58+
// Ensure capacity for index 5
59+
vm.data_structures.ensure_capacity(DataStructureType::BTreeMap, 5);
60+
61+
// The vector should now have length 6 (indices 0-5)
62+
assert_eq!(vm.data_structures.btrees.len(), 6);
63+
64+
// All elements should be None
65+
for i in 0..6 {
66+
assert!(vm.data_structures.btrees[i].is_none());
67+
}
68+
69+
// Create a BTreeMap at index 3
70+
vm.data_structures.btrees[3] = Some(BTreeMapDS::new());
71+
72+
// Ensure capacity for index 10
73+
vm.data_structures.ensure_capacity(DataStructureType::BTreeMap, 10);
74+
75+
// The vector should now have length 11 (indices 0-10)
76+
assert_eq!(vm.data_structures.btrees.len(), 11);
77+
78+
// The BTreeMap at index 3 should still exist
79+
assert!(vm.data_structures.btrees[3].is_some());
80+
81+
// Other elements should be None
82+
for i in 0..11 {
83+
if i != 3 {
84+
assert!(vm.data_structures.btrees[i].is_none());
85+
}
86+
}
87+
}
88+
89+
#[test]
90+
fn test_ensure_capacity_all_types() {
91+
let program_id = Pubkey::new_unique();
92+
let (_, _, account) = create_test_account(1000000);
93+
let accounts = vec![account];
94+
95+
let mut vm = VM::new(&program_id, &accounts, &[]);
96+
97+
// Test ensure_capacity for all data structure types
98+
vm.data_structures.ensure_capacity(DataStructureType::BTreeMap, 3);
99+
vm.data_structures.ensure_capacity(DataStructureType::Trie, 4);
100+
vm.data_structures.ensure_capacity(DataStructureType::Graph, 5);
101+
vm.data_structures.ensure_capacity(DataStructureType::OHLCV, 6);
102+
vm.data_structures.ensure_capacity(DataStructureType::Hypergraph, 7);
103+
104+
// Verify the lengths
105+
assert_eq!(vm.data_structures.btrees.len(), 4);
106+
assert_eq!(vm.data_structures.tries.len(), 5);
107+
assert_eq!(vm.data_structures.graphs.len(), 6);
108+
assert_eq!(vm.data_structures.ohlcvs.len(), 7);
109+
assert_eq!(vm.data_structures.hypergraphs.len(), 8);
110+
}
111+
112+
#[test]
113+
fn test_data_structure_operations_with_ensure_capacity() {
114+
let program_id = Pubkey::new_unique();
115+
let (_, _, account) = create_test_account(1000000);
116+
let accounts = vec![account];
117+
118+
let mut vm = VM::new(&program_id, &accounts, &[]);
119+
120+
// Test BTreeClear with ensure_capacity
121+
vm.stack.push(Value(10)).unwrap(); // id = 10
122+
let result = vm.execute(&[OpCode::BTreeClear as u8]);
123+
assert!(result.is_ok());
124+
125+
// Verify that the BTreeMap was created at index 10
126+
assert_eq!(vm.data_structures.btrees.len(), 11);
127+
assert!(vm.data_structures.btrees[10].is_some());
128+
129+
// Test GraphCreate with ensure_capacity
130+
vm.stack.push(Value(15)).unwrap(); // id = 15
131+
let result = vm.execute(&[OpCode::GraphCreate as u8]);
132+
assert!(result.is_ok());
133+
134+
// Verify that the Graph was created at index 15
135+
assert_eq!(vm.data_structures.graphs.len(), 16);
136+
assert!(vm.data_structures.graphs[15].is_some());
137+
}

0 commit comments

Comments
 (0)