Si je fais une boucle qui réserve des tableaux entier de 1kb, Int [1024], et je veux que cela alloue des tableaux 10000, puis-je le rendre plus rapide en exécutant les allocations de mémoire à partir de plusieurs threads? P>
Je veux qu'ils soient dans le tas. P>
Supposons que j'ai un processeur multicœur pour le travail. P>
J'ai déjà essayé cela, mais cela a diminué la performance. Je me demandais juste, ai-je simplement fait un mauvais code ou y a-t-il quelque chose que je ne connaissais pas sur l'allocation de mémoire? P>
La réponse dépend-elle du système d'exploitation? S'il vous plaît dites-moi comment cela fonctionne sur différentes plates-formes si oui. P>
EDIT: P>
La boucle d'allocation entier de tableau entier n'était qu'un exemple simplifié. Ne vous inquiétez pas de me dire comment je peux améliorer cela. P>
8 Réponses :
Aussi loin que je connaisse que tous les systèmes d'exploitation ont une serrure Mutex implicite à l'intérieur de l'appel dynamique d'attribution du système (Malloc ...). Si vous pensez un moment à ce sujet, si vous ne verrouillez pas cette action, vous pouvez rencontrer de terribles problèmes. P>
Vous pouvez utiliser les blocs de bâtiment de filetage de l'API multithreading http://threadingbuildingblocks.org/ qui a un allocator évolutif amical multithreading. P>
Mais je pense qu'une meilleure idée devrait être d'allouer toute la mémoire une fois (devrait fonctionner assez vite) et de le scinder seul. Je pense que l'allocator TBB fait quelque chose de similaire. P>
faire quelque chose comme p>
Nouveau Int [1024 * 10000] et que céder les parties de 1024Ints à votre tableau de pointeur ou que vous utilisez. P>
Comprends-tu? p>
Parce que le tas est partagé par processus, le tas sera verrouillé pour chaque allocation. Il ne peut donc pas être accédé en série par chaque thread. Cela pourrait expliquer la diminution des performances lorsque vous effectuez une alloc de plusieurs threads comme vous le faites. P>
Cela suppose une implémentation très naïve avec une seule serrure pour tout le tas.
La réponse dépend du système d'exploitation et du temps d'exécution utilisé, mais dans la plupart des cas, vous ne pouvez pas. P>
En règle générale, vous aurez deux versions du temps d'exécution: une version multi-threadée et une version à une seule version filetée. P>
La version à une seule-thread n'est pas thread-coffre-fort. Les allocations fabriquées par deux threads en même temps peuvent souffler votre application. P>
La version multi-threadée est la sécurité du thread. Cependant, en ce qui concerne les allocations sur la plupart des implémentations courantes, cela signifie simplement que les appels vers Il peut être possible qu'il y ait des systèmes d'exploitation pouvant gérer en toute sécurité les allocations parallèles dans le même processus, à l'aide d'un verrouillage minimal, ce qui vous permettrait de réduire le temps passé à affecter. Malheureusement, je ne sais aucun. P> Malloc CODE> sont emballés dans un mutex. Un seul thread peut jamais être dans la fonction
MALLOC CODE> à une heure donnée, alors tenter d'accélérer les allocations avec plusieurs threads entraînera simplement un convoi de verrouillage. P>
Si les tableaux appartiennent ensemble et ne seront libérés que dans son ensemble, vous pouvez simplement attribuer un tableau de 10000 * 1024 INTS, puis rendre vos matrices individuelles le pointez. Rappelez-vous simplement que vous ne pouvez pas Supprimer code> les petits tableaux, seulement l'ensemble.
int *all_arrays = new int[1024 * 10000];
int *small_array123 = all_arrays + 1024 * 123;
Ensuite, je ne comprends pas ce que vous vraiment i> veux faire. Expliquez le problème non aussi simplifié et peut-être que nous pouvons vous aider à ce sujet.
J'ai essayé de faire une copie plus rapide d'un arbre d'objet, j'ai donc essayé de scinder l'opération de la copie sur différents threads. Cependant, je l'ai déjà eu pour être assez rapide. Alors maintenant, j'essaie juste de mieux comprendre l'allocation de la mémoire.
La réponse dépend de la routine d'allocations de mémoire, qui constituent une combinaison d'une couche de bibliothèque C ++ Si vous souhaitez compléter plus rapidement, envisagez d'attribuer une matrice de 10000 * 1024 INTS, puis en utilisant différentes parties de celle-ci (par exemple, NOUVEAU code>, probablement enveloppée autour de libc
MALLOC () code>, qui à son tour occasionnellement Appelle une fonction OS telle que
sémbe () code>. Les caractéristiques de mise en œuvre et de performance de toutes celles-ci sont non spécifiées et peuvent varier d'une version du compilateur à la version, avec des drapeaux de compilateur, des versions d'OS différentes, des différents OSES, etc. Si le profilage montre qu'il est plus lent, alors c'est la ligne inférieure. Vous pouvez essayer de faire varier le nombre de threads, mais ce qui se passe probablement est que les threads essaient d'obtenir le même verrou afin de modifier le tas ... Les frais généraux impliqués avec disant "OK, thread x obtient la prochaine étape" et "thread x ici, j'ai fini" consistez simplement à perdre du temps. Un autre environnement C ++ pourrait finir par utiliser des opérations atomiques pour éviter de verrouiller, ce qui pourrait ou non être plus rapide ... Aucune règle générale. P>
[0] .. [1023] code>,
[1024] .. [2047] code> ...). P>
comme pour glibc, il a arène em> s (voir Ici ), qui a une serrure par arène. p>
Vous pouvez également envisager TCMALLOC par Google (Stands pour la mise en cache de fil Malloc ), qui montre 30% de performance de renforcement pour l'application filetée. Nous l'utilisons dans notre projet. En mode de débogage, il peut même découvrir une utilisation incorrecte de la mémoire (par exemple une nouvelle inadéquation gratuite) P>
Cela dépend de nombreuses choses, mais principalement: p>
malloc code> vous utilisez li>
ul>
Le système d'exploitation est responsable de l'affectation de la "mémoire virtuelle" que votre processus a accès à et construit un tableau de traduction qui correspond à la mémoire virtuelle de retour aux adresses de mémoire réelles. P>
Maintenant, la mise en œuvre par défaut de MALLOC code> est généralement conservatrice et aura simplement une serrure géante autour de tout cela. Cela signifie que les demandes sont traitées en série et la seule chose qui alloue à partir de plusieurs threads au lieu d'une fois, ralentit tout le monde. P>
Il existe plus de schémas d'allocation intelligente, généralement basés sur des pools, et ils peuvent être trouvés dans d'autres MALLOC CODE> MALLOC: TCMALLOC CODE> (de Google) et Jemalloc < / Code> (utilisé par Facebook) sont deux de ces implémentations conçues pour des applications multi-threadées. p>
Il n'y a pas de balle d'argent cependant et, à un moment donné, le système d'exploitation doit effectuer la translation réelle virtuelle <=> qui nécessite une forme de verrouillage. P>
Votre meilleur pari est d'allouer par Arenas: P>
- allouer de gros morceaux (arènes) à la fois li>
- diviser les matrices de la taille appropriée li>
ul>
Il n'est pas nécessaire de paralléser l'allocation d'arène et vous ferez mieux de demander les plus grandes arènes que vous pouvez (gardez à l'esprit que les demandes d'allocation pour une quantité trop importante peuvent échouer), alors vous pouvez paralliser la scission. . P>
tcmalloc code> et jemalloc code> peut aider un peu, mais ils ne sont pas conçus pour gros em> allocations (qui est inhabituelle) et je ne sais pas S'il est possible de configurer la taille des arènes, ils demandent. P>
Je pense que vous devez peut-être adapter votre attente de multi-threading. p>
L'avantage principal de la multi-threading est que vous pouvez faire des tâches asynchroniquement, c'est-à-dire dans Une autre approche pourrait être que l'allocation du fil fonctionne à l'avance et parallèle code>. Dans votre cas, lorsque votre fil principal a besoin de plus de mémoire, il n'a pas de problème si elle est allouée par un autre fil - vous devez toujours arrêter et attendre que l'allocation soit accomplie, il y a donc
aucun parallélisme code> ici. De plus, il y a une surcharge d'une signalisation de fil lorsqu'elle est terminée et l'autre attente d'achèvement, qui peut simplement dégrader la performance. De plus, si vous démarrez un fil à chaque fois que vous avez besoin d'allocation, ceci est un
énorme code> au-dessus. Sinon, vous avez besoin d'un mécanisme pour transmettre la demande d'allocation et la réponse entre les threads, une sorte de file d'attente de tâches qui est à nouveau un surcharge sans gain. P>
pré-alloua code> la mémoire que vous
sera code> besoin. Cela peut vous donner un réel gain, mais si vous faites une pré-allocation, vous pourriez aussi bien le faire dans le fil principal qui sera plus simple. Par exemple. Allouer 10 m dans un coup d'un coup (ou 10 fois 1 m, ou autant de mémoire contiguë que vous pouvez avoir) et disposez d'un tableau de 10 000 pointeurs en pointant à 1024 compensations, représentant vos tableaux. Si vous n'avez pas besoin de les interdire de manière indépendante les uns des autres, cela semble être beaucoup plus simple et pourrait être encore plus efficace que d'utiliser un multi-filetage. P>
Prenez en compte les frais généraux du lancement des threads ...
L'allocation de mémoire n'est pas un processus de simple mémoire. Vous devez suivre et l'organiser (surtout quand il est libre). Donc, les structures de données qui détiennent cette inofrmation sont très sensibles aux erreurs. Ainsi, lorsqu'ils sont modifiés, vous devez vous assurer que plusieurs threads agissant sur ces données ne font pas gâcher. Cela signifie fondamentalement que l'accès à la structure doit être synchronisé cela aura une surcharge (la plupart du temps).
@Martin: Vous décrivez une conception dans laquelle il existe un seul pool de données de mémoire à partir de laquelle toutes les threads allouent la mémoire. Un bon allocator multi-fileté ne le fait pas. Ils ont des pools par fil qui ne nécessitent aucune synchronisation.
@Msalters: J'étais pessimiste à propos de l'allocator Standard C ++ Lib Memory.
@Martin: Il n'y a pas d'allocator standard. Il n'y a qu'une norme pour l'interface, et cela ne reconnaît même pas de threads. Chaque fournisseur a sa propre mise en œuvre et ceux-ci diffèrent de manière significative.