吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 6952|回复: 34
收起左侧

[Windows] python 3.9.9 x64 去除GIL(全局解释器锁)版——让多线程不再鸡肋

  [复制链接]
xqyqx 发表于 2022-5-10 14:01
本帖最后由 xqyqx 于 2022-5-10 16:03 编辑

0x00 前言
最近玩python时想实现多进程多线程混合运行,了解到了GIL(全局解释器锁)的存在,关于GIL,可以参见下面的官方文档
https://docs.python.org/zh-cn/3/glossary.html?highlight=gil#term-global-interpreter-lock
也就是说,python的多线程并不能实现真正意义上的并行,虽然多进程没有GIL的问题,但是创建进程的开销、内存占用以及通信效率都是多进程的问题(更何况多线程多进程我全都想要),所以我开始尝试修改Cpython的源码以去除GIL,但未果。
后来某次在GitHub上发现了一个去除GIL的项目,并且被认为“有希望在未来几年里真正进入 CPython”(项目地址:https://github.com/colesbury/nogil),本版python即基于此源码编译
0x01 过程
下载源码之后,查阅相关资料发现python源码可在Linux/Unix上直接编译运行(Linux/Unix的朋友可以直接去那个项目上git clone然后make就行了),但Windows上的编译却十分麻烦(主要是坑太多了),我便萌发了将其制作为安装包形式的想法。
查阅官方文档得知编译python源码需要VS2017环境,安装之后进入PCBuild文件夹,先下载依赖项,之后运行build.bat
第一次编译就报错了,后来经过多方排查后发现是该项目的作者使用了仅能运行于64位系统上的API函数,32位的编译就只能放弃了,执行命令:.\build.bat -p x64
编译完成,运行根目录下的python.bat即可打开python命令行
运行项目作者所修改版本中特有的命令:import sys; print(sys.flags.nogil),无报错,编译成功
2022-05-10_132141.png
但是这虽然能在本机上使用,但不便于分发,源码中包含了太多不必要的文件,且文件位置与发行版python不符,因此需要将其做成msi安装包
制作msi安装包过程中的坑特别多,每次编译了半个小时左右就突然给你报错,网上关于这方面的信息少之又少,因此只能自己手动排查,排查过程这里就不再赘述
最终在.\PCBuild\AMD64\en-us目录下生成了安装包与embed包(解压即用)
编译完成留念:
2022-05-08_134205.png


0x02测试
安装过程挺顺利的,一次成功
2022-05-08_214951.png
2022-05-08_134235.png
2022-05-08_215122.png


全家福:
2022-05-10_132958.png
2022-05-10_133057.png
2022-05-10_133219.png
2022-05-10_133315.png


接下来我们来验证其是否真正去除了GIL
测试代码如下(多线程跑死循环):
[Python] 纯文本查看 复制代码
from threading import Thread
import sys
def running():
    while True:
        pass
    return True
 
def main():
    thread_array = {}
    for tid in range(8):
        t = Thread(target=running)
        t.start()
        thread_array[tid] = t
    for i in range(8):
        thread_array[i].join()
if __name__ == '__main__':
    main()

测试结果如下(对比测试环境为python3.9.5(即normal),测试时系统环境保持不变):
3.9.9-nogil:
2022-05-08_202342.png
3.9.5-normal:
2022-05-08_220625.png
我的电脑是四核,因此如果带有GIL的话,理论CPU占用率不高于25%,但在nogil版本中cpu占用更是跑满,因此可以验证GIL已成功去除。


接下来我们来看一看去除GIL后的性能表现如何
我们用一个计数器来模拟CPU密集型任务:
[Python] 纯文本查看 复制代码
from threading import Thread
import time
import sys
def my_counter():
    i = 0
    for _ in range(100000000):
        i = i + 1
    return True
 
def main():
    thread_array = {}
    start_time = time.time()
    for tid in range(2):
        t = Thread(target=my_counter)
        t.start()
        thread_array[tid] = t
    for i in range(2):
        thread_array[i].join()
    end_time = time.time()
    print("Total time: {}".format(end_time - start_time))
 
if __name__ == '__main__':
    main()

结果如下:
normal单线程:
单线程gil.png
nogil单线程:
单线程nogil.png
normal多线程:
多线程gil.png
nogil多线程:
多线程nogil.png

结果排名:nogil多线程>normal单线程>normal多线程>nogil单线程
可见去除GIL后多线程性能确实增加,但以单线程性能下降作为代价

再来用一个多任务测试(斐波那契、累加、阶乘,都运用了递归运算):
[Python] 纯文本查看 复制代码
import threading
import time

class MyThread(threading.Thread):

    def __init__(self, func, args, name=''):
        threading.Thread.__init__(self)
        self.name = name
        self.func = func
        self.args = args
    def getResult(self):
        return self.res

    def run(self):
        self.res = self.func(*self.args)
def fib(x):
    if x < 2:
        return 1
    return (fib(x - 2) + fib(x - 1))


def fac(x):
    if x < 2:
        return 1
    return (x * fac(x - 1))


def sum(x):
    if x < 2:
        return 1
    return (x + sum(x - 1))


funcs = [fib, fac, sum]
n = 35


def main():

    nfuncs = range(len(funcs))

    print('*** SINGLE THREAD')
    start_time = time.time()
    for i1 in range(3):
         for i in nfuncs:
             print(funcs[i](n))
    end_time = time.time()
    print("Total time: ",format(end_time - start_time))

    print('\n*** MULTIPLE THREADS')
    start_time = time.time()
    threads = []
    for i in nfuncs:
        t1 = MyThread(funcs[i], (n, ), funcs[i].__name__)
        t2 = MyThread(funcs[i], (n, ), funcs[i].__name__)
        t3 = MyThread(funcs[i], (n, ), funcs[i].__name__)
        threads.append(t1)
        threads.append(t2)
        threads.append(t3)
    for i in range(9):
        threads[i].start()
    for i in range(9):
        threads[i].join()
        print(threads[i].getResult())
    end_time = time.time()
    print("Total time: ",format(end_time - start_time))
    print('all DONE')


if __name__ == '__main__':
    main()


结果如下:
normal:
gil多任务.png
nogil:
nogil多任务.png

可见在递归上nogil不管是单线程还是多线程都吊打normal



总的来说如果使用nogil版的python解释器的话,在其中运用多线程会获得不错的性能,但同时也应当在编程中注意避免使用单线程,还要注意线程安全问题

0x03 结果
两个文件,exe为安装包,压缩包为解压即用
腾讯哈勃扫描结果:
https://habo.qq.com/file/showdetail?md5=ae9b7507e2575b7c1d8f87354819b65f
https://habo.qq.com/file/showdetail?md5=c6ab002a9e353cc742add4f4cbb147d4
仅供测试所用,请勿用于实际生产环境
下载地址.txt (150 Bytes, 下载次数: 181)

免费评分

参与人数 7吾爱币 +7 热心值 +7 收起 理由
易芷晴 + 1 + 1 我很赞同!
czm995212 + 1 + 1 我很赞同!
notrack + 1 + 1 用心讨论,共获提升!
nndyky + 1 + 1 鼓励转贴优秀软件安全工具和文档!
weizxh + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
痛痛痛痛 + 1 + 1 谢谢@Thanks!
ruanjiandiguo + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

uc359599080 发表于 2022-5-11 08:17
电脑cpu终结者吗?
 楼主| xqyqx 发表于 2022-5-13 08:24
zeroxia 发表于 2022-5-12 21:34
第一个测试,normal单线程和nogil多线程,结果都是12秒多。没区别啊。

第一个测试中只开了两个线程,体现不出多核的优点,仅仅是为了表明去除GIL后单线程性能有所降低
jun269 发表于 2022-5-11 09:08
楼主真是牛人啊,这些流行的语言都会搞,大神,
heisehaishui 发表于 2022-5-11 09:29
感谢楼主的慷概分享
pangpang02 发表于 2022-5-11 09:58
谢谢分享
LoveLanshu 发表于 2022-5-11 11:42
感谢楼主分享
yuechaomax 发表于 2022-5-11 11:49
这个版本下载来试一下,还没有研究这么深入。
seanwang 发表于 2022-5-11 12:47
试了一下还可以 还没深入研究。
underbider 发表于 2022-5-11 13:05
谢谢分享!!!多线程多进程不错!!!!
痛痛痛痛 发表于 2022-5-11 14:25
强悍的操作行为,顶一波
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 提醒:禁止复制他人回复等『恶意灌水』行为,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-26 07:14

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表