{"id":593114,"date":"2025-08-18T15:32:39","date_gmt":"2025-08-18T14:32:39","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=pycharm&#038;p=593114"},"modified":"2025-10-16T11:00:40","modified_gmt":"2025-10-16T10:00:40","slug":"python-mais-rapido-desbloqueando-o-global-interpreter-lock-do-python","status":"publish","type":"pycharm","link":"https:\/\/blog.jetbrains.com\/pt-br\/pycharm\/2025\/08\/python-mais-rapido-desbloqueando-o-global-interpreter-lock-do-python\/","title":{"rendered":"Python mais r\u00e1pido: Desbloqueando o Global Interpreter Lock do Python"},"content":{"rendered":"<h2 id=\"what-is-pythons-global-interpreter-lock-gil\" class=\"wp-block-heading\">O que \u00e9 o Global Interpreter Lock (GIL) do Python?<\/h2>\n<p>&#8220;Global Interpreter Lock&#8221; (ou &#8220;GIL&#8221;) \u00e9 um termo conhecido na comunidade Python. \u00c9 um recurso bem conhecido do Python. Mas o que exatamente \u00e9 um GIL?<\/p>\n<p>Se voc\u00ea tem experi\u00eancia com outras linguagens de programa\u00e7\u00e3o (como o Rust), talvez j\u00e1 saiba o que \u00e9 um mutex. \u00c9 uma abrevia\u00e7\u00e3o de &#8220;mutual exclusion&#8221; (exclus\u00e3o m\u00fatua). Um mutex garante que os dados s\u00f3 possam ser acessados por um thread de cada vez. Isso evita que eles sejam modificados por v\u00e1rios threads ao mesmo tempo. Voc\u00ea pode pensar nisso como um tipo de &#8220;bloqueio&#8221;. Ele impede que todos os threads acessem os dados, exceto o thread que possui a chave.<\/p>\n<p>O GIL \u00e9 tecnicamente um mutex. Ele permite que apenas um thread tenha acesso ao interpretador Python de cada vez. \u00c0s vezes, eu o imagino como um volante para o Python. N\u00e3o d\u00e1 para ter mais de uma pessoa no controle do volante! Por outro lado, um grupo de pessoas em uma viagem de carro geralmente troca de motorista. \u00c9 como transferir o acesso ao interpretador para um thread diferente.<\/p>\n<p>Devido ao GIL, o Python n\u00e3o permite processos verdadeiramente multithread. Esse recurso provocou debates na \u00faltima d\u00e9cada, e houve muitas tentativas de tornar o Python mais r\u00e1pido removendo o GIL e permitindo processos multithread. Recentemente, no Python 3.13, foi implementada uma op\u00e7\u00e3o para utilizar o Python sem o GIL, \u00e0s vezes conhecido como no-GIL ou Python com threads livres. Assim come\u00e7a uma nova era da programa\u00e7\u00e3o em Python.<\/p>\n<h2 id=\"why-was-the-gil-there-in-the-first-place\" class=\"wp-block-heading\">Por que o GIL estava l\u00e1, afinal?<\/h2>\n<p>Se o GIL \u00e9 t\u00e3o impopular, por que ele foi implementado para in\u00edcio de conversa? Na verdade, h\u00e1 benef\u00edcios em ter um GIL. Em outras linguagens de programa\u00e7\u00e3o com multithread verdadeiro, \u00e0s vezes os problemas s\u00e3o causados por mais de um thread que modifica os dados, com o resultado final dependendo de qual thread ou processo termina primeiro. Isso \u00e9 chamado de &#8220;condi\u00e7\u00e3o de corrida&#8221;. Linguagens como Rust costumam ser dif\u00edceis de aprender porque os programadores precisam usar mutexes para evitar condi\u00e7\u00f5es de corrida.<\/p>\n<p>Em Python, todos os objetos t\u00eam um contador de refer\u00eancias para monitorar quantos outros objetos requerem informa\u00e7\u00f5es deles. Se o contador de refer\u00eancias chegar a zero, como sabemos que n\u00e3o h\u00e1 condi\u00e7\u00e3o de corrida no Python devido ao GIL, podemos declarar com seguran\u00e7a que o objeto n\u00e3o \u00e9 mais necess\u00e1rio e pode ser coletado como lixo.<\/p>\n<p>Quando o Python foi lan\u00e7ado pela primeira vez em 1991, a maioria dos computadores pessoais tinha apenas um n\u00facleo, e n\u00e3o havia muitos programadores solicitando suporte para multithread. Ter um GIL resolve muitos problemas na implementa\u00e7\u00e3o de programas e tamb\u00e9m pode facilitar a manuten\u00e7\u00e3o do c\u00f3digo. Portanto, um GIL foi adicionado por Guido van Rossum (o criador do Python) em 1992.<\/p>\n<p>Avan\u00e7ando para 2025: os computadores pessoais t\u00eam processadores de v\u00e1rios n\u00facleos e, portanto, muito mais capacidade de computa\u00e7\u00e3o. Podemos aproveitar esse poder extra para alcan\u00e7ar verdadeira concorr\u00eancia sem nos livrar do GIL.<\/p>\n<p>Mais adiante neste post, detalharemos o processo de remo\u00e7\u00e3o. Mas, por enquanto, vamos dar uma olhada em como a verdadeira concorr\u00eancia funciona com o GIL implementado.<\/p>\n<h2 id=\"multiprocessing-in-python\" class=\"wp-block-heading\">Multiprocessamento em Python<\/h2>\n<p>Antes de nos aprofundarmos no processo de remo\u00e7\u00e3o do GIL, vamos dar uma olhada em como os desenvolvedores Python podem alcan\u00e7ar a verdadeira concorr\u00eancia usando a biblioteca de multiprocessamento. A biblioteca padr\u00e3o de multiprocessamento oferece concorr\u00eancia local e remota, contornando o Global Interpreter Lock efetivamente com o uso de subprocessos em vez de threads. Dessa forma, o m\u00f3dulo de multiprocessamento permite que o programador aproveite totalmente v\u00e1rios processadores em uma determinada m\u00e1quina.<\/p>\n<p>No entanto, para realizar o multiprocessamento, teremos que projetar nosso programa de maneira um pouco diferente. Vamos dar uma olhada no seguinte exemplo de como utilizar a biblioteca de multiprocessamento em Python.<br \/>Voc\u00ea se lembra da nossa hamburgueria ass\u00edncrona na <a href=\"https:\/\/blog.jetbrains.com\/pycharm\/2025\/06\/concurrency-in-async-await-and-threading\/\">parte 1 da s\u00e9rie de blogs<\/a>:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import asyncio\n\nimport time\n\nasync def make_burger(order_num):\n\n    print(f\"Preparing burger #{order_num}...\")\n\n    await asyncio.sleep(5) # time for making the burger\n\n    print(f\"Burger made #{order_num}\")\n\nasync def main():\n\n    order_queue = []\n\n    for i in range(3):\n\n        order_queue.append(make_burger(i))\n\n    await asyncio.gather(*(order_queue))\n\nif __name__ == \"__main__\":\n\n    s = time.perf_counter()\n\n    asyncio.run(main())\n\n    elapsed = time.perf_counter() - s\n\n    print(f\"Orders completed in {elapsed:0.2f} seconds.\")\n<\/pre>\n<p>Podemos usar a biblioteca de multiprocessamento para fazer o mesmo, por exemplo:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import multiprocessing\n\n\nimport time\n\n\ndef make_burger(order_num):\n\n\n   print(f\"Preparing burger #{order_num}...\")\n\n\n   time.sleep(5) # time for making the burger\n\n\n   print(f\"Burger made #{order_num}\")\n\n\n\n\nif __name__ == \"__main__\":\n\n\n   print(\"Number of available CPU:\", multiprocessing.cpu_count())\n\n\n\n\n   s = time.perf_counter()\n\n\n   all_processes = []\n\n\n   for i in range(3):\n       process = multiprocessing.Process(target=make_burger, args=(i,))\n       process.start()\n       all_processes.append(process)\n\n\n   for process in all_processes:\n       process.join()\n\n\n   elapsed = time.perf_counter() - s\n\n\n   print(f\"Orders completed in {elapsed:0.2f} seconds.\")\n<\/pre>\n<p>Como voc\u00ea deve se lembrar, muitos dos m\u00e9todos de multiprocessamento s\u00e3o muito semelhantes ao processo de threading. Para ver a diferen\u00e7a no multiprocessamento<em>,<\/em> vamos explorar um caso de uso mais complexo:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import multiprocessing\nimport time\nimport queue\n\n\n\n\ndef make_burger(order_num, item_made):\n   name = multiprocessing.current_process().name\n   print(f\"{name} is preparing burger #{order_num}...\")\n   time.sleep(5)  # time for making burger\n   item_made.put(f\"Burger #{order_num}\")\n   print(f\"Burger #{order_num} made by {name}\")\n\n\n\n\ndef make_fries(order_num, item_made):\n   name = multiprocessing.current_process().name\n   print(f\"{name} is preparing fries #{order_num}...\")\n   time.sleep(2)  # time for making fries\n   item_made.put(f\"Fries #{order_num}\")\n   print(f\"Fries #{order_num} made by {name}\")\n\n\n\n\ndef working(task_queue, item_made, order_num, lock):\n   break_count = 0\n   name = multiprocessing.current_process().name\n   while True:\n       try:\n           task = task_queue.get_nowait()\n       except queue.Empty:\n           print(f\"{name} has nothing to do...\")\n           if break_count &gt; 1:\n               break  # stop if idle for too long\n           else:\n               break_count += 1\n           time.sleep(1)\n       else:\n           lock.acquire()\n           try:\n               current_num = order_num.value\n               order_num.value = current_num + 1\n           finally:\n               lock.release()\n           task(current_num, item_made)\n           break_count = 0\n\n\n\n\nif __name__ == \"__main__\":\n\n\n   print(\"Welcome to Pyburger! Please place your order.\")\n\n\n   burger_num = input(\"Number of burgers:\")\n   fries_num = input(\"Number of fries:\")\n\n\n   s = time.perf_counter()\n\n\n   task_queue = multiprocessing.Queue()\n   item_made = multiprocessing.Queue()\n   order_num = multiprocessing.Value(\"i\", 0)\n   lock = multiprocessing.Lock()\n\n\n   for i in range(int(burger_num)):\n       task_queue.put(make_burger)\n   for i in range(int(fries_num)):\n       task_queue.put(make_fries)\n\n\n   staff1 = multiprocessing.Process(\n       target=working,\n       name=\"John\",\n       args=(\n           task_queue,\n           item_made,\n           order_num,\n           lock,\n       ),\n   )\n   staff2 = multiprocessing.Process(\n       target=working,\n       name=\"Jane\",\n       args=(\n           task_queue,\n           item_made,\n           order_num,\n           lock,\n       ),\n   )\n\n\n   staff1.start()\n   staff2.start()\n\n\n   staff1.join()\n   staff2.join()\n\n\n   print(\"All tasks finished. Closing now.\")\n   print(\"Items created are:\")\n\n\n   while not item_made.empty():\n       print(item_made.get())\n\n\n   elapsed = time.perf_counter() - s\n\n\n   print(f\"Orders completed in {elapsed:0.2f} seconds.\")\n<\/pre>\n<p>Aqui est\u00e1 o resultado obtido:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Welcome to Pyburger! Please place your order.\nNumber of burgers:3\nNumber of fries:2\nJane has nothing to do...\nJohn is preparing burger #0...\nJane is preparing burger #1...\nBurger #0 made by John\nJohn is preparing burger #2...\nBurger #1 made by Jane\nJane is preparing fries #3...\nFries #3 made by Jane\nJane is preparing fries #4...\nBurger #2 made by John\nJohn has nothing to do...\nFries #4 made by Jane\nJane has nothing to do...\nJohn has nothing to do...\nJane has nothing to do...\nJohn has nothing to do...\nJane has nothing to do...\nAll tasks finished. Closing now.\nItems created are:\nBurger #0\nBurger #1\nFries #3\nBurger #2\nFries #4\nOrders completed in 12.21 seconds.\n<\/pre>\n<p>Observe que h\u00e1 algumas limita\u00e7\u00f5es no multiprocessamento que fazem com que o c\u00f3digo acima seja projetado dessa maneira. Vamos examin\u00e1-las uma a uma.<\/p>\n<p>Primeiro, lembre-se de que anteriormente t\u00ednhamos as fun\u00e7\u00f5es make_burger e make_fries para gerar uma fun\u00e7\u00e3o com o order_num correto:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def make_burger(order_num):\n   def making_burger():\n       logger.info(f\"Preparing burger #{order_num}...\")\n       time.sleep(5)  # time for making burger\n       logger.info(f\"Burger made #{order_num}\")\n\n\n   return making_burger\n\n\n\n\ndef make_fries(order_num):\n   def making_fries():\n       logger.info(f\"Preparing fries #{order_num}...\")\n       time.sleep(2)  # time for making fries\n       logger.info(f\"Fries made #{order_num}\")\n\n\n   return making_fries\n<\/pre>\n<p>N\u00e3o podemos fazer o mesmo usando o multiprocessamento. Uma tentativa de fazer isso nos dar\u00e1 um erro do tipo:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">AttributeError: Can't get local object 'make_burger..making_burger'<\/pre>\n<p>O motivo \u00e9 que o multiprocessamento usa <a href=\"https:\/\/docs.python.org\/3\/library\/pickle.html\" target=\"_blank\" rel=\"noopener\">pickle<\/a>, que s\u00f3 pode serializar fun\u00e7\u00f5es de n\u00edvel de m\u00f3dulo superior em geral. Essa \u00e9 uma das limita\u00e7\u00f5es do multiprocessamento<em>.<\/em><\/p>\n<p>Em segundo lugar, observe que, no trecho de c\u00f3digo do exemplo acima, usando o multiprocessamento, n\u00e3o usamos nenhuma vari\u00e1vel global para dados compartilhados. Por exemplo, n\u00e3o podemos utilizar vari\u00e1veis globais para item_made e order_num. Para compartilhar dados entre diferentes processos, objetos de classe especial, como Queue e Value da biblioteca de multiprocessamento, s\u00e3o usados e passados para os processos como argumentos.<br \/>De modo geral, o compartilhamento de dados e estados entre processos diferentes n\u00e3o \u00e9 incentivado, pois pode causar muitos outros problemas. No nosso exemplo acima, precisamos usar um bloqueio para garantir que o valor de order_num s\u00f3 possa ser acessado e incrementado por um processo de cada vez. Sem o bloqueio, o n\u00famero do pedido do item pode ser bagun\u00e7ado, desta forma:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Items created are:\n\nBurger #0\nBurger #0\nFries #2\nBurger #1\nFries #3\n<\/pre>\n<p>Veja como usar um bloqueio para evitar problemas:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">           lock.acquire()\n           try:\n               current_num = order_num.value\n               order_num.value = current_num + 1\n           finally:\n               lock.release()\n           task(current_num, item_made)\n<\/pre>\n<p>Para saber mais sobre como usar a biblioteca padr\u00e3o de multiprocessamento, voc\u00ea pode consultar a documenta\u00e7\u00e3o <a href=\"https:\/\/docs.python.org\/3\/library\/multiprocessing.html\" target=\"_blank\" rel=\"noopener\">aqui<\/a>.<\/p>\n<h2 id=\"removing-the-gil\" class=\"wp-block-heading\">Removendo o GIL<\/h2>\n<p>A remo\u00e7\u00e3o do GIL tem sido um tema para discuss\u00e3o h\u00e1 quase uma d\u00e9cada. Em 2016, no Python Language Summit, Larry Hastings apresentou suas ideias sobre a realiza\u00e7\u00e3o de uma &#8220;GIL-ectomia&#8221; no interpretador CPython e o progresso que ele havia feito com essa ideia [1]. Foi uma tentativa pioneira de remover o GIL do Python. Em 2021, Sam Gross reacendeu a discuss\u00e3o sobre a remo\u00e7\u00e3o do GIL [2], e isso levou ao <a href=\"https:\/\/peps.python.org\/pep-0703\/\" target=\"_blank\" rel=\"noopener\">PEP 703 \u2013 Tornando o Global Interpreter Lock opcional no CPython<\/a> em 2023.<\/p>\n<p>Como podemos ver, a remo\u00e7\u00e3o do GIL n\u00e3o \u00e9, de forma alguma, uma decis\u00e3o precipitada e tem sido objeto de consider\u00e1vel debate na comunidade. Conforme demonstrado pelos exemplos acima de multiprocessamento (e o PEP 703, no link acima), quando a garantia fornecida pelo GIL \u00e9 removida, as coisas se complicam rapidamente.<\/p>\n<p>[1]: <a href=\"https:\/\/lwn.net\/Articles\/689548\/\" target=\"_blank\" rel=\"noopener\">https:\/\/lwn.net\/Articles\/689548\/<\/a><\/p>\n<p>[2]: https:\/\/lwn.net\/ml\/python-dev\/CAGr09bSrMNyVNLTvFq-h6t38kTxqTXfgxJYApmbEWnT71L74-g@mail.gmail.com\/<\/p>\n<h3 id=\"reference-counting\" class=\"wp-block-heading\">Contagem de refer\u00eancias<\/h3>\n<p>Quando o GIL est\u00e1 presente, a contagem de refer\u00eancias e a coleta de lixo s\u00e3o mais simples e diretas. Quando apenas um thread por vez tem acesso a objetos Python, podemos confiar na <a href=\"https:\/\/en.wikipedia.org\/wiki\/Reference_counting\" target=\"_blank\" rel=\"noopener\">contagem de refer\u00eancias<\/a> n\u00e3o at\u00f4mica e remover o objeto quando ela chega a zero.<\/p>\n<p>Com a remo\u00e7\u00e3o do GIL, as coisas ficam mais complicadas. N\u00e3o podemos mais usar a contagem de refer\u00eancias n\u00e3o at\u00f4mica, pois isso n\u00e3o garante a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Thread_safety\" target=\"_blank\" rel=\"noopener\">seguran\u00e7a dos threads<\/a>. As coisas podem dar errado quando v\u00e1rios threads est\u00e3o realizando v\u00e1rios incrementos e decrementos da refer\u00eancia no objeto Python ao mesmo tempo. O ideal seria usar a contagem de refer\u00eancias at\u00f4mica para garantir a seguran\u00e7a dos threads. No entanto, esse m\u00e9todo sofre uma alta sobrecarga, e a efici\u00eancia \u00e9 prejudicada quando h\u00e1 muitos threads.<\/p>\n<p>A solu\u00e7\u00e3o \u00e9 usar a contagem de refer\u00eancias tendenciosa (biased), que tamb\u00e9m garante a seguran\u00e7a dos threads. A ideia \u00e9 atribuir a cada objeto uma tend\u00eancia (bias) para um thread propriet\u00e1rio, ou seja, o thread que acessa esse objeto na maior parte do tempo. Os threads propriet\u00e1rios podem realizar a contagem de refer\u00eancias n\u00e3o at\u00f4mica nos objetos que possuem, enquanto outros threads devem realizar a contagem de refer\u00eancias at\u00f4mica nesses objetos. Esse m\u00e9todo \u00e9 prefer\u00edvel \u00e0 contagem de refer\u00eancias at\u00f4mica simples, porque a maioria dos objetos s\u00f3 \u00e9 acessada por um thread na maior parte do tempo. Podemos reduzir a sobrecarga de execu\u00e7\u00e3o, permitindo que o thread propriet\u00e1rio realize a contagem de refer\u00eancias n\u00e3o at\u00f4mica.<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-586289\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/07\/image-22.png\" alt=\"\" width=\"1280\" height=\"720\" \/><\/figure>\n<p>Al\u00e9m disso, alguns objetos Python usados comumente, como True, False<em>, <\/em>pequenos inteiros e algumas strings internas, s\u00e3o imortais. Aqui, &#8220;imortal&#8221; significa apenas que os objetos permanecer\u00e3o no programa durante todo o seu tempo de vida e, portanto, n\u00e3o requerem contagem de refer\u00eancias.<\/p>\n<h3 id=\"garbage-collection\" class=\"wp-block-heading\">Coleta de lixo<\/h3>\n<p>Tamb\u00e9m temos que modificar a forma como a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Garbage_collection_(computer_science)\" target=\"_blank\" rel=\"noopener\">coleta de lixo<\/a> \u00e9 feita. Em vez de diminuir a contagem de refer\u00eancias imediatamente quando a refer\u00eancia \u00e9 liberada e remover o objeto imediatamente quando essa contagem chega a zero, \u00e9 usada uma t\u00e9cnica chamada de &#8220;contagem de refer\u00eancias diferida&#8221;.\u00a0<\/p>\n<p>Quando a contagem de refer\u00eancias precisa ser diminu\u00edda, o objeto \u00e9 armazenado em uma tabela, que ser\u00e1 verificada duas vezes para saber se essa diminui\u00e7\u00e3o na contagem de refer\u00eancias \u00e9 precisa ou n\u00e3o. Isso evita a remo\u00e7\u00e3o prematura do objeto quando ele ainda est\u00e1 sendo referenciado, o que pode acontecer sem o GIL, j\u00e1 que a contagem de refer\u00eancias n\u00e3o \u00e9 t\u00e3o simples quanto com o GIL. Isso complica o processo de coleta de lixo, j\u00e1 que a coleta de lixo pode precisar percorrer a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Stack_(abstract_data_type)\" target=\"_blank\" rel=\"noopener\">pilha<\/a> de cada thread para a realizar contagem de refer\u00eancias de cada thread.<\/p>\n<p>Outro aspecto a ser considerado \u00e9 que a contagem de refer\u00eancias precisa ser est\u00e1vel durante a coleta de lixo. Se um objeto estiver prestes a ser descartado e, de repente, for referenciado, isso causar\u00e1 s\u00e9rios problemas. Por esse motivo, durante o ciclo de coleta de lixo, ele ter\u00e1 que &#8220;parar o mundo&#8221; para oferecer garantias de seguran\u00e7a de thread.<\/p>\n<h3 id=\"memory-allocation\" class=\"wp-block-heading\">Aloca\u00e7\u00e3o de mem\u00f3ria<\/h3>\n<p>Quando o GIL est\u00e1 presente para garantir a seguran\u00e7a do thread, o alocador de mem\u00f3ria interno do Python, pymalloc, \u00e9 utilizado. Mas, sem o GIL, precisaremos de um novo alocador de mem\u00f3ria. Sam Gross prop\u00f4s o <a href=\"https:\/\/github.com\/microsoft\/mimalloc\" target=\"_blank\" rel=\"noopener\">mimalloc<\/a> no PEP, que \u00e9 um alocador de uso geral criado por Daan Leijen e mantido pela Microsoft. \u00c9 uma boa escolha porque ele \u00e9 seguro para threads e tem boa performance em objetos pequenos.<\/p>\n<p>O mimalloc preenche seu heap com p\u00e1ginas e p\u00e1ginas com blocos<em>.<\/em> Cada p\u00e1gina cont\u00e9m blocos, e os blocos dentro de cada p\u00e1gina s\u00e3o todos do mesmo tamanho. Ao adicionar algumas restri\u00e7\u00f5es ao acesso \u00e0 lista e ao dict, o coletor de lixo n\u00e3o precisa manter uma <a href=\"https:\/\/en.wikipedia.org\/wiki\/Linked_list\" target=\"_blank\" rel=\"noopener\">lista vinculada<\/a> para descobrir todos os objetos e tamb\u00e9m permite o acesso de leitura \u00e0 lista e ao dict sem adquirir o bloqueio.<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-586300\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/07\/image-23.png\" alt=\"\" width=\"1280\" height=\"720\" \/><\/figure>\n<p>H\u00e1 mais detalhes sobre a remo\u00e7\u00e3o do GIL, mas \u00e9 imposs\u00edvel abordar todos eles aqui. Voc\u00ea pode consultar <a href=\"https:\/\/peps.python.org\/pep-0703\/\" target=\"_blank\" rel=\"noopener\">PEP 703 &#8211; Tornando o Global Interpreter Lock opcional no CPython<\/a> para obter uma an\u00e1lise completa.<\/p>\n<h2 id=\"difference-in-performance-with-and-without-the-gil\" class=\"wp-block-heading\">Diferen\u00e7a de performance com e sem o GIL<\/h2>\n<p>Como o Python 3.13 oferece uma op\u00e7\u00e3o de threads livres, podemos comparar a performance da vers\u00e3o padr\u00e3o do Python 3.13 com a vers\u00e3o com threads livres.<\/p>\n<h3 id=\"install-thread-free-python\" class=\"wp-block-heading\">Instalar o Python com threads livres<\/h3>\n<p>Usaremos o <a href=\"https:\/\/github.com\/pyenv\/pyenv\" target=\"_blank\" rel=\"noopener\">pyenv<\/a> para instalar as duas vers\u00f5es: a padr\u00e3o (por exemplo, 3.13.5) e a vers\u00e3o com threads livres (por exemplo, 3.13.5t).\u00a0<\/p>\n<p>Como alternativa, voc\u00ea tamb\u00e9m pode usar os instaladores no site <a href=\"http:\/\/python.org\" target=\"_blank\" rel=\"noopener\">Python.org<\/a>. Certifique-se de selecionar a op\u00e7\u00e3o <em>Customize<\/em> durante a instala\u00e7\u00e3o e marque a caixa adicional para instalar o Python com threads livres (<a href=\"https:\/\/til.simonwillison.net\/python\/trying-free-threaded-python\" target=\"_blank\" rel=\"noopener\">veja o exemplo nesta postagem do nosso blog<\/a>).<\/p>\n<p>Depois de instalar as duas vers\u00f5es, podemos adicion\u00e1-las como interpretadores em um projeto do PyCharm.<\/p>\n<p>Primeiro, clique no nome do seu interpretador Python no canto inferior direito.<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-586311\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/07\/image-24.png\" alt=\"\" width=\"960\" height=\"328\" \/><\/figure>\n<p>Selecione <em>Add New Interpreter<\/em> no menu e, em seguida, <em>Add Local Interpreter<\/em>.<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-586322\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/07\/image-25.png\" alt=\"\" width=\"1600\" height=\"575\" \/><\/figure>\n<p>Escolha <em>Select existing<\/em>, aguarde o carregamento do caminho do interpretador (o que pode demorar um pouco se voc\u00ea tiver muitos interpretadores, como eu tenho) e, em seguida, selecione o novo interpretador que acabou de instalar no menu suspenso <em>Python path<\/em>.<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-586333\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/07\/image-26.png\" alt=\"\" width=\"1538\" height=\"1100\" \/><\/figure>\n<p>Clique em <em>OK<\/em> para adicion\u00e1-lo. Repita as mesmas etapas para o outro interpretador. Agora, quando voc\u00ea clicar novamente no nome do interpretador no canto inferior direito, ver\u00e1 v\u00e1rios interpretadores Python 3.13, como na imagem acima.<\/p>\n<h3 id=\"testing-with-a-cpu-bounded-process\" class=\"wp-block-heading\">Testes com um processo limitado por CPU<\/h3>\n<p>Em seguida, precisamos de um script para testar as diferentes vers\u00f5es. Lembre-se de que explicamos na <a href=\"https:\/\/blog.jetbrains.com\/pycharm\/2025\/06\/concurrency-in-async-await-and-threading\/#cpu-bound-tasks\">parte 1 desta s\u00e9rie de postagens do nosso blog<\/a> que, para acelerar processos limitados por CPU, precisamos de um verdadeiro multithread. Para ver se a remo\u00e7\u00e3o do GIL permitir\u00e1 um multithread verdadeiro e tornar\u00e1 o Python mais r\u00e1pido, podemos testar com um processo limitado por CPU em v\u00e1rios threads. Aqui est\u00e1 o script que pedi para o <a href=\"https:\/\/www.jetbrains.com\/junie\/\" target=\"_blank\" rel=\"noopener\">Junie<\/a> gerar (com alguns ajustes finais feitos por mim):<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import time\nimport multiprocessing  # Kept for CPU count\nfrom concurrent.futures import ThreadPoolExecutor\nimport sys\n\n\n\n\ndef is_prime(n):\n   \"\"\"Check if a number is prime (CPU-intensive operation).\"\"\"\n   if n &lt;= 1:\n       return False\n   if n &lt;= 3:\n       return True\n   if n % 2 == 0 or n % 3 == 0:\n       return False\n   i = 5\n   while i * i &lt;= n:\n       if n % i == 0 or n % (i + 2) == 0:\n           return False\n       i += 6\n   return True\n\n\n\n\ndef count_primes(start, end):\n   \"\"\"Count prime numbers in a range.\"\"\"\n   count = 0\n   for num in range(start, end):\n       if is_prime(num):\n           count += 1\n   return count\n\n\n\n\ndef run_single_thread(range_size, num_chunks):\n   \"\"\"Run the prime counting task in a single thread.\"\"\"\n   chunk_size = range_size \/\/ num_chunks\n   total_count = 0\n\n\n   start_time = time.time()\n\n\n   for i in range(num_chunks):\n       start = i * chunk_size + 1\n       end = (i + 1) * chunk_size + 1 if i &lt; num_chunks - 1 else range_size + 1\n       total_count += count_primes(start, end)\n\n\n   end_time = time.time()\n\n\n   return total_count, end_time - start_time\n\n\n\n\ndef thread_task(start, end):\n   \"\"\"Task function for threads.\"\"\"\n   return count_primes(start, end)\n\n\n\n\ndef run_multi_thread(range_size, num_threads, num_chunks):\n   \"\"\"Run the prime counting task using multiple threads.\"\"\"\n   chunk_size = range_size \/\/ num_chunks\n   total_count = 0\n\n\n   start_time = time.time()\n\n\n   with ThreadPoolExecutor(max_workers=num_threads) as executor:\n       futures = []\n       for i in range(num_chunks):\n           start = i * chunk_size + 1\n           end = (i + 1) * chunk_size + 1 if i &lt; num_chunks - 1 else range_size + 1\n           futures.append(executor.submit(thread_task, start, end))\n\n\n       for future in futures:\n           total_count += future.result()\n\n\n   end_time = time.time()\n\n\n   return total_count, end_time - start_time\n\n\n\n\ndef main():\n   # Fixed parameters\n   range_size = 1000000  # Range of numbers to check for primes\n   num_chunks = 16       # Number of chunks to divide the work into\n   num_threads = 4       # Fixed number of threads for multi-threading test\n\n\n   print(f\"Python version: {sys.version}\")\n   print(f\"CPU count: {multiprocessing.cpu_count()}\")\n   print(f\"Range size: {range_size}\")\n   print(f\"Number of chunks: {num_chunks}\")\n   print(\"-\" * 60)\n\n\n   # Run single-threaded version as baseline\n   print(\"Running single-threaded version (baseline)...\")\n   count, single_time = run_single_thread(range_size, num_chunks)\n   print(f\"Found {count} primes in {single_time:.4f} seconds\")\n   print(\"-\" * 60)\n\n\n   # Run multi-threaded version with fixed number of threads\n   print(f\"Running multi-threaded version with {num_threads} threads...\")\n   count, thread_time = run_multi_thread(range_size, num_threads, num_chunks)\n   speedup = single_time \/ thread_time\n   print(f\"Found {count} primes in {thread_time:.4f} seconds (speedup: {speedup:.2f}x)\")\n   print(\"-\" * 60)\n\n\n   # Summary\n   print(\"SUMMARY:\")\n   print(f\"{'Threads':&lt;10} {'Time (s)':&lt;12} {'Speedup':&lt;10}\")\n   print(f\"{'1 (baseline)':&lt;10} {single_time:&lt;12.4f} {'1.00x':&lt;10}\")\n   print(f\"{num_threads:&lt;10} {thread_time:&lt;12.4f} {speedup:.2f}x\")\n\n\nif __name__ == \"__main__\":\n   main()\n<\/pre>\n<p>Para facilitar a execu\u00e7\u00e3o do script com diferentes interpretadores Python, podemos adicionar um script de execu\u00e7\u00e3o personalizado ao nosso projeto PyCharm.<\/p>\n<p>Na parte superior, selecione <em>Edit Configurations&#8230;<\/em> no menu suspenso ao lado do bot\u00e3o <em>Run<\/em> (<img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/09\/AD_4nXcRz0HogcsseQcC85-vSwRJhBWhiZCysBUZjcuu1uQYusTx-hj6h-l4FAs_HUE1gcJV5kjSjPuUu1opfKgu6sH5G9epkDlMMw0fBRPyReunr-DgSpg5O_6QTIYROqOaiMHGAX42vg.png\" width=\"30\" height=\"23\" \/>).<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/09\/AD_4nXeLJtj8xjnN-WQM7UQAop3RCg1hSElHa9WqFMBgXWGyxaxg07kOEhYnBV2i1WOnw5fUB_LsN-QWXidRJ-WMUVAkjF9aSVws684Jbnnoc8P-t_EDD622RghKpL5LjckKoKf251If3A.png\" width=\"391\" height=\"238\" \/><\/p>\n<p>Clique no bot\u00e3o + no canto superior esquerdo e, em seguida, escolha Python no menu suspenso <em>Add New Configuration<\/em>.<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-586344\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/07\/image-27.png\" alt=\"\" width=\"680\" height=\"1130\" \/><\/figure>\n<p>Escolha um nome que permita saber qual interpretador espec\u00edfico est\u00e1 sendo usado, por exemplo, 3.13.5 em vez de 3.15.3t. Escolha o interpretador correto e adicione o nome do script de teste, da seguinte forma:<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-586355\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/07\/image-28.png\" alt=\"\" width=\"1600\" height=\"1148\" \/><\/figure>\n<p>Adicione duas configura\u00e7\u00f5es, uma para cada interpretador. Em seguida, clique em <em>OK<\/em>.<\/p>\n<p>Agora, podemos selecionar e executar facilmente o script de teste com ou sem o GIL, selecionando a configura\u00e7\u00e3o e clicando no bot\u00e3o <em>Run<\/em> (<img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/09\/AD_4nXcRz0HogcsseQcC85-vSwRJhBWhiZCysBUZjcuu1uQYusTx-hj6h-l4FAs_HUE1gcJV5kjSjPuUu1opfKgu6sH5G9epkDlMMw0fBRPyReunr-DgSpg5O_6QTIYROqOaiMHGAX42vg.png\" width=\"30\" height=\"23\" \/>) na parte superior.<\/p>\n<h3 id=\"comparing-the-results\" class=\"wp-block-heading\">Comparando os resultados<\/h3>\n<p>Este \u00e9 o resultado que obtive ao executar a vers\u00e3o padr\u00e3o 3.13.5 com o GIL:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Python version: 3.13.5 (main, Jul 10 2025, 20:33:15) [Clang 17.0.0 (clang-1700.0.13.5)]\nCPU count: 8\nRange size: 1000000\nNumber of chunks: 16\n------------------------------------------------------------\nRunning single-threaded version (baseline)...\nFound 78498 primes in 1.1930 seconds\n------------------------------------------------------------\nRunning multi-threaded version with 4 threads...\nFound 78498 primes in 1.2183 seconds (speedup: 0.98x)\n------------------------------------------------------------\nSUMMARY:\nThreads    Time (s)     Speedup   \n1 (baseline) 1.1930       1.00x     \n4          1.2183       0.98x\n<\/pre>\n<p>Como voc\u00ea pode ver, n\u00e3o h\u00e1 altera\u00e7\u00e3o significativa na velocidade ao executar a vers\u00e3o com 4 threads em compara\u00e7\u00e3o com a linha de base de thread \u00fanico. Vejamos o que obtemos ao executar a vers\u00e3o com threads livres 3.13.5t:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Python version: 3.13.5 experimental free-threading build (main, Jul 10 2025, 20:36:28) [Clang 17.0.0 (clang-1700.0.13.5)]\nCPU count: 8\nRange size: 1000000\nNumber of chunks: 16\n------------------------------------------------------------\nRunning single-threaded version (baseline)...\nFound 78498 primes in 1.5869 seconds\n------------------------------------------------------------\nRunning multi-threaded version with 4 threads...\nFound 78498 primes in 0.4662 seconds (speedup: 3.40x)\n------------------------------------------------------------\nSUMMARY:\nThreads    Time (s)     Speedup   \n1 (baseline) 1.5869       1.00x     \n4          0.4662       3.40x\n<\/pre>\n<p>Dessa vez, a velocidade foi tr\u00eas vezes maior. Observe que, em ambos os casos, h\u00e1 uma sobrecarga para multithread. Portanto, mesmo com o verdadeiro multithread, a velocidade n\u00e3o \u00e9 4 vezes maior com 4 threads.<\/p>\n<h2 id=\"conclusion\" class=\"wp-block-heading\">Conclus\u00e3o<\/h2>\n<p>Na parte 2 da s\u00e9rie de postagens do nosso blog sobre o Python mais r\u00e1pido, discutimos o motivo de termos o GIL do Python no passado, contornando a limita\u00e7\u00e3o do GIL usando multiprocessamento, bem como o processo e o efeito da remo\u00e7\u00e3o do GIL.<\/p>\n<p>At\u00e9 o momento desta postagem do nosso blog, a vers\u00e3o com threads livres do Python ainda n\u00e3o \u00e9 o padr\u00e3o. No entanto, com a ado\u00e7\u00e3o da comunidade e de bibliotecas de terceiros, a comunidade espera que a vers\u00e3o com threads livres do Python seja o padr\u00e3o no futuro. Foi anunciado que o Python 3.14 incluir\u00e1 uma vers\u00e3o com threads livres que passar\u00e1 do est\u00e1gio experimental, mas ainda ser\u00e1 opcional.<\/p>\n<p>O <a href=\"https:\/\/www.jetbrains.com\/pycharm\/\" target=\"_blank\" rel=\"noopener\">PyCharm<\/a> fornece o melhor suporte ao Python da categoria para garantir velocidade e precis\u00e3o. Aproveite o recurso mais inteligente de complementa\u00e7\u00e3o de c\u00f3digo, as verifica\u00e7\u00f5es de conformidade com o PEP 8, as refatora\u00e7\u00f5es inteligentes e uma variedade de inspe\u00e7\u00f5es para atender a todas as suas necessidades de programa\u00e7\u00e3o. Conforme demonstrado nesta postagem do nosso blog, o PyCharm fornece configura\u00e7\u00f5es personalizadas para interpretadores Python e configura\u00e7\u00f5es de execu\u00e7\u00e3o, permitindo que voc\u00ea alterne entre interpretadores com apenas alguns cliques, o que o torna adequado para uma ampla gama de projetos Python.<\/p>\n<div class=\"buttons\">\n<div class=\"buttons__row\"><a class=\"btn\" href=\"https:\/\/www.jetbrains.com\/pycharm\/\" target=\"\" rel=\"noopener\">Baixe o PyCharm agora<\/a><\/div>\n<\/div>\n\n\n<p><em>Artigo original em ingl\u00eas por:<\/em><\/p>\n\n\n    <div class=\"about-author \">\n        <div class=\"about-author__box\">\n            <div class=\"row\">\n                <div class=\"about-author__box-img\">\n                    <img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/01\/CheukTingHo-Kimono-e1738750639162-200x200.jpg\" width=\"200\" height=\"200\" alt=\"Cheuk Ting Ho\" loading=\"lazy\"  class=\"avatar avatar-200 wp-user-avatar wp-user-avatar-200 photo avatar-default\">\n                <\/div>\n                <div class=\"about-author__box-text\">\n                                            <h4>Cheuk Ting Ho<\/h4>\n                                                        <\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n","protected":false},"author":1086,"featured_media":593132,"comment_status":"closed","ping_status":"closed","template":"","categories":[1401,2347],"tags":[8056,5377],"cross-post-tag":[8851],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/pycharm\/593114"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/pycharm"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/types\/pycharm"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/users\/1086"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/comments?post=593114"}],"version-history":[{"count":5,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/pycharm\/593114\/revisions"}],"predecessor-version":[{"id":650264,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/pycharm\/593114\/revisions\/650264"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/media\/593132"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/media?parent=593114"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/categories?post=593114"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/tags?post=593114"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/cross-post-tag?post=593114"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}