Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion pallets/subtensor/src/benchmarks/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,10 +301,48 @@ mod pallet_benchmarks {
let amount: u64 = 100_000_000_000_000u64.saturating_mul(2);
add_balance_to_coldkey_account::<T>(&coldkey, amount.into());

frame_system::Pallet::<T>::set_block_number(1u32.into());
fill_subnets_below_limit::<T>(&account("FillOwner", 0, seed));

#[extrinsic_call]
_(RawOrigin::Signed(coldkey.clone()), hotkey.clone());
}

/// The queueing outcome of `do_register_network`: at `SubnetLimit` the call prunes a subnet and
/// queues rather than creating one. Not an extrinsic; it exists so the dispatches that can reach
/// this branch have a worst case to charge.
///
/// This also covers the third outcome, `wait_to_cleanup`. That path reaches the same queueing
/// block without running `get_network_to_prune` or `do_dissolve_network`, so at equal queue
/// depth it is strictly cheaper than this one. Queue depth itself is measured at neither: both
/// queues are decoded unconditionally on every call and neither is a `BoundedVec`, so there is
/// no maximum to benchmark against.
#[benchmark]
fn register_network_pruning() {
let seed: u32 = 1;
let coldkey: T::AccountId = account("Test", 0, seed);
let hotkey: T::AccountId = account("TestHotkey", 0, seed);

Subtensor::<T>::set_network_rate_limit(1);
let amount: u64 = 100_000_000_000_000u64.saturating_mul(2);
add_balance_to_coldkey_account::<T>(&coldkey, amount.into());

frame_system::Pallet::<T>::set_block_number(1u32.into());
fill_subnets_to_limit::<T>(&account("FillOwner", 0, seed));

#[block]
{
assert_ok!(Subtensor::<T>::do_register_network(
RawOrigin::Signed(coldkey.clone()).into(),
&hotkey,
1,
Some(max_subnet_identity()),
));
}

assert!(!NetworkRegistrationQueue::<T>::get().is_empty());
}

