39 votes

Comment améliorer le partage de mémoire entre les processus licorne avec Ruby 2.0 sur Linux

Ruby 2.0 introduit une copie sur écriture amical garbage collector. Mon processus ne semblent pas garder la mémoire partagée pour plus de quelques minutes -, il semble se déplacer de shared_dirty à private_dirty assez rapidement.

Certains autres ont eu du succès se présente au travail:

Ce programme peut être utilisé pour contrôler la mémoire des statistiques sur Linux: https://gist.github.com/kenn/5105061

Ma licorne de configuration: https://gist.github.com/inspire22/f82c77c0a465f1945305

Pour une raison quelconque, mon licorne applications, également avec preload_app=true, ont beaucoup moins de mémoire partagée. Ruby 2.0-p195, rails 3.2, linux 2.6.18 (centos)

[root@thorn script]# ruby memstats.rb 4946
Process:             4946
Command Line:        unicorn_rails worker[4] -c /u/apps/newap/current/lib/unicorn.rb -E production -D
Memory Summary:
  private_clean                   0 kB
  private_dirty              56,324 kB
  pss                        60,256 kB
  rss                        83,628 kB
  shared_clean                4,204 kB
  shared_dirty               23,100 kB
  size                      108,156 kB
  swap                           68 kB 

Si j'ai arrêté le processus maître entièrement (et pas seulement un signal HUP) puis de le redémarrer et vérifier immédiatement un travailleur, avant toute demande de file d'attente, je reçois une meilleure histoire:

[root@thorn script]# ruby memstats.rb 5743
Process:             5743
Command Line:        unicorn_rails worker[4] -c /u/apps/newap/current/lib/unicorn.rb -E production -D
Memory Summary:
  private_clean                   0 kB
  private_dirty              21,572 kB
  pss                        27,735 kB
  rss                        66,296 kB
  shared_clean                2,484 kB
  shared_dirty               42,240 kB
  size                       91,768 kB
  swap                            0 kB

Mais dans les 5 secondes qui suivent la mise en place, ils sont de retour à ~20 mo de shared_clean+shared_dirty.

Je soupçonne que la permutation pourrait être la cause du problème, mais après l'abaissement de swappiness et faire en sorte que ni les parents, ni les enfants les processus qui sont échangés (en utilisant swapstats.rb), le problème persiste.

Je ne comprends pas exactement ce que shared_dirty de la mémoire est, et comment il est transformé en mémoire privée. J'aimerais également des suggestions pour améliorer la longévité et le montant de mon mémoire partagée. Merci!

7voto

Homer6 Points 5850

Selon cette réponse, qui vous ont peut être déjà vu, il y a une ligne qui se lit comme suit:

Note qu'un "partage de pouvoir" de la page est compté comme un particulier de la cartographie jusqu'à ce qu'il est en fait partagée. c'est à dire si il n'y a qu'un seul processus actuellement à l'aide de libtruc, que la bibliothèque de la section de texte apparaîtra dans le processus de privé de mappings. Il en sera tenu compte dans les projections partagées (et retiré du privé) seulement si/quand un autre processus commence en utilisant cette bibliothèque.

Ce que je voudrais faire pour tester si vous êtes obtenir les prestations décrites dans cet article, est de mettre un 10 MO fichier xml comme une chaîne littérale directement dans votre code source. Ensuite, si vous le feu jusqu'à 20 salariés, vous serez en mesure de voir si vous êtes à l'aide de 200 MO de mémoire, ou seulement 10MO, comme cela est prévu avec la nouvelle collecte des ordures fonctionnalité.

Mise à JOUR:

J'étais à la recherche par le biais de la licorne source et trouvé une référence à ce merveilleux article.

Pour résumer, il affirme que pour adapter vos applications afin de tirer avantage de Ruby Enterprise Edition du copy-on-write amical garbage collector, vous devez définir GC.copy_on_write_friendly à vrai avant de la fourche.

if GC.respond_to?(:copy_on_write_friendly=)
    GC.copy_on_write_friendly = true
end

Basé sur votre condition licorne fichier de configuration, il semble être à côté de la cession.

Aussi, j'ai apprécié la lecture de ces articles connexes:

Selon la fourchette de l'homme page:

Sous Linux, fork() est implémentée à l'aide de copie sur écriture des pages, de sorte que le seule peine qu'il encourt est le temps et la mémoire nécessaires à dupliquer le parent de la page des tables, et pour créer une tâche unique structure pour l'enfant.

Depuis la version 2.3.3, plutôt que d'invoquer le noyau du système fork() d'appel, la glibc (fork () wrapper qui est fourni dans le cadre de la NPTL le filetage de la mise en œuvre appelle clone(2) avec des drapeaux qui fournissent l' même effet que le système traditionnel d'appel. (Un appel à fork() est équivalent à un appel de clone(2) préciser les drapeaux, juste SIGCHLD.) La glibc wrapper invoque n'importe quelle fourchette de gestionnaires qui ont été établies à l'aide de pthread_atfork ne(3).

Et selon le clone de l'homme page:

Contrairement à la fourche(2), ces appels permettent à l'enfant d'échanger des pièces de son contexte d'exécution avec le processus appelant, comme la mémoire l'espace, la table des descripteurs de fichiers, et la table de signal les gestionnaires.

Donc, je suis en train de lire ce dire: linux la fourche du copy-on-write, ce qui est la caractéristique de la licorne s'appuie sur la mise en œuvre de la mémoire de partage, n'a pas été mise en œuvre jusqu'à la libc 2.2.3 (s'il vous plaît, quelqu'un me corrige si je me trompe dans cette interprétation).

Pour vérifier la version de la libc vous êtes en cours d'exécution, vous pouvez taper:

ldd --version

Ou, trouver de la glibc et l'exécuter directement. Sur mon système, il a trouvé le fichier à l'emplacement suivant:

locate libc.so
/lib/x86_64-linux-gnu/libc.so.6

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X