{"id":1337,"date":"2015-07-12T12:02:18","date_gmt":"2015-07-12T16:02:18","guid":{"rendered":"http:\/\/bioinfo.iric.ca\/?p=1337"},"modified":"2017-04-12T12:00:21","modified_gmt":"2017-04-12T16:00:21","slug":"faites-travailler-vos-cpus","status":"publish","type":"post","link":"https:\/\/bioinfo.iric.ca\/fr\/faites-travailler-vos-cpus\/","title":{"rendered":"Faites travailler vos CPUs !"},"content":{"rendered":"<div class=\"fusion-fullwidth fullwidth-box fusion-builder-row-1 nonhundred-percent-fullwidth non-hundred-percent-height-scrolling\" style=\"--awb-border-radius-top-left:0px;--awb-border-radius-top-right:0px;--awb-border-radius-bottom-right:0px;--awb-border-radius-bottom-left:0px;--awb-flex-wrap:wrap;\" ><div class=\"fusion-builder-row fusion-row\"><div class=\"fusion-layout-column fusion_builder_column fusion-builder-column-0 fusion_builder_column_1_1 1_1 fusion-one-full fusion-column-first fusion-column-last\" style=\"--awb-bg-size:cover;--awb-margin-bottom:0px;\"><div class=\"fusion-column-wrapper fusion-flex-column-wrapper-legacy\"><div class=\"fusion-text fusion-text-1\"><p>Si vous \u00eates comme moi, vous avez s\u00fbrement r\u00e9alis\u00e9 que, par d\u00e9faut, les scripts python que l&rsquo;on \u00e9crit n&rsquo;utilisent qu&rsquo;une portion de la puissance computationnelle \u00e0 notre disposition.. Vous vous \u00eates s\u00fbrement dit:<\/p>\n<blockquote>\n<p>J&rsquo;ai pourtant pay\u00e9 une somme rondelette pour un CPU \u00e0 <b>4 coeurs<\/b> ! Mais qu&rsquo;est-ce qui se passe ?<\/p>\n<\/blockquote>\n<p>Bien que la plupart des CPUs modernes comportent plusieurs coeurs, le code que l&rsquo;on \u00e9crit doit aussi \u00eatre formatt\u00e9 ad\u00e9quatement afin d&rsquo;en tirer pleinement avantage.<\/p>\n<p>Alors explorons ensemble le module <a href=\"https:\/\/docs.python.org\/2\/library\/multiprocessing.html#module-multiprocessing\" target=\"_blank\">multiprocessing<\/a> de Python afin de d\u00e9couvrir comment faire rouler notre code sur plus d&rsquo;un coeur \u00e0 la fois.<\/p>\n<p>C&rsquo;est en fait assez simple si notre code est d\u00e9j\u00e0 bien segment\u00e9 en fonctions (comme il devrait l&rsquo;\u00eatre&#8230;).<br \/>\nL&rsquo;exemple que je mettrai en place utilisera un ensemble de processus travailleurs qui consumeront une liste de t\u00e2ches contenue dans une queue.<\/p>\n<p>Commen\u00e7ons par d\u00e9finir une fonction qui calcule un hypoth\u00e9tique potentiel th\u00e9rapeutique entre 2 individus :<\/p>\n<pre><code class=\"python\">def simulate_treatment(param1, param2):\r\n    donor = Individual(param1, param2)\r\n    patient = Individual(param1, param2)\r\n\r\n    heals = 0\r\n    nb_therapeutic_targets = 0\r\n    if donor.can_heal(patient):\r\n        heals = 1\r\n        nb_therapeutic_targets = len(donor.targets(patient))\r\n\r\n    return heals, nb_therapeutic_targets\r\n<\/code>\r\n<\/pre>\n<p>Il nous manque \u00e9videmment la d\u00e9finition de la classe \u00ab\u00a0Individual\u00a0\u00bb mais elle n&rsquo;est pas vraiment n\u00e9cessaire pour comprendre ce qui se passe ici.<br \/>\nSi nous voulions rouler 1 million d&rsquo;it\u00e9rations de cette simulation, nous pourrrions faire la chose suivante:<\/p>\n<pre><code class=\"python\">def main():\r\n    for i in range(1000000):\r\n        heals, nb_therapeutic_minors = simulate_treatment(param1, param2)\r\n        # do some stuff with the results\r\n<\/code>\r\n<\/pre>\n<p>Et ceci ex\u00e9cuterait 1 million d&rsquo;iterations de la fonction \u00ab\u00a0simulate_treatment\u00a0\u00bb de mani\u00e8re <b>s\u00e9quentielle<\/b> sur un seul coeur. Bouououo&#8230;<br \/>\nModifions donc notre main() un peu afin de r\u00e9partir notre million d&rsquo;it\u00e9rations sur 4 coeurs:<\/p>\n<pre><code class=\"python\">import multiprocessing\r\n\r\ndef main():\r\n    p = Pool(processes=4)\r\n    tasks = [(param1, param2) for i in range(1000000)]\r\n    simulations = p.map_async(simulate_treatment, tasks)\r\n    p.close()\r\n    p.join()\r\n    for sim in simulations.get():\r\n        heals = sim[0]\r\n        nb_therapeutic_minors = sim[1]\r\n        # do some stuff with the results\r\n<\/code>\r\n<\/pre>\n<p>Plusieurs choses se produisent alors:<\/p>\n<pre><code class=\"python\">p = Pool(processes=4)<\/code><\/pre>\n<p>initialise un \u00ab\u00a0pool\u00a0\u00bb de 4 processus travailleurs qui grignoteront la liste de t\u00e2ches de simulation instanti\u00e9e par:<\/p>\n<pre><code class=\"python\">tasks = [(param1, param2) for i in range(1000000)]<\/code><\/pre>\n<p>la liste est ensuite soumise au Pool, les processus rouleront de fa\u00e7on asynchrone et les r\u00e9sultats seront cumul\u00e9s dans un nouvel iterable portant le nom appropri\u00e9 de \u00ab\u00a0simulations\u00a0\u00bb.<\/p>\n<pre><code class=\"python\">simulations = p.map_async(simulate_treatment, tasks)<\/code><\/pre>\n<p>Nous fermons ensuite le Pool afin d&rsquo;\u00e9viter la soumission de nouvelles t\u00e2ches et patientons jusqu&rsquo;\u00e0 la compl\u00e9tion des t\u00e2ches de la queue \u00e0 l&rsquo;aide des commandes:<\/p>\n<pre><code class=\"python\">p.close()\r\np.join()\r\n<\/code>\r\n<\/pre>\n<p>Plut\u00f4t simple, non ?<\/p>\n<blockquote>\n<p>Mais JP, tu as soumis un tuple de params \u00e0 une fonction pr\u00e9sentant une signature qui requiert 2 param\u00e8tres distincts.. \u00c7a va pas ?<\/p>\n<\/blockquote>\n<p>Hehe, c&rsquo;est bien, je vois que vous \u00eates attentifs ! En fait, ce code est incorrect et plantera si vous tentez de le rouler.<br \/>\nAlors laissez-moi sugg\u00e9rer un dernier addendum.<br \/>\nSi vous \u00eates comme moi, vous appr\u00e9ciez les belles fonctions bien d\u00e9finies avec des signatures claires. Malheureusement, map_async n&rsquo;accepte qu&rsquo;un iterable en entr\u00e9e.. Ma suggestion est donc la suivante: utilisons une fonction \u00ab\u00a0enveloppe\u00a0\u00bb afin de r\u00e9cup\u00e9rer le tuple de param\u00e8tres soumis \u00e0 la queue et l&rsquo;expandre afin de la passer \u00e0 notre vraie fonction:<\/p>\n<pre><code class=\"python\">def simulate_treatment_wrapper(args):\r\n    return simulate_treatment(*args)\r\n<\/code>\r\n<\/pre>\n<h2>Une fois que tout est en place<\/h2>\n<\/div><div class=\"fusion-builder-row fusion-builder-row-inner fusion-row\"><div class=\"fusion-layout-column fusion_builder_column_inner fusion-builder-nested-column-0 fusion_builder_column_inner_1_2 1_2 fusion-one-half fusion-column-first\" style=\"--awb-bg-size:cover;width:50%;width:calc(50% - ( ( 4% ) * 0.5 ) );margin-right: 4%;\"><div class=\"fusion-column-wrapper fusion-flex-column-wrapper-legacy\"><div class=\"fusion-text fusion-text-2\"><p>Ce bout de code:<\/p>\n<pre><code class=\"python\">\r\n\r\n\r\n\r\n\r\ndef simulate_treatment(param1, param2):\r\n    donor = Individual(param1, param2)\r\n    patient = Individual(param1, param2)\r\n\r\n    heals = 0\r\n    nb_therapeutic_targets = 0\r\n    if donor.can_heal(patient):\r\n        heals = 1\r\n        nb_therapeutic_targets = len(donor.targets(patient))\r\n\r\n    return heals, nb_therapeutic_targets\r\n\r\ndef main():\r\n    for i in range(1000000):\r\n        heals, nb_therapeutic_minors = simulate_treatment(param1, param2)\r\n        # do some stuff with the results\r\n<\/code>\r\n<\/pre>\n<\/div><div class=\"fusion-clearfix\"><\/div><\/div><\/div><div class=\"fusion-layout-column fusion_builder_column_inner fusion-builder-nested-column-1 fusion_builder_column_inner_1_2 1_2 fusion-one-half fusion-column-last\" style=\"--awb-bg-size:cover;width:50%;width:calc(50% - ( ( 4% ) * 0.5 ) );\"><div class=\"fusion-column-wrapper fusion-flex-column-wrapper-legacy\"><div class=\"fusion-text fusion-text-3\"><p>Devient:<\/p>\n<pre><code class=\"python\">import multiprocessing\r\n\r\ndef simulate_treatment_wrapper(args):\r\n    return simulate_treatment(*args)\r\n\r\ndef simulate_treatment(param1, param2):\r\n    donor = Individual(param1, param2)\r\n    patient = Individual(param1, param2)\r\n\r\n    heals = 0\r\n    nb_therapeutic_targets = 0\r\n    if donor.can_heal(patient):\r\n        heals = 1\r\n        nb_therapeutic_targets = len(donor.targets(patient))\r\n\r\n    return heals, nb_therapeutic_targets\r\n\r\ndef main():\r\n    p = Pool(processes=4)\r\n    tasks = [(param1, param2) for i in range(1000000)]\r\n    simulations = p.map_async(simulate_treatment_wrapper, tasks)\r\n    p.close()\r\n    p.join()\r\n    for sim in simulations.get():\r\n        heals = sim[0]\r\n        nb_therapeutic_minors = sim[1]\r\n        # do some stuff with the results\r\n<\/code>\r\n<\/pre>\n<\/div><div class=\"fusion-clearfix\"><\/div><\/div><\/div><\/div><div class=\"fusion-text fusion-text-4\"><p>Comme vous pouvez le voir, nous n&rsquo;avons pas fait de changements trop importants et pourtant, notre code roule maintenant en parall\u00e8le sur 4 coeurs physiques !<br \/>\nBienvenue&#8230; \ud83d\ude42<\/p>\n<\/div><div class=\"fusion-clearfix\"><\/div><\/div><\/div><\/div><\/div>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[32,26],"tags":[66],"class_list":["post-1337","post","type-post","status-publish","format-standard","hentry","category-performance-fr-2","category-langage-python","tag-multiprocessing-fr"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/bioinfo.iric.ca\/fr\/wp-json\/wp\/v2\/posts\/1337","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bioinfo.iric.ca\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bioinfo.iric.ca\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bioinfo.iric.ca\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bioinfo.iric.ca\/fr\/wp-json\/wp\/v2\/comments?post=1337"}],"version-history":[{"count":10,"href":"https:\/\/bioinfo.iric.ca\/fr\/wp-json\/wp\/v2\/posts\/1337\/revisions"}],"predecessor-version":[{"id":3177,"href":"https:\/\/bioinfo.iric.ca\/fr\/wp-json\/wp\/v2\/posts\/1337\/revisions\/3177"}],"wp:attachment":[{"href":"https:\/\/bioinfo.iric.ca\/fr\/wp-json\/wp\/v2\/media?parent=1337"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bioinfo.iric.ca\/fr\/wp-json\/wp\/v2\/categories?post=1337"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bioinfo.iric.ca\/fr\/wp-json\/wp\/v2\/tags?post=1337"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}