Skip to content

Commit 5335952

Browse files
committed
refactor: use ordered publisher list
This change brings down the average cost of a price update without aggregation from 5k to 2k because binary search requires much less lookups. There are mechanims implemented in the code to make sure that upon the upgrade we sort the array without adding overhead to the happy path (working binary search). Also add_publisher now does an insertion sort to keep the list sorted and del_publisher won't change the order of the list.
1 parent ceb4a32 commit 5335952

File tree

2 files changed

+57
-12
lines changed

2 files changed

+57
-12
lines changed

program/rust/src/processor/add_publisher.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,21 @@ pub fn add_publisher(
7373
}
7474
}
7575

76-
let current_index: usize = try_convert(price_data.num_)?;
76+
let mut current_index: usize = try_convert(price_data.num_)?;
7777
sol_memset(
7878
bytes_of_mut(&mut price_data.comp_[current_index]),
7979
0,
8080
size_of::<PriceComponent>(),
8181
);
8282
price_data.comp_[current_index].pub_ = cmd_args.publisher;
83+
84+
while current_index > 0
85+
&& price_data.comp_[current_index].pub_ < price_data.comp_[current_index - 1].pub_
86+
{
87+
price_data.comp_.swap(current_index, current_index - 1);
88+
current_index -= 1;
89+
}
90+
8391
price_data.num_ += 1;
8492
price_data.header.size = try_convert::<_, u32>(PriceAccount::INITIAL_SIZE)?;
8593
Ok(())

program/rust/src/processor/upd_price.rs

+48-11
Original file line numberDiff line numberDiff line change
@@ -127,27 +127,64 @@ pub fn upd_price(
127127
// Check clock
128128
let clock = Clock::from_account_info(clock_account)?;
129129

130-
let mut publisher_index: usize = 0;
130+
let publisher_index: usize;
131131
let latest_aggregate_price: PriceInfo;
132132

133133
// The price_data borrow happens in a scope because it must be
134134
// dropped before we borrow again as raw data pointer for the C
135135
// aggregation logic.
136136
{
137137
// Verify that symbol account is initialized
138-
let price_data = load_checked::<PriceAccount>(price_account, cmd_args.header.version)?;
138+
let mut price_data = load_checked::<PriceAccount>(price_account, cmd_args.header.version)?;
139139

140-
// Verify that publisher is authorized
141-
while publisher_index < try_convert::<u32, usize>(price_data.num_)? {
142-
if price_data.comp_[publisher_index].pub_ == *funding_account.key {
143-
break;
140+
// Verify that publisher is authorized by initially binary searching
141+
// for the publisher's component in the price account. The binary
142+
// search might not work if the publisher list is not sorted; therefore
143+
// we fall back to a linear search and during the search we try to bring
144+
// the publishers list closer to a sorted state. (We do not sort them
145+
// entirely to avoid exceeding the compute budget.)
146+
let mut lo = 0;
147+
let mut hi = try_convert::<u32, usize>(price_data.num_)?;
148+
149+
let mut index = None;
150+
while lo < hi {
151+
let mid = lo + (hi - lo) / 2;
152+
match price_data.comp_[mid].pub_.cmp(funding_account.key) {
153+
std::cmp::Ordering::Less => {
154+
lo = mid + 1;
155+
}
156+
std::cmp::Ordering::Greater => {
157+
hi = mid;
158+
}
159+
std::cmp::Ordering::Equal => {
160+
index = Some(mid);
161+
break;
162+
}
144163
}
145-
publisher_index += 1;
146164
}
147-
pyth_assert(
148-
publisher_index < try_convert::<u32, usize>(price_data.num_)?,
149-
OracleError::PermissionViolation.into(),
150-
)?;
165+
166+
publisher_index = match index {
167+
Some(index) => index,
168+
None => {
169+
let mut index = 0;
170+
while index < try_convert::<u32, usize>(price_data.num_)? {
171+
if price_data.comp_[index].pub_ == *funding_account.key {
172+
break;
173+
}
174+
if index > 0 && price_data.comp_[index].pub_ < price_data.comp_[index - 1].pub_
175+
{
176+
price_data.comp_.swap(index, index - 1);
177+
}
178+
index += 1;
179+
}
180+
pyth_assert(
181+
index < try_convert::<u32, usize>(price_data.num_)?,
182+
OracleError::PermissionViolation.into(),
183+
)?;
184+
index
185+
}
186+
};
187+
151188

152189
latest_aggregate_price = price_data.agg_;
153190
let latest_publisher_price = price_data.comp_[publisher_index].latest_;

0 commit comments

Comments
 (0)