Skip to content

Commit

Permalink
Mkl neo swaps benchmarks (#1390)
Browse files Browse the repository at this point in the history
* clean up TODO

* First benchmark

* .

* .

* .

* Sceond bench

* Use weights

* Use asset_count in deploy_combinatorial_pool

* .

* .

* Use weights
  • Loading branch information
maltekliemann authored Oct 30, 2024
1 parent 6055847 commit 790906f
Show file tree
Hide file tree
Showing 6 changed files with 415 additions and 166 deletions.
156 changes: 155 additions & 1 deletion zrml/neo-swaps/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
use super::*;
use crate::{
liquidity_tree::{traits::LiquidityTreeHelper, types::LiquidityTree},
traits::{LiquiditySharesManager, PoolOperations},
traits::{LiquiditySharesManager, PoolOperations, PoolStorage},
types::DecisionMarketOracle,
AssetOf, BalanceOf, MarketIdOf, Pallet as NeoSwaps, Pools, MIN_SPOT_PRICE,
};
Expand Down Expand Up @@ -493,6 +493,160 @@ mod benchmarks {
);
}

// Remark on benchmarks for combinatorial pools: Combinatorial buying, selling and deploying
// pools depends on the number of assets as well as the number of markets. But these parameters
// depend on each other (the more markets, the more assets). The benchmark parameter is the
// market count and the logarithm of the number of assets. This maximizes the number of markets
// per asset.

#[benchmark]
fn combo_buy(n: Linear<1, 7>) {
let market_count = n;

let alice: T::AccountId = whitelisted_caller();
let base_asset = Asset::Ztg;
let asset_count = 2u16.pow(market_count);

let mut market_ids = vec![];
for _ in 0..market_count {
let market_id = create_market::<T>(alice.clone(), base_asset, 2);
market_ids.push(market_id);
}

let amount = _100.saturated_into();
let total_cost = amount + T::MultiCurrency::minimum_balance(base_asset);
assert_ok!(T::MultiCurrency::deposit(base_asset, &alice, total_cost));
assert_ok!(NeoSwaps::<T>::deploy_combinatorial_pool(
RawOrigin::Signed(alice).into(),
asset_count,
market_ids,
amount,
create_spot_prices::<T>(asset_count),
CENT.saturated_into(),
false,
));

let pool_id = 0u8.into();
let pool = <Pallet<T> as PoolStorage>::get(pool_id).unwrap();
let assets = pool.assets();

let amount_in = _1.saturated_into();
let min_amount_out = Zero::zero();

// Work is maximized by having no keep indicies.
let middle = asset_count / 2;
let buy_arg = (0..middle).map(|i| assets[i as usize]).collect::<Vec<_>>();
let sell_arg = (middle..asset_count).map(|i| assets[i as usize]).collect::<Vec<_>>();

let helper = BenchmarkHelper::<T>::new();
let bob = helper.accounts().next().unwrap();
assert_ok!(T::MultiCurrency::deposit(base_asset, &bob, amount_in));

#[extrinsic_call]
_(
RawOrigin::Signed(bob),
pool_id,
asset_count,
buy_arg,
sell_arg,
amount_in,
min_amount_out,
);
}

#[benchmark]
fn combo_sell(n: Linear<1, 7>) {
let market_count = n;

let alice: T::AccountId = whitelisted_caller();
let base_asset = Asset::Ztg;
let asset_count = 2u16.pow(market_count);

let mut market_ids = vec![];
for _ in 0..market_count {
let market_id = create_market::<T>(alice.clone(), base_asset, 2);
market_ids.push(market_id);
}

let amount = _100.saturated_into();
let total_cost = amount + T::MultiCurrency::minimum_balance(base_asset);
assert_ok!(T::MultiCurrency::deposit(base_asset, &alice, total_cost));
assert_ok!(NeoSwaps::<T>::deploy_combinatorial_pool(
RawOrigin::Signed(alice).into(),
asset_count,
market_ids,
amount,
create_spot_prices::<T>(asset_count),
CENT.saturated_into(),
false,
));

let pool_id = 0u8.into();
let pool = <Pallet<T> as PoolStorage>::get(pool_id).unwrap();
let assets = pool.assets();

// Work is maximized by having as few sell indices as possible.
let buy_arg = vec![assets[0]];
let sell_arg = vec![assets[1]];
let keep_arg = (2..asset_count).map(|i| assets[i as usize]).collect::<Vec<_>>();

let amount_buy: BalanceOf<T> = _2.saturated_into();
let amount_keep = if keep_arg.is_empty() {
// If n = 1;
Zero::zero()
} else {
_1.saturated_into()
};
let min_amount_out = Zero::zero();

let helper = BenchmarkHelper::<T>::new();
let bob = helper.accounts().next().unwrap();

// We don't care about being precise here and just deposit a huge bunch of tokens for Bob.
for &asset in assets.iter() {
let amount_for_bob = amount_buy.max(amount_buy);
assert_ok!(T::MultiCurrency::deposit(asset, &bob, amount_for_bob));
}

#[extrinsic_call]
_(
RawOrigin::Signed(bob),
pool_id,
asset_count,
buy_arg,
keep_arg,
sell_arg,
amount_buy,
amount_keep,
min_amount_out,
);
}

