---
title: "Booster les Boucles Chaudes Rust : Réduire les Branch Mispredictions"
description: "Optimisation bas niveau en Rust, se concentrant sur la minimisation des branch mispredictions dans les boucles critiques en performance"
url: "https://mayorana.ch/fr/blog/branch-prediction-rust"
language: "fr"
date: "2025-11-17"
author: "Mohamed Bennekrouf"
tags:
  - "rust"
  - "optimization"
  - "branch-prediction"
  - "performance"
  - "profiling"
en_version: "https://mayorana.ch/en/blog/branch-prediction-rust.md"
---

# Booster les Boucles Chaudes Rust : Réduire les Branch Mispredictions

Les branch mispredictions surviennent quand le branch predictor du CPU devine incorrectement si un saut conditionnel (ex : d'un `if`) est pris, causant des stalls de pipeline et coûtant des cycles (10-20 cycles par misprediction sur les CPUs modernes). Dans une boucle chaude critique en performance en Rust, je restructurerais le code pour minimiser ou éliminer les branches, exploitant les fonctionnalités de Rust, et utiliserais des outils de profiling pour confirmer des améliorations mesurables dans l'efficacité du pipeline CPU.

<div class="svg-container" style="margin:2rem 0;">
<svg class="lo4-fig" viewBox="0 0 800 260" width="100%" style="height:auto;max-width:780px;display:block;margin:0 auto;" role="img" aria-label="Diagramme de pipeline comparant un if branché causant un flush de misprediction à une version branchless qui garde le pipeline plein">
<style>
.lo4-fig{--bg:#f8fafc;--box:#ffffff;--tx:#1e293b;--mut:#64748b;--ln:#cbd5e1;--ac:#FF6B00}
:root.dark .lo4-fig,[data-theme="dark"] .lo4-fig{--bg:#0f172a;--box:#1e293b;--tx:#f8fafc;--mut:#94a3b8;--ln:#475569}
.lo4-fig .box{fill:var(--box);stroke:var(--ln);stroke-width:1.5}
.lo4-fig .warn{fill:var(--box);stroke:var(--ac);stroke-width:2}
.lo4-fig .fin{fill:var(--box);stroke:var(--ac);stroke-width:2}
.lo4-fig .tx{fill:var(--tx);font:600 12px ui-sans-serif,system-ui,sans-serif;text-anchor:middle}
.lo4-fig .mut{fill:var(--mut);font:500 11px ui-sans-serif,system-ui,sans-serif;text-anchor:middle}
.lo4-fig .ac{fill:var(--ac);font:700 12px ui-sans-serif,system-ui,sans-serif;text-anchor:middle}
.lo4-fig line{stroke:var(--ln);stroke-width:1.5}
</style>
<defs>
<marker id="lo4-arrow-fr" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M0,0 L10,5 L0,10 z" fill="var(--ln)"/>
</marker>
</defs>
<!-- ligne 1 : branché -->
<text x="20" y="22" class="tx" text-anchor="start">Branché : if x &gt; 0 { sum += x }</text>
<rect x="20" y="35" width="140" height="50" rx="6" class="box"/>
<text x="90" y="65" class="tx">Fetch / Decode</text>
<line x1="160" y1="60" x2="178" y2="60" marker-end="url(#lo4-arrow-fr)"/>
<rect x="180" y="35" width="140" height="50" rx="6" class="box"/>
<text x="250" y="65" class="tx">Prédit pris</text>
<line x1="320" y1="60" x2="338" y2="60" marker-end="url(#lo4-arrow-fr)"/>
<rect x="340" y="35" width="140" height="50" rx="6" class="warn"/>
<text x="410" y="65" class="ac">Misprediction !</text>
<line x1="480" y1="60" x2="498" y2="60" marker-end="url(#lo4-arrow-fr)"/>
<rect x="500" y="35" width="280" height="50" rx="6" class="warn"/>
<text x="640" y="65" class="ac">Flush + refetch : ~15 cycles perdus</text>
<!-- ligne 2 : branchless -->
<text x="20" y="122" class="tx" text-anchor="start">Branchless : sum += (x &gt; 0) as i32 * x</text>
<rect x="20" y="135" width="140" height="50" rx="6" class="box"/>
<text x="90" y="165" class="tx">Fetch / Decode</text>
<line x1="160" y1="160" x2="178" y2="160" marker-end="url(#lo4-arrow-fr)"/>
<rect x="180" y="135" width="280" height="50" rx="6" class="box"/>
<text x="320" y="160" class="tx">Exécute masque + multiplication</text>
<text x="320" y="176" class="mut">aucun saut conditionnel</text>
<line x1="460" y1="160" x2="478" y2="160" marker-end="url(#lo4-arrow-fr)"/>
<rect x="480" y="135" width="300" height="50" rx="6" class="fin"/>
<text x="630" y="165" class="ac">Retire : débit stable</text>
<text x="400" y="225" class="mut">Même résultat, aucun flush — prévisible quelle que soit la distribution</text>
</svg>
</div>

## Techniques pour Réduire les Branch Mispredictions

### 1. Élimination de branches avec Arithmétique

Remplace les déclarations `if` par des opérations sans branches pour éviter les sauts conditionnels.

**Avant** (avec branches) :
```rust
let mut sum = 0;
for x in data {
    if x > 0 { sum += x; } // Branch : pris ou pas ?
}
```

**Après** (sans branches) :
```rust
let mut sum = 0;
for x in data {
    sum += (x > 0) as i32 * x; // Pas de branch : true=1, false=0
}
```

La comparaison génère un masque (`1` pour `true`, `0` pour `false`), et la multiplication évite un saut. Le système de types de Rust assure que c'est sûr et explicite.

### 2. Tri de données pour des Patterns Prévisibles

Si les branches dépendent des données d'entrée, trie-les pour grouper des résultats similaires, rendant la prédiction de branche plus facile.

**Avant** :
```rust
for x in data {
    if x < threshold { process_a(x); } else { process_b(x); }
}
```

**Après** :
```rust
data.sort_by(|a, b| a.partial_cmp(b).unwrap());
for x in data {
    if x < threshold { process_a(x); } else { process_b(x); }
}
```

Trier avec `sort_by` crée de longues séquences de branches "prises" ou "non prises", améliorant la précision du predictor.

<div class="svg-container" style="margin:2rem 0;">
<svg class="lo4b-fig" viewBox="0 0 800 275" width="100%" style="height:auto;max-width:780px;display:block;margin:0 auto;" role="img" aria-label="Bande de seize résultats de branche montrant que des données non triées alternent prise et non prise avec de nombreuses mauvaises prédictions, alors que des données triées forment deux longues séquences avec une seule mauvaise prédiction à la frontière">
<style>
.lo4b-fig{--bg:#f8fafc;--box:#ffffff;--tx:#1e293b;--mut:#64748b;--ln:#cbd5e1;--ac:#FF6B00}
:root.dark .lo4b-fig,[data-theme="dark"] .lo4b-fig{--bg:#0f172a;--box:#1e293b;--tx:#f8fafc;--mut:#94a3b8;--ln:#475569}
.lo4b-fig .cell{fill:var(--box);stroke:var(--ln);stroke-width:1.5}
.lo4b-fig .cellt{fill:var(--bg);stroke:var(--mut);stroke-width:1.5}
.lo4b-fig .box{fill:var(--box);stroke:var(--ln);stroke-width:1.5}
.lo4b-fig .miss{fill:var(--ac)}
.lo4b-fig .ti{fill:var(--tx);font:700 13px ui-sans-serif,system-ui,sans-serif}
.lo4b-fig .tx{fill:var(--tx);font:600 12px ui-sans-serif,system-ui,sans-serif;text-anchor:middle}
.lo4b-fig .mut{fill:var(--mut);font:500 11px ui-sans-serif,system-ui,sans-serif;text-anchor:middle}
.lo4b-fig .mutl{fill:var(--mut);font:500 11px ui-sans-serif,system-ui,sans-serif}
.lo4b-fig .ac{fill:var(--ac);font:700 12px ui-sans-serif,system-ui,sans-serif;text-anchor:middle}
</style>
<!-- titre ligne 1 -->
<text x="20" y="22" class="ti">Données non triées — x &lt; threshold bascule sans cesse</text>
<!-- ligne 1 : 16 résultats de branche -->
<rect x="40" y="34" width="30" height="32" rx="4" class="cellt"/>
<text x="55" y="55" class="tx">P</text>
<rect x="74" y="34" width="30" height="32" rx="4" class="cell"/>
<text x="89" y="55" class="tx">N</text>
<circle cx="89" cy="80" r="4" class="miss"/>
<rect x="108" y="34" width="30" height="32" rx="4" class="cellt"/>
<text x="123" y="55" class="tx">P</text>
<circle cx="123" cy="80" r="4" class="miss"/>
<rect x="142" y="34" width="30" height="32" rx="4" class="cellt"/>
<text x="157" y="55" class="tx">P</text>
<rect x="176" y="34" width="30" height="32" rx="4" class="cell"/>
<text x="191" y="55" class="tx">N</text>
<circle cx="191" cy="80" r="4" class="miss"/>
<rect x="210" y="34" width="30" height="32" rx="4" class="cellt"/>
<text x="225" y="55" class="tx">P</text>
<circle cx="225" cy="80" r="4" class="miss"/>
<rect x="244" y="34" width="30" height="32" rx="4" class="cell"/>
<text x="259" y="55" class="tx">N</text>
<circle cx="259" cy="80" r="4" class="miss"/>
<rect x="278" y="34" width="30" height="32" rx="4" class="cell"/>
<text x="293" y="55" class="tx">N</text>
<rect x="312" y="34" width="30" height="32" rx="4" class="cellt"/>
<text x="327" y="55" class="tx">P</text>
<circle cx="327" cy="80" r="4" class="miss"/>
<rect x="346" y="34" width="30" height="32" rx="4" class="cellt"/>
<text x="361" y="55" class="tx">P</text>
<rect x="380" y="34" width="30" height="32" rx="4" class="cell"/>
<text x="395" y="55" class="tx">N</text>
<circle cx="395" cy="80" r="4" class="miss"/>
<rect x="414" y="34" width="30" height="32" rx="4" class="cellt"/>
<text x="429" y="55" class="tx">P</text>
<circle cx="429" cy="80" r="4" class="miss"/>
<rect x="448" y="34" width="30" height="32" rx="4" class="cell"/>
<text x="463" y="55" class="tx">N</text>
<circle cx="463" cy="80" r="4" class="miss"/>
<rect x="482" y="34" width="30" height="32" rx="4" class="cell"/>
<text x="497" y="55" class="tx">N</text>
<rect x="516" y="34" width="30" height="32" rx="4" class="cellt"/>
<text x="531" y="55" class="tx">P</text>
<circle cx="531" cy="80" r="4" class="miss"/>
<rect x="550" y="34" width="30" height="32" rx="4" class="cellt"/>
<text x="565" y="55" class="tx">P</text>
<text x="690" y="47" class="ac">10 sur 16 mal prédites</text>
<text x="690" y="66" class="mut">~150 cycles jetés</text>
<text x="690" y="84" class="mut">le predictor n'apprend rien</text>
<!-- titre ligne 2 -->
<text x="20" y="118" class="ti">Après sort_by — deux longues séquences</text>
<!-- ligne 2 : 16 résultats de branche -->
<rect x="40" y="130" width="30" height="32" rx="4" class="cell"/>
<text x="55" y="151" class="tx">N</text>
<rect x="74" y="130" width="30" height="32" rx="4" class="cell"/>
<text x="89" y="151" class="tx">N</text>
<rect x="108" y="130" width="30" height="32" rx="4" class="cell"/>
<text x="123" y="151" class="tx">N</text>
<rect x="142" y="130" width="30" height="32" rx="4" class="cell"/>
<text x="157" y="151" class="tx">N</text>
<rect x="176" y="130" width="30" height="32" rx="4" class="cell"/>
<text x="191" y="151" class="tx">N</text>
<rect x="210" y="130" width="30" height="32" rx="4" class="cell"/>
<text x="225" y="151" class="tx">N</text>
<rect x="244" y="130" width="30" height="32" rx="4" class="cell"/>
<text x="259" y="151" class="tx">N</text>
<rect x="278" y="130" width="30" height="32" rx="4" class="cell"/>
<text x="293" y="151" class="tx">N</text>
<rect x="312" y="130" width="30" height="32" rx="4" class="cellt"/>
<text x="327" y="151" class="tx">P</text>
<circle cx="327" cy="176" r="4" class="miss"/>
<rect x="346" y="130" width="30" height="32" rx="4" class="cellt"/>
<text x="361" y="151" class="tx">P</text>
<rect x="380" y="130" width="30" height="32" rx="4" class="cellt"/>
<text x="395" y="151" class="tx">P</text>
<rect x="414" y="130" width="30" height="32" rx="4" class="cellt"/>
<text x="429" y="151" class="tx">P</text>
<rect x="448" y="130" width="30" height="32" rx="4" class="cellt"/>
<text x="463" y="151" class="tx">P</text>
<rect x="482" y="130" width="30" height="32" rx="4" class="cellt"/>
<text x="497" y="151" class="tx">P</text>
<rect x="516" y="130" width="30" height="32" rx="4" class="cellt"/>
<text x="531" y="151" class="tx">P</text>
<rect x="550" y="130" width="30" height="32" rx="4" class="cellt"/>
<text x="565" y="151" class="tx">P</text>
<text x="690" y="143" class="ac">1 sur 16 mal prédite</text>
<text x="690" y="162" class="mut">seulement à la frontière</text>
<text x="690" y="180" class="mut">le predictor se stabilise</text>
<!-- légende -->
<rect x="120" y="200" width="560" height="60" rx="6" class="box"/>
<text x="400" y="224" class="tx">P = branche prise · N = non prise · point orange = vidage du pipeline</text>
<text x="400" y="244" class="mut">Le tri se paie une fois si le corps de boucle tourne souvent par élément</text>
</svg>
</div>

### 3. Mouvements Conditionnels avec Pattern Matching

Utilise les enums et `match` de Rust pour structurer le code pour une optimisation potentielle sans branches.

**Avant** :
```rust
let result = if flag { compute_a() } else { compute_b() };
```

**Après** :
```rust
enum Op { A, B }
let op = if flag { Op::A } else { Op::B };
let result = match op {
    Op::A => compute_a(),
    Op::B => compute_b(),
};
```

Pour des `compute_a` et `compute_b` simples, le compilateur peut optimiser cela en mouvement conditionnel (`cmov` sur x86), évitant les sauts.

### 4. Loop Unrolling

Déroule les petites boucles pour réduire la fréquence des branches de fin de boucle.

**Avant** :
```rust
for i in 0..4 {
    if data[i] > 0 { out[i] = data[i]; }
}
```

**Après** :
```rust
out[0] = (data[0] > 0) as i32 * data[0];
out[1] = (data[1] > 0) as i32 * data[1];
out[2] = (data[2] > 0) as i32 * data[2];
out[3] = (data[3] > 0) as i32 * data[3];
```

Moins de branches de fin de boucle améliorent le flux du pipeline.

## Techniques avancées d'Optimisation

### 5. Utilisation de Lookup Tables

Remplace la logique conditionnelle complexe par des tables de lookup pré-calculées.

```rust
// Avant : logique conditionnelle complexe
fn classify_score(score: u8) -> &'static str {
    if score >= 90 {
        "excellent"
    } else if score >= 80 {
        "good" 
    } else if score >= 70 {
        "average"
    } else if score >= 60 {
        "poor"
    } else {
        "fail"
    }
}

// Après : lookup table sans branches
const SCORE_TABLE: [&str; 101] = {
    let mut table = ["fail"; 101];
    let mut i = 0;
    while i <= 100 {
        table[i] = if i >= 90 {
            "excellent"
        } else if i >= 80 {
            "good"
        } else if i >= 70 {
            "average"
        } else if i >= 60 {
            "poor"
        } else {
            "fail"
        };
        i += 1;
    }
    table
};

fn classify_score_optimized(score: u8) -> &'static str {
    SCORE_TABLE[score.min(100) as usize] // Pas de branches !
}
```

### 6. Masking et Bit Manipulation

Utilise des opérations bit-wise pour éviter les branches dans les cas booléens.

```rust
// Technique de masking pour sélection conditionnelle
fn conditional_max_branchless(a: i32, b: i32) -> i32 {
    let mask = (a >= b) as i32; // 1 si a >= b, 0 sinon
    let not_mask = 1 - mask;    // Inverse du masque
    
    // Sélection sans branche
    a * mask + b * not_mask
}

// Optimisation avec bit operations
fn abs_branchless(x: i32) -> i32 {
    let mask = x >> 31; // Tous les bits à 1 si négatif, 0 si positif
    (x + mask) ^ mask   // Complément à deux sans branche
}

// Min/max sans branches
fn min_branchless(a: i32, b: i32) -> i32 {
    b ^ ((a ^ b) & -((a < b) as i32))
}

fn max_branchless(a: i32, b: i32) -> i32 {
    a ^ ((a ^ b) & -((a < b) as i32))
}
```

### 7. Vectorisation avec SIMD pour Éliminer les branches

```rust
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;

// Traitement vectorisé sans branches
#[cfg(target_arch = "x86_64")]
unsafe fn process_positive_simd(data: &[f32], output: &mut [f32]) {
    assert_eq!(data.len(), output.len());
    
    for (chunk_in, chunk_out) in data.chunks_exact(8)
        .zip(output.chunks_exact_mut(8)) {
        
        let values = _mm256_loadu_ps(chunk_in.as_ptr());
        let zeros = _mm256_setzero_ps();
        
        // Masque : -1 pour positif, 0 pour négatif/zéro
        let mask = _mm256_cmp_ps(values, zeros, _CMP_GT_OQ);
        
        // Applique le masque : garde seulement les valeurs positives
        let result = _mm256_and_ps(values, mask);
        
        _mm256_storeu_ps(chunk_out.as_mut_ptr(), result);
    }
    
    // Traite les éléments restants
    let remainder_start = data.len() & !7; // Arrondi vers le bas au multiple de 8
    for i in remainder_start..data.len() {
        output[i] = if data[i] > 0.0 { data[i] } else { 0.0 };
    }
}
```

### 8. Réorganisation de Code pour Localité de branches

```rust
// Technique du "branch grouping"
fn process_mixed_data_optimized(data: &[(i32, bool)]) -> Vec<i32> {
    let mut positive_results = Vec::new();
    let mut negative_results = Vec::new();
    
    // Première passe : séparer selon le flag (pattern prévisible)
    for &(value, flag) in data {
        if flag {
            positive_results.push(value);
        } else {
            negative_results.push(value);
        }
    }
    
    // Deuxième passe : traitement sans branches internes
    let mut final_results = Vec::new();
    
    // Traite tous les positifs ensemble
    for value in positive_results {
        final_results.push(value * 2); // Traitement A
    }
    
    // Traite tous les négatifs ensemble  
    for value in negative_results {
        final_results.push(value + 1); // Traitement B
    }
    
    final_results
}
```

## Exploiter les Fonctionnalités de Rust

Le modèle d'ownership de Rust et les [abstractions zéro-coût](https://mayorana.ch/fr/blog/zero-cost-abstractions-rust) (ex : fusion d'itérateurs) réduisent les branches implicites. Les itérateurs comme `filter` peuvent être inlinés et optimisés, et le système de types encourage des patterns propres et optimisables sans code unsafe.

### Techniques spécifiques à Rust
```rust
// Utilisation d'iterators pour éliminer les branches explicites
fn process_data_functional(data: &[i32]) -> Vec<i32> {
    data.iter()
        .map(|&x| x.max(0)) // Branchless max avec 0
        .filter(|&x| x != 0) // Le compilateur peut optimiser
        .map(|x| x * 2)
        .collect()
}

// Pattern matching exhaustif pour aider l'optimiseur
#[derive(Debug, Clone, Copy)]
enum ProcessingMode {
    Fast,
    Accurate,
    Debug,
}

fn process_with_mode(data: &[f32], mode: ProcessingMode) -> Vec<f32> {
    match mode {
        ProcessingMode::Fast => {
            // Version optimisée sans vérifications
            data.iter().map(|&x| x * 2.0).collect()
        }
        ProcessingMode::Accurate => {
            // Version avec vérifications
            data.iter().map(|&x| (x * 2.0).min(f32::MAX)).collect()
        }
        ProcessingMode::Debug => {
            // Version avec logging
            data.iter().map(|&x| {
                let result = x * 2.0;
                println!("Processing {} -> {}", x, result);
                result
            }).collect()
        }
    }
}
```

## Outils de Profiling et vérification

Pour mesurer et confirmer les réductions de branch mispredictions, j'utiliserais :

### Linux `perf`
- **Commande** : `perf stat -e branches,branch-misses ./target/release/myapp`
- **Métriques** : Surveiller `branch-misses` en pourcentage de `branches`. Une chute de 10% à 2% indique le succès.
- **Exemple de Sortie (Avant)** :
  ```
  10,000,000 branches
  1,000,000 branch-misses (10.00%)
  ```
- **Après Optimisation** :
  ```
  8,000,000 branches
  160,000 branch-misses (2.00%)
  ```

### Profiling avancé avec perf

```bash
# Profiling détaillé des branches
perf record -e branches,branch-misses,branch-loads,branch-load-misses ./app
perf report --stdio --sort=symbol

# Analyse par fonction avec pourcentages
perf annotate function_name

# Profiling des stalls de pipeline
perf stat -e cycles,instructions,stalled-cycles-frontend,stalled-cycles-backend ./app

# Événements spécifiques au CPU (Intel)
perf stat -e cpu/event=0xc4,umask=0x20/,cpu/event=0xc5,umask=0x20/ ./app
```

### Valgrind avec Cachegrind

```bash
# Analyse des mispredictions avec Valgrind
valgrind --tool=cachegrind --branch-sim=yes ./target/release/app

# Analyse détaillée par fonction
cg_annotate cachegrind.out.* source_file.rs
```

### Benchmarking Intégré

```rust
use criterion::{black_box, Criterion, BenchmarkId};

fn branch_prediction_bench(c: &mut Criterion) {
    let sizes = [1000, 10000, 100000];
    let mut group = c.benchmark_group("branch_prediction");
    
    for size in sizes {
        // Données random (mauvais pour branch prediction)
        let random_data: Vec<i32> = (0..size).map(|_| fastrand::i32(..)).collect();
        
        // Données triées (bon pour branch prediction)
        let mut sorted_data = random_data.clone();
        sorted_data.sort();
        
        group.bench_with_input(
            BenchmarkId::new("branched_random", size),
            &random_data,
            |b, data| {
                b.iter(|| {
                    let mut sum = 0;
                    for &x in data {
                        if x > 0 {
                            sum += x;
                        }
                    }
                    black_box(sum)
                })
            }
        );
        
        group.bench_with_input(
            BenchmarkId::new("branchless_random", size),
            &random_data,
            |b, data| {
                b.iter(|| {
                    let mut sum = 0;
                    for &x in data {
                        sum += (x > 0) as i32 * x;
                    }
                    black_box(sum)
                })
            }
        );
        
        group.bench_with_input(
            BenchmarkId::new("branched_sorted", size),
            &sorted_data,
            |b, data| {
                b.iter(|| {
                    let mut sum = 0;
                    for &x in data {
                        if x > 0 {
                            sum += x;
                        }
                    }
                    black_box(sum)
                })
            }
        );
    }
    
    group.finish();
}
```

### Tests de performance Automatisés

```rust
#[cfg(test)]
mod performance_tests {
    use super::*;
    use std::time::Instant;
    
    #[test]
    fn test_branch_vs_branchless() {
        let data: Vec<i32> = (0..1_000_000)
            .map(|_| fastrand::i32(-100..100))
            .collect();
        
        // Test avec branches
        let start = Instant::now();
        let mut sum1 = 0;
        for &x in &data {
            if x > 0 {
                sum1 += x;
            }
        }
        let branched_time = start.elapsed();
        
        // Test sans branches
        let start = Instant::now();
        let mut sum2 = 0;
        for &x in &data {
            sum2 += (x > 0) as i32 * x;
        }
        let branchless_time = start.elapsed();
        
        // Les résultats doivent être identiques
        assert_eq!(sum1, sum2);
        
        println!("Branched: {:?}", branched_time);
        println!("Branchless: {:?}", branchless_time);
        println!("Speedup: {:.2}x", 
            branched_time.as_nanos() as f64 / branchless_time.as_nanos() as f64);
    }
}
```

## Optimisations spécifiques par domaine
### Traitement d'Images

```rust
// Seuillage d'image sans branches
fn threshold_image_branchless(image: &[u8], threshold: u8, output: &mut [u8]) {
    for (pixel, out_pixel) in image.iter().zip(output.iter_mut()) {
        // Utilise la comparaison comme masque
        *out_pixel = ((*pixel >= threshold) as u8) * 255;
    }
}

// Version SIMD pour seuillage
#[cfg(target_arch = "x86_64")]
unsafe fn threshold_image_simd(image: &[u8], threshold: u8, output: &mut [u8]) {
    use std::arch::x86_64::*;
    
    let threshold_vec = _mm256_set1_epi8(threshold as i8);
    
    for (chunk_in, chunk_out) in image.chunks_exact(32)
        .zip(output.chunks_exact_mut(32)) {
        
        let pixels = _mm256_loadu_si256(chunk_in.as_ptr() as *const __m256i);
        
        // Comparaison vectorisée
        let mask = _mm256_cmpgt_epi8(pixels, threshold_vec);
        
        _mm256_storeu_si256(chunk_out.as_mut_ptr() as *mut __m256i, mask);
    }
}
```

### Algorithmes de tri

```rust
// Tri par comptage pour éviter les comparaisons
fn counting_sort_no_branches(data: &[u8]) -> Vec<u8> {
    let mut counts = [0usize; 256];
    
    // Compte sans branches
    for &byte in data {
        counts[byte as usize] += 1;
    }
    
    // Reconstruit sans comparaisons
    let mut result = Vec::with_capacity(data.len());
    for (value, &count) in counts.iter().enumerate() {
        result.extend(std::iter::repeat(value as u8).take(count));
    }
    
    result
}
```

## Validation et Mesures

### Métriques de performance clés

- **Branch Miss Rate** : <5% excellent, <10% acceptable
- **Instructions Per Cycle (IPC)** : >2.0 indique un pipeline efficace
- **Frontend/Backend Stalls** : Devrait diminuer avec moins de mispredictions

### Analyse de régression

```rust
// Macro pour détecter les régressions de performance
macro_rules! assert_performance_improvement {
    ($optimized:expr, $baseline:expr, $min_improvement:expr) => {
        let optimized_time = time_function($optimized);
        let baseline_time = time_function($baseline);
        
        let improvement = (baseline_time.as_nanos() as f64 / optimized_time.as_nanos() as f64);
        
        assert!(
            improvement >= $min_improvement,
            "Performance regression detected: {:.2}x vs expected {:.2}x",
            improvement, $min_improvement
        );
    };
}
```

## Ce qu'il faut retenir sur les branches
Pour réduire les branch mispredictions dans une boucle chaude Rust, j'utiliserais l'élimination de branches arithmétiques, le tri de données, et les mouvements conditionnels avec pattern matching, comme montré. Les fonctionnalités de Rust (ownership, iterators) supportent naturellement ces optimisations. Je confirmerais les améliorations avec `perf stat` surveillant `branch-misses`, visant une réduction de 10% à <2%, et Valgrind pour l'analyse détaillée. Ces techniques peuvent améliorer les performances de 20-50% dans les boucles intensives en branches.
