zo iNiTiATiON SPEECHES
S01E04 2024-08-01

S01E04 — 01-08-2024.

Le multi-filage.

jour 4.

Pas facile de penser multi-filage dans le contexte d’un compilateur. Dans l’idéal, on aimerait pouvoir paralléliser chaque phase de la compilation. Ainsi chacune de ces phases serait exécutée dans un fil séparé. Sauf que le multi-filage ne nous garantit pas que l’ordre d’exécution sera le même que celui précisé au départ. Par exemple si j’envoie chacune des valeurs du tableau suivant [1, 2, 3] dans un fil à part :

fn main() {
  let mut handles = Vec::new();

  for x in [1, 2, 3] {
    handles.push(std::thread::spawn(move || {
      println!("{x}");
    }));
  }

  for handle in handles {
    handle.join().unwrap();
  }
}

Le résultat variera entre 132, 213, 231 etc. Et ce n’est pas ce que je veux. Les phase de la compilation doivent s’exécuter de façon conditionnelle. C’est-à-dire que l’ordre des étapes doit être conservée. Il faut trouver un moyen de bénéficier du multi-filage sans perdre l’ordre d’éxécution.

Pour cela, je dois introduire la notion de channel. Avec ça je devrais être pepouze :

fn main() {
  let mut handles = Vec::new();
  let (tx, rx) = std::sync::mpsc::channel();

  for x in [1, 2, 3] {
    let tx = tx.clone();

    handles.push(std::thread::spawn(move || {
      tx.send(x).unwrap();
      print!("{x}");
    }));
  }

  drop(tx);

  for x in rx {
    print!("{x}");
  }

  print!("—");

  for handle in handles {
    handle.join().unwrap();
  }
}

Tu parles ! Voici les résultats que j’obtiens : 323121, 223113, 112233, etc. En soi, tout ceci est tout à fait normal. Cela montre que la parallélisation fonctionne très bien. Ok-ay mais impossible d’implémenter cette approche dans mon compilo. Finalité, je suis toujours bloqué ma gueule.

Après quelques recherches sur les Internets, il existe des modèles de programmation de chaînes. Je te mets les noms ici, ça pourrait t’aider dans tes recherches : producer-consumer, pipeline, worker pool, pub-sub, actor-model et pleins d’autres. Grâce à ces modèles, je vais dormir moins idiot ce soir. Et surtout, je peux piocher dans un des modèles, selon mon besoin.

Je suis parti sur un truc simple juste pour tester, car pour le moment c’est trop prématuré de penser au multi-filage. Le modèle producer-consumer fait bien son taf.

fn main() {
  let (tx, rx) = std::sync::mpsc::channel();

  let producer = std::thread::spawn(move || {
    for x in [1, 2, 3] {
      tx.send(x).unwrap();
      print!("{x}");
    }
  });

  let consumer = std::thread::spawn(move || {
    for x in rx {
      print!("{x}");
    }
  });

  producer.join().unwrap();
  consumer.join().unwrap();
}

Izi! J’obtiens alors 123123 à chaque fois que j’exécute mon programme. N’empêche, ça m’a pris une journée pour comprendre mon problème. Normal, je n’ai pas de Ph. D la famille. Je comprends à mon rythme. En tout cas, je suis content, ça fonctionne très bien. Tu trouveras mon implémentation ici. Il est possible que j’eusse écrit un besoin complément inutile, mais je verrai ça plus tard. Je dois avoir un POC viable d’ici la fin de semaine. Plus que trois jours et j’ai à peine commencer le tokenizer.

Ça va aller. Le turfu rien que le turfu.

@invisageable