#[benchmark]
fn deploy_combinatorial_pool(n: Linear<1, 7>) {
let market_count = n;

let alice: T::AccountId = whitelisted_caller();
let base_asset = Asset::Ztg;
let asset_count = 2u16.pow(market_count);

let mut market_ids = vec![];
for _ in 0..market_count {
let market_id = create_market::<T>(alice.clone(), base_asset, 2);
market_ids.push(market_id);
}

let amount = _100.saturated_into();
let total_cost = amount + T::MultiCurrency::minimum_balance(base_asset);
assert_ok!(T::MultiCurrency::deposit(base_asset, &alice, total_cost));

let spot_prices = create_spot_prices::<T>(asset_count);
let swap_fee = CENT.saturated_into();

#[extrinsic_call]
_(RawOrigin::Signed(alice), asset_count, market_ids, amount, spot_prices, swap_fee, true);
}

#[benchmark]
fn decision_market_oracle_evaluate() {
let alice = whitelisted_caller();
Expand Down
41 changes: 22 additions & 19 deletions zrml/neo-swaps/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ mod pool_storage;
mod tests;
pub mod traits;
pub mod types;
mod utility;
pub mod weights;

pub use pallet::*;
Expand All @@ -46,6 +47,7 @@ mod pallet {
math::{traits::MathOps, types::Math},
traits::{LiquiditySharesManager, PoolOperations, PoolStorage},
types::{FeeDistribution, MaxAssets, Pool, PoolType},
utility::LogCeil,
weights::*,
};
use alloc::{
Expand Down Expand Up @@ -428,7 +430,7 @@ mod pallet {
/// Depends on the implementation of `CompleteSetOperationsApi` and `ExternalFees`; when
/// using the canonical implementations, the runtime complexity is `O(asset_count)`.
#[pallet::call_index(0)]
#[pallet::weight(T::WeightInfo::buy((*asset_count).saturated_into()))] // TODO Use into()
#[pallet::weight(T::WeightInfo::buy((*asset_count).into()))]
#[transactional]
pub fn buy(
origin: OriginFor<T>,
Expand Down Expand Up @@ -478,7 +480,7 @@ mod pallet {
/// Depends on the implementation of `CompleteSetOperationsApi` and `ExternalFees`; when
/// using the canonical implementations, the runtime complexity is `O(asset_count)`.
#[pallet::call_index(1)]
#[pallet::weight(T::WeightInfo::sell((*asset_count).saturated_into()))] // TODO Use `into()`
#[pallet::weight(T::WeightInfo::sell((*asset_count).into()))]
#[transactional]
pub fn sell(
origin: OriginFor<T>,
Expand Down Expand Up @@ -679,9 +681,9 @@ mod pallet {
Ok(Some(T::WeightInfo::deploy_pool(spot_prices_len)).into())
}

#[allow(clippy::too_many_arguments)] // TODO Bundle `buy`/`keep`/`sell` into one arg.
#[allow(clippy::too_many_arguments)]
#[pallet::call_index(6)]
#[pallet::weight(T::WeightInfo::buy((*asset_count).into()))] // TODO
#[pallet::weight(T::WeightInfo::combo_buy(asset_count.log_ceil().into()))]
#[transactional]
pub fn combo_buy(
origin: OriginFor<T>,
Expand All @@ -691,7 +693,7 @@ mod pallet {
sell: Vec<AssetOf<T>>,
#[pallet::compact] amount_in: BalanceOf<T>,
#[pallet::compact] min_amount_out: BalanceOf<T>,
) -> DispatchResultWithPostInfo {
) -> DispatchResult {
let who = ensure_signed(origin)?;

let pool = <Self as PoolStorage>::get(pool_id)?;
Expand All @@ -700,14 +702,12 @@ mod pallet {
asset_count_real.try_into().map_err(|_| Error::<T>::NarrowingConversion)?;
ensure!(asset_count == asset_count_real_u16, Error::<T>::IncorrectAssetCount);

Self::do_combo_buy(who, pool_id, buy, sell, amount_in, min_amount_out)?;

Ok(Some(T::WeightInfo::buy(asset_count.into())).into()) // TODO
Self::do_combo_buy(who, pool_id, buy, sell, amount_in, min_amount_out)
}

#[allow(clippy::too_many_arguments)] // TODO Bundle `buy`/`keep`/`sell` into one arg.
#[allow(clippy::too_many_arguments)]
#[pallet::call_index(7)]
#[pallet::weight(T::WeightInfo::buy((*asset_count).saturated_into()))] // TODO
#[pallet::weight(T::WeightInfo::combo_sell(asset_count.log_ceil().into()))]
#[transactional]
pub fn combo_sell(
origin: OriginFor<T>,
Expand All @@ -719,7 +719,7 @@ mod pallet {
#[pallet::compact] amount_buy: BalanceOf<T>,
#[pallet::compact] amount_keep: BalanceOf<T>,
#[pallet::compact] min_amount_out: BalanceOf<T>,
) -> DispatchResultWithPostInfo {
) -> DispatchResult {
let who = ensure_signed(origin)?;

let pool = <Self as PoolStorage>::get(pool_id)?;
Expand All @@ -737,16 +737,15 @@ mod pallet {
amount_buy,
amount_keep,
min_amount_out,
)?;

Ok(Some(T::WeightInfo::buy(asset_count.into())).into()) // TODO
)
}

#[pallet::call_index(8)]
#[pallet::weight({0})] // TODO
#[pallet::weight(T::WeightInfo::deploy_combinatorial_pool(asset_count.log_ceil().into()))]
#[transactional]
pub fn deploy_combinatorial_pool(
origin: OriginFor<T>,
asset_count: AssetIndexType,
market_ids: Vec<MarketIdOf<T>>,
amount: BalanceOf<T>,
spot_prices: Vec<BalanceOf<T>>,
Expand All @@ -755,6 +754,13 @@ mod pallet {
) -> DispatchResult {
let who = ensure_signed(origin)?;

let mut real_asset_count = 1u16;
for market_id in market_ids.iter() {
let market = T::MarketCommons::market(market_id)?;
real_asset_count = real_asset_count.saturating_mul(market.outcomes());
}
ensure!(asset_count == real_asset_count, Error::<T>::IncorrectAssetCount);

Self::do_deploy_combinatorial_pool(
who,
market_ids,
Expand Down Expand Up @@ -1221,7 +1227,6 @@ mod pallet {
);
}

// TODO This is where the common code begins!
let (liquidity_parameter, amounts_in) =
Math::<T>::calculate_reserves_from_spot_prices(amount, spot_prices)?;
ensure!(
Expand Down Expand Up @@ -1273,12 +1278,11 @@ mod pallet {
Ok(())
}

#[allow(clippy::too_many_arguments)] // TODO Bundle `buy`/`keep`/`sell` into one arg.
#[allow(clippy::too_many_arguments)]
#[require_transactional]
pub(crate) fn do_combo_buy(
who: T::AccountId,
pool_id: T::PoolId,
// TODO Replace `buy`/`keep`/`sell` with a struct.
buy: Vec<AssetOf<T>>,
sell: Vec<AssetOf<T>>,
amount_in: BalanceOf<T>,
Expand Down Expand Up @@ -1371,7 +1375,6 @@ mod pallet {
})
}

// TODO Replace `buy`/`keep`/`sell` with a struct.
#[allow(clippy::too_many_arguments)]
#[require_transactional]
pub(crate) fn do_combo_sell(
Expand Down
Loading

0 comments on commit 790906f

Please sign in to comment.