Benchmarks pallet-membership et dépendance à pallet-certification

Pour la règle de distance que j’ai développée (voir Calcul de distance via oracle - #19 by cgeek), j’aimerais réaliser des benchmarks sur un Runtime WASM à la fois pour vérifier le temps de traitement maximal (spoil : en mode natif sur ma machine je suis proche des 100ms dans le pire des cas de distance de la WoT G1, donc je poursuis le développement sans Oracle) et pour affecter un poids adéquat.

Comme la règle de distance serait appelée uniquement sur adhésion ou son renouvellement (comme Duniter v1), l’emplacement indiqué serait la méthode check_idty_allowed_to_claim_membership qui se trouve implémentée par la pallet duniter-wot, elle-même dépendante de la pallet certification.

Oui mais : comment puis-je modifier le fichier pallets/membership/src/benchmark.rs pour y introduire ces pallet duniter-wot et certification ?

Sauf erreur de ma part, aujourd’hui tous les benchmarks sont réalisés uniquement sur pallet isolée, n’est-ce pas ?

cc @bgallois @HugoTrentesaux

2 Likes

Actuellement les benchmarks sont réalisés sur pallet “isolée”, c’est-à-dire qu’il n’y a pas de couplage fort entre les pallets pour le benchmark (voir !138 pour une explication de la création de gdev-benchmark et le couplage des pallets identity, certification et wot).

Mais il reste quand même un couplage faible entre les pallets. La pallet membership est couplée à duniter_wot via CheckCallAllowed par exemple. Ainsi une fonction ajoutée à check_idty_allowed_to_claim_membership de la pallet duniter_wot sera benchmarkée avec l’extrinsic claim_membership de la pallet membership dans le runtime.

2 Likes

Effectivement, il va falloir trouver une solution pour le benchmark des fonctions couplées. Il y a check_idty_allowed_to_claim_membership et aussi check_idty_allowed_to_renew_membership j’imagine.

Je me demande s’il existe un concept de benchmark au niveau du runtime. En tout cas il y a plein de choses à explorer dans la sous-commande benchmark :

./target/release/duniter benchmark --help
Sub-commands concerned with benchmarking. The pallet benchmarking moved to the `pallet` sub-command

Usage: duniter benchmark <COMMAND>

Commands:
  pallet     Benchmark the extrinsic weight of FRAME Pallets
  storage    Benchmark the storage speed of a chain snapshot
  overhead   Benchmark the execution overhead per-block and per-extrinsic
  block      Benchmark the execution time of historic blocks
  machine    Command to benchmark the hardware
  extrinsic  Benchmark the execution time of different extrinsics
  help       Print this message or the help of the given subcommand(s)

Options:
  -h, --help     Print help information
  -V, --version  Print version information

Et si jamais on ne trouve pas de solution satisfaisante, on peut toujours benchmarker individuellement les bouts et utiliser comme poids final la somme des poids des bouts. Il faut pas trop se sentir bloqué par la question des benchmark, puisque le but final est juste d’obtenir une borne haute approximative d’une mesure.
En l’occurrence, si on peut benchmarker la règle de distance à part, on peut ajouter son poids à toutes les fonctions qui l’appellent, directement ou non.


[edit] je découvre cette page de documentation https://docs.substrate.io/build/pallet-coupling/

1 Like

Suite à vos réponses, je me suis orienté vers un test sur la pallet-certification en créant un extrinsic de calcul de distance et en ajoutant au GenesisConfig un paramètre permettant d’initialiser le module de calcul avec des données pré-renseignées avec, dans le cas du runtime gdev-benchmark, la WoT G1.

J’ai commencé à benchmarker, mais je suis surpris du résultat :

/// Weight functions for `pallet_certification`.
pub struct WeightInfo<T>(PhantomData<T>);
impl<T: frame_system::Config> pallet_certification::WeightInfo for WeightInfo<T> {
	// Storage: Cert BinWoT (r:1 w:0)
	// Storage: Membership CounterForMembership (r:1 w:0)
	// Storage: Parameters ParametersStorage (r:1 w:0)
	/// The range of component `i` is `[1, 12533]`.
	/// The range of component `i` is `[1, 12533]`.
	fn compute_distance_bench(_i: u32, ) -> Weight {
		// Minimum execution time: 1_390_000 nanoseconds.
		Weight::from_ref_time(210_319_628_642 as u64)
			.saturating_add(T::DbWeight::get().reads(3 as u64))
	}
}

En fait je ne sais pas comment interpréter 210_319_628_642. Si ce sont des nanosecondes, ça voudrait dire un ref_time à 210s (presque 4 minutes), alors que le benchmark est terminé en 10 secondes :thinking:, ça ne colle pas. Ou alors c’est une projection pessimiste ?

À chaque fois le x de // Minimum execution time: x nanoseconds. est un peu plus de 1000× plus petit que le y de Weight::from_ref_time(y), ça doit vouloir dire 210ms ? y doit être le poids (moins le coût des 3 accès db). Sur 2000 milliards par bloc, on doit donc pouvoir faire au mieux 9 évaluations par bloc.

Tu as lancé le benchmark sur une machine de quelle puissance, comparée à celle du RPi4 qui est notre machine de référence ?

1 Like

Sur un Mac M1 Max. Ça risque d’être juste pour le Raspberry.

Édit : quoique. J’ai un Raspberry Pi 4 chez moi, je testerai.

J’ai pu tester ce week-end sur Raspberry PI 4 Model B.

Mais au préalable j’ai revu mon test pour focaliser sur un membre en particulier (ID 6550 dans le module de WoT) qui possède actuellement le plus gros temps de calcul de distance.

Résultats

Sur M1

// Minimum execution time: 193_857_000 nanoseconds.
Weight::from_ref_time(194_093_000_000 as u64)

Sur Raspberry PI 4

// Minimum execution time: 979_537_765 nanoseconds.
Weight::from_ref_time(980_609_090_000 as u64)

Soit 194ms sur M1 et 980ms sur Raspberry PI. Ce dernier est 5 fois plus lent, mais ça passe :slight_smile:

:information_source: Mon Rapsberry n’a toutefois pas de SSD. Néanmoins le ici problème ici étant le CPU, le résultat ne devrait pas beaucoup varier. Mais je tenterai avec un SSD pour connaître l’ordre de grandeur. Et pour disposer moi aussi de la machine de référence et commiter les poids.

Conclusion

A priori donc, nous ne sommes pas obligés d’utiliser un Oracle pour la règle de distance. Toutefois :

  1. Nous serons limités à 1 ou 2 calculs de distance par bloc;
  2. A mesure que la toile grossira, le poids devra être réévalué et recalibré;
  3. A moyen terme, l’Oracle restera optionnel si l’on upgrade la machine de référence;
  4. Sinon, ainsi qu’à long terme, un Oracle deviendra nécessaire.
6 Likes

Résultat :

// Minimum execution time: 979_508_277 nanoseconds.
Weight::from_ref_time(980_400_732_000 as u64)

Dans le calcul de WoT on gagne à peine 0,200ms avec un SSD. C’est négligeable.

1 Like

À 14400 blocs par jour, si on imagine 100 000 membres, et une ré-évaluation de la règle de distance tous les 6 mois, ça fait mois de 1% des blocs “sacrifiés” pour la règle de distance, c’est gérable.

Par contre, il faut une manière de s’assurer qu’un entrant ne peut pas déclencher l’évaluation de la règle de distance à tous les blocs, par exemple interdire la ré-évaluation si aucune nouvelle certification n’a été reçue depuis l’évaluation précédente.

1 Like

J’espère bien qu’on sera passé sur un Oracle d’ici là :slight_smile:

C’est censé être le rôle de msPeriod dans le protocole Duniter v1, traduit en MembershipPeriod dans Duniter V2S. Mais je ne sais pas dire si cette fonctionnalité est bien couverte par un test.

Parfois, il suffit qu’un certificateur voit sa qualité augmenter pour que cela passe. Donc cette proposition n’est pas bonne. Un délai entre deux réévaluations serait plus pertinent.

3 Likes

Une évaluation négative peut apporter un malus (taxe, délai pour la prochaine évaluation…). Le client devrait faire le calcul et vérifier qu’il passe avant de le demander en blockchain, parce que la blockchain n’a aucun intérêt à calculer des distances qui ne passent pas.

On n’aurait pas le droit de demander l’évaluation un certain temps après une évaluation positive.

2 Likes

Il existe tout de même les frais d’extrinsics, qui seront élevés à proportion du poids. Et si j’ai bien compris, pour un membre les frais seront remboursés si et seulement si l’extrinsic n’échoue pas.

Donc le spam d’évaluation négative est “géré” de ce point de vue.

Par contre le spam d’évaluation positive (gratuite !) ne l’est pas. Je viens de créer un ticket #113 pour détailler et tracer.

2 Likes

Il y a en effet un problème dans le vocabulaire choisi par elois, il y a certaines choses qui s’appelle “Period” et d’autres qui s’appellent “Validity”. Dans le code MembershipPeriod est défini comme la durée de validité d’une adhésion :

/// Maximum life span of a non-renewable membership (in number of blocks)
type MembershipPeriod: Get<Self::BlockNumber>;

Pour moi il y a deux types de limitations en fréquence :

  • celles qui ont un sens du point de vue des règles, comme le délai entre deux certifications (nouvelle ou renouvellement)
  • celles qui n’ont pas de sens comme le délai entre deux renouvellements d’adhésion

Pour le calcul de la règle de distance qu’on sait coûteuse en calcul, on veut se protéger d’un spam parce qu’on veut éviter que certaines personnes s’accaparent la capacité de calcul de la blockchain. Mais donc ça revient à la question de comment on partage une ressource commune (Comment partager équitablement cette ressource commune qu'est la blockchain Ğ1?).

Même raisonnement pour un certificateur de certificateur. Si qqun atteint 80% de la toile à 3 pas, chacune de ses certifications suffit au certifié pour atteindre 80% de la toile à 4 pas et donc à 5 pas pour les personnes qu’il certifie. Donc si on veut coller au plus juste, il faut recalculer l’ensemble des distances des “dossiers en cours” à chaque nouvelle certification. D’où l’idée de trouver un intermédiaire qui soit réaliste du point de vue du calcul demandé à la blockchain.

Ce serait dommage d’infliger ça aux membres qui souhaitent rentrer et n’ont pas encore de DU ni de bonne compréhension. D’où ma proposition qui a le mérite de simplifier : si aucun des 5 certificateurs n’a fait l’effort d’expliquer la règle de distance et d’utiliser les simulateurs disponibles pour l’anticiper, alors une certification supplémentaire serait de toute façon bienvenue.

Ou alors on autorise de faire réévaluer la règle de distance par quelqu’un d’autre, moyennant un frais important.

Je n’ai pas de solution idéale en tête mais je pense qu’on s’approche de proposition faciles à implémenter et raisonablement satisfaisantes :slight_smile:

1 Like

C’est pour ça que le client devrait faire le calcul avant de demander au réseau de le vérifier : le rôle du réseau n’est pas de faire les calculs mais juste de les vérifier. Or il se trouve que le seul moyen de vérifier ce calcul est de le reproduire.

Mais ce malus particulier n’est pas utile si on a un quota de remboursement pour les membres, la possibilité de demander l’évaluation pour une identité qui n’appartient pas à l’appelant, et un délai entre les évaluations.

3 Likes

Le calcul de distance n’est pas juste un booléen, il y a plusieurs données disponibles :

Ok(WotDistance {
  sentries,
  reached: area.len() as u32 - 1,
  reached_at_border: border.len() as u32,
  success,
  success_at_border,
  outdistanced: success < required_sentries,
})

Un client qui effectuerait le calcul (en tapant dans Storage.cert.binWot() pour obtenir la toile et qui inclurait le module de WoT Rust (compilé en Wasm, ou bien réécrit dans le langage cible du client) aurait déjà ce genre d’informations et pourrait juger de la probabilité d’échec.

Et si l’on ne veut pas que ce soit une probabilité, il faut revoir les modalités de calcul. Par exemple, que la toile de calcul ne soit mise à jour que tous les X blocs et que les extrinsics liés à la distance incluent un intervalle de blocs de validité, de sorte que la responsabilité de l’échec de l’extrinsic pour motif d’intervalle soit à imputer au nœud et pas à l’utilisateur, tandis que l’échec de distance soit de la responsabilité du client.