2
votes

Comment sortir le plus rapidement possible un tampon fixe?

Exemple de code:

$ mkfifo out
$ dd if=/dev/zero bs=16384 >out &
$ dd if=/dev/zero bs=16384 >out &
$ dd if=/dev/zero bs=16384 >out &
$ dd if=/dev/zero bs=16384 >out &
pv <out -ptebaSs 800G >/dev/null

Ce programme ouvre 4 threads et affiche sur stdout le contenu de "buffer" qui fait 128 octets ou 16 entiers longs sur un processeur 64 bits.

Si je lance ensuite:

./writetest | pv -ptebaSs 800G> / dev / null

J'obtiens une vitesse d'environ 7,5 Go / s.

Par ailleurs, c'est la même vitesse que j'obtiens si je le fais: p >

#include <stdio.h>
#include <unistd.h>
#include <sched.h>
#include <pthread.h>

int
main (int argc, char **argv)
{

  unsigned char buffer[128];
  char buf[0x4000];
  setvbuf (stdout, buf, _IOFBF, 0x4000);
  fork ();
  fork ();

  pthread_t this_thread = pthread_self ();

  struct sched_param params;

  params.sched_priority = sched_get_priority_max (SCHED_RR);

  pthread_setschedparam (this_thread, SCHED_RR, &params);


  while (1)
    {
      fwrite (&buffer, 128, 1, stdout);
    }
}

Y a-t-il un moyen de rendre cela plus rapide? Remarque. le tampon dans le programme réel n'est pas rempli de zéros.

ma curiosité est de comprendre combien de données un seul programme (multithreaad ou multiprocessus) peut produire

Il semble que 4 personnes n'ont pas compris cette question simple. J'ai même mis en gras la raison de la question.


10 commentaires

BTW fork ne crée pas de thread. Pas mon DV / CV cependant.


@Jabberwocky Je pourrais utiliser pthread .. mais il n'y a pas d'amélioration de la vitesse .. le goulot d'étranglement semble être le tuyau.


ma curiosité est de comprendre la quantité de données qu'un seul programme (multithreaaded ou multiprocessus) peut produire.


Vous devriez le préciser dans la question.


Avez-vous déjà essayé "write (STDOUT_FILENO, ...)"? Et les fonctions comme sendfile () et vmsplice () conviennent-elles?


@SKi write est plus lent car avec un tampon si petit, il est préférable d'utiliser fwrite .. dans tous mes tests, toute modification du code ci-dessus le ralentit ... les choses s'améliorent lorsque j'augmente la priorité du processus et le planificateur à Temps réel et -20 ... mais le tuyau est toujours le goulot d'étranglement ... sendfile afaik utilise fwrite en interne ... à propos de vmsplice je ne sais pas , il attend un tableau de structures iovec ... hmm


J'ai mis à jour la source en augmentant la priorité de planification des threads. Vous devez également l'exécuter avec sudo nice -n -20 pour avoir une vitesse maximale.


@Zibri sendfile () est un appel système comme write (), donc il n'utilisera pas la fonction c-lib 'de haut niveau' comme fwrite ().


ok .. mais est-ce que l'un de vous a compilé mon code puis sa "version" et a trouvé que sa version est plus rapide sur sa machine?


S'il vous plaît, @oguzismail supprime la cale .. il est clair pourquoi j'ai demandé cela .. en fait les gens essaient de répondre.


3 Réponses :


1
votes

Vous devez d'abord déterminer votre facteur de limitation de débit. Il peut s'agir de la vitesse du processeur / de la mémoire, de la latence du processeur / de l'appel système, de l'implémentation du canal, de l'implémentation stdio. Il y en a probablement plus, mais c'est un bon début:

  1. cpu / mémoire - testez à quelle vitesse vous pouvez mémoriser un tas de zéros.

  2. cpu / syscall - teste, en écrivant 1 octet dans / dev / null, combien de temps il faut pour faire une simple écriture sur votre système

  3. implémentation du tube - vous avez en quelque sorte ceci, mais vous pouvez essayer de faire varier la capacité du tube (fcntl (2) F_GETPIPE_SZ. F_SETPIPE_SZ, si vous êtes sous Linux).

  4. Implémentation stdio - remplacez fwite / setbuf par write. Je suggérerais d'aligner votre taille d'écriture sur la capacité du tube / nombre-processus pourrait donner un bon résultat, mais vous devriez probablement étudier plus largement.

Essayez tout ce qui précède avec plusieurs processus, même si vous devrez peut-être mettre à l'échelle les memcpy pour obtenir des résultats significatifs.

Avec ces chiffres, vous devriez être en mesure de calculer votre débit maximal. Veuillez faire un rapport, je suis sûr que plus de quelques personnes sont intéressées.


3 commentaires

hmm .. J'ai essayé tout ça ... étrangement write + buffer le rend plus lent que le code ci-dessus. À propos du fnctl je ne l'ai pas essayé et je pense que cela pourrait améliorer les choses, mais je ne l'ai jamais utilisé ... si vous voulez faire des tests, vous pouvez poster votre modification de code sur le mien si c'est plus rapide ...


Je viens d'essayer avec F_SETPIPE_SZ après avoir ouvert une fifo ... la vitesse est plus lente de 30% ... mais vous êtes libre d'essayer ... peut-être que j'ai fait quelque chose de mal.


mise à jour: doubler la taille du tuyau améliore la vitesse .. mais l'augmenter davantage ne semble pas améliorer la vitesse davantage.



-1
votes

Ce que vous programmez est:

  1. Appelle fwrite . Cela copie simplement les données de buffer vers buf .
  2. Une fois que buf est rempli, il appelle write .

Pour l'accélérer, évitez cette copie à l'étape 1 et fwrite et utilisez write syscall directement. Par exemple:

char buf[0x4000];
for(;;)
    write(STDOUT_FILENO, buf, sizeof buf); // Implement error handling.

Vous pouvez également agrandir buf pour minimiser le nombre d'appels système (les atténuations de Spectre ont rendu les appels système plus coûteux). P >


3 commentaires

@Zibri Cela peut être lent uniquement si vous écrivez un petit tableau. Écrivez un plus grand tableau, par exemple 4MB. Vous devez jouer avec la taille du tableau pour maximiser votre débit.


J'ai essayé ... et comme j'obtiens la même vitesse qu'avec le programme d'origine, je préfère le garder tel quel ... comme je l'ai dit, le goulot d'étranglement est dans le tuyau à 90% ...


@Zibri Vous avez raison de dire que c'est lié aux E / S, mais que la copie inutile n'aide pas.



2
votes

Eh bien, il semble que le programmateur Linux et les priorités d'E / S jouées ont joué un grand rôle dans le ralentissement.

De plus, des atténuations de la vunerability du spectre et d'autres processeurs ont joué.

Après une optimisation supplémentaire, pour obtenir vitesse plus rapide, j'ai dû régler ces choses:

1) program nice level (nice -n -20)
2) program ionice level (ionice -c 1 -n 7)
3) pipe size increased 8 times.
4) disable cpu mitigations by adding "pti=off spectre_v2=off l1tf=off" in kernel command line
5) tuning the linux scheduler

echo -n -1 >/proc/sys/kernel/sched_rt_runtime_us
echo -n -1 >/proc/sys/kernel/sched_rt_period_us
echo -n -1 >/proc/sys/kernel/sched_rr_timeslice_ms
echo -n 0 >/proc/sys/kernel/sched_tunable_scaling

Maintenant, le programme produit (sur le même PC) 8,00 Go / sec!

Si vous avez d'autres idées que vous êtes invité à contribuer.


0 commentaires