#[benchmark]
fn commit_weights() {
let tempo: u16 = 1;
Expand Down Expand Up @@ -1449,13 +1487,16 @@ mod pallet_benchmarks {
fn register_network_with_identity() {
let coldkey: T::AccountId = whitelisted_caller();
let hotkey: T::AccountId = account("Alice", 0, 1);
let identity: Option<SubnetIdentityOfV3> = None;
let identity: Option<SubnetIdentityOfV3> = Some(max_subnet_identity());

Subtensor::<T>::set_network_registration_allowed(1.into(), true);
Subtensor::<T>::set_network_rate_limit(1);
let amount: u64 = 9_999_999_999_999;
add_balance_to_coldkey_account::<T>(&coldkey, amount.into());

frame_system::Pallet::<T>::set_block_number(1u32.into());
fill_subnets_below_limit::<T>(&account("FillOwner", 0, 1));

#[extrinsic_call]
_(
RawOrigin::Signed(coldkey.clone()),
Expand Down
80 changes: 80 additions & 0 deletions pallets/subtensor/src/benchmarks/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,86 @@ pub(super) fn set_reserves<T: Config>(
SubnetAlphaIn::<T>::insert(netuid, alpha_in);
}

/// The largest identity `is_valid_subnet_identity` accepts: 6,656 bytes across the eight fields.
///
/// Both registration outcomes carry the identity. Creation writes it to `SubnetIdentitiesV3`;
/// queueing encodes it into the `NetworkRegistrationQueue` entry and again into the event. `None`
/// measures neither.
pub(super) fn max_subnet_identity() -> SubnetIdentityOfV3 {
SubnetIdentityOfV3 {
subnet_name: vec![b'n'; 256],
github_repo: vec![b'g'; 1024],
subnet_contact: vec![b'c'; 1024],
subnet_url: vec![b'u'; 1024],
discord: vec![b'd'; 256],
description: vec![b'e'; 1024],
logo_url: vec![b'l'; 1024],
additional: vec![b'a'; 1024],
}
}

/// Fill the chain with prunable subnets so a registration benchmark runs against a realistic
/// `NetworksAdded` map rather than an empty one.
///
/// `do_register_network` counts every entry in `NetworksAdded` on every call, and once that count
/// reaches `SubnetLimit` it also walks `get_network_to_prune`. Registering into an empty chain
/// measures neither. Callers pick which outcome they are measuring via `subnets`: below the limit
/// the call creates a subnet (many writes), at the limit it prunes and queues instead (far more
/// reads). Neither dominates the other.
///
/// Immunity is set to one block and `NetworkRegisteredAt` to zero so no candidate is skipped.
/// Requires a current block above zero.
///
/// Every subnet is made dynamic with a funded pool, and both prices are staggered per subnet.
///
/// Two independent scans walk `NetworksAdded`, and a fixture of stable, empty-pool subnets makes
/// both of them exit early on every entry:
///
/// - `get_network_to_prune` calls `get_moving_alpha_price`, which returns on `SubnetMechanism == 0`
/// without reading `SubnetMovingPrice`.
/// - `get_median_subnet_alpha_price` calls swap `current_price`, which returns on a non-dynamic
/// mechanism, and on a dynamic one still returns before reading the TAO reserve or the balancer
/// unless the alpha reserve is nonzero.
///
/// So the scans would walk 128 entries while pricing none, which is the same class of mistake as
/// benchmarking against an empty chain. Staggering matters as well as seeding: the median builds a
/// `BTreeMap` keyed on price, and identical reserves collapse it to a single entry.
pub(super) fn fill_subnets<T: Config>(owner: &T::AccountId, subnets: u16) {
NetworkImmunityPeriod::<T>::set(1);

for netuid in 1..=subnets {
let offset = u64::from(netuid);
let netuid = NetUid::from(netuid);

Subtensor::<T>::init_new_network(netuid, 1);
NetworkRegisteredAt::<T>::insert(netuid, 0);
SubnetOwner::<T>::insert(netuid, owner.clone());
SubnetMechanism::<T>::insert(netuid, 1);
set_reserves::<T>(
netuid,
TaoBalance::from(150_000_000_000_u64.saturating_add(offset.saturating_mul(1_000_000))),
AlphaBalance::from(100_000_000_000_u64),
);
SubnetMovingPrice::<T>::insert(
netuid,
I96F32::saturating_from_num(1_000_u64.saturating_add(offset))
.saturating_div(I96F32::saturating_from_num(1_000)),
);
}
}

/// One below `SubnetLimit`: the registration still creates a subnet, but pays the full count.
pub(super) fn fill_subnets_below_limit<T: Config>(owner: &T::AccountId) {
let limit = Subtensor::<T>::get_max_subnets();
fill_subnets::<T>(owner, limit.saturating_sub(1));
}

/// At `SubnetLimit`: the registration prunes a subnet and queues instead of creating one.
pub(super) fn fill_subnets_to_limit<T: Config>(owner: &T::AccountId) {
let limit = Subtensor::<T>::get_max_subnets();
fill_subnets::<T>(owner, limit);
}

pub(super) fn benchmark_registration_burn() -> TaoBalance {
TaoBalance::from(1_000_000)
}
Expand Down
12 changes: 10 additions & 2 deletions pallets/subtensor/src/macros/dispatches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1001,8 +1001,13 @@ mod dispatches {
}

/// User register a new subnetwork
///
/// Below `SubnetLimit` this creates a subnet; at the limit it either prunes one and queues,
/// or queues to wait on a cleanup already in flight. The first two are mutually exclusive
/// and neither dominates, so charge the worse; the third is dominated by the second.
#[pallet::call_index(59)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::register_network())]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::register_network()
.max(<T as crate::pallet::Config>::WeightInfo::register_network_pruning()))]
pub fn register_network(origin: OriginFor<T>, hotkey: T::AccountId) -> DispatchResult {
Self::do_register_network(origin, &hotkey, 1, None)
}
Expand Down Expand Up @@ -1181,8 +1186,11 @@ mod dispatches {
}

/// User register a new subnetwork
///
/// Same outcomes as `register_network`, so the same worst case applies.
#[pallet::call_index(79)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::register_network_with_identity())]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::register_network_with_identity()
.max(<T as crate::pallet::Config>::WeightInfo::register_network_pruning()))]
pub fn register_network_with_identity(
origin: OriginFor<T>,
hotkey: T::AccountId,
Expand Down
Loading