찾다
백엔드 개발파이썬 튜토리얼如果GIL是低效的设计,与其对应的什么设计是好的替代方案?

回复内容:

早期 linux 也是大内核锁,进内核态就整个上锁,用户态可以并行。Big Kernel Lock 在语义上的好处是不存在死锁,既有的非多线程代码可以利用一点多核的优势。

“更好的设计” 并不有趣,只能一点一点地一粒一粒地替换成细力度的锁,这份工作在 linux 社区好像做了很多年。但是 linux 好在并不对 API 做承诺,谁修改了接口,谁就可以自己去修改所有的扩展,不在主线里的扩展不 care,挂就挂,谁让他们不进主线呢。

但 Python/Ruby 这种基于 C 的三方库文化浓厚的松散社区,接口的改动成本太高了,何况效益并不一定显著,比如:
  • 确保细粒度锁之后,原本的单线程脚本的性能没有影响?
  • 用三方库死锁了怎么规范?
  • 网络 IO 任务可以用协程缓一缓,那我们弄多线程图啥?计算密集谁用 python?
python wiki 里有篇文章比较详细地列了为什么干不掉 GIL: GlobalInterpreterLock

如果是新语言,躲开 GIL 则相对容易,限制 C 扩展的接口只走 FFI,不暴露解释器的内部实现细节,锁粒度的粗细就只是实现层面的问题了。但这要求语言的性能足够快,这时又成了一个性能和发布时间的权衡。 GIL算不得低效的设计。其坏处在于锁的粒度太粗。在我看来Lua的设计就很好。

Lua也有类似于GIL的结构,但锁的范围仅限于一个runtime环境。而一个OS进程内可以同时存在多个lua runtime环境。这使得一个lua环境里同一时间只能有一个正在执行的语句。想要OS进程内的并行执行,可以同时开很多个Lua环境,他们之间可以做通信。

所以Lua只锁住一个Lua环境,OS进程里可以有多个Lua环境一起执行。而Python GIL的问题在于他是OS进程里全局的。导致了不能同时执行Python语句。

一个环境里用锁来同步使得虚拟机的设计变的简单和高效。这本身无可厚非。Java在早期也是如此,但后来Java花了很大的精力在环境里做了更细粒度的锁。使得Java可以在一个虚拟机环境里并行执行多个线程。这使得Java的虚拟机变的复杂了许多,性能也会受到影响,因为代码并不好预测哪些变量和资源需要放到锁保护的区域里,只能全都检查一遍。而将虚拟机环境做一个全局锁就不需要检查每一个变量和资源了。

所以我认为好的设计。就是将GIL锁的范围从OS进程的全局改为虚拟机全局。使得一个OS进程里可以同时存在多个虚拟机。每个虚拟机里因为GIL的存在只能使用一个CPU核心。但多个虚拟机使得整个OS进程可以利用多个CPU核心,同时因为在一个OS进程内,数据交换可以直接用引用传递,不涉及内存拷贝带来的巨大开销。 加细粒度的lock实际上会让CPython跑单线程的程序时变慢,楼上一个答案说的还不如加个大锁就是这意思。。

大多数人觉得GIL不好,移除了多线程就能跑满多核了,这个思考方式有问题。。。移除GIL会显著增加CPython实现复杂度,抛开这个不提,写共享内存多线程程序的难度依然不会下降,你在C里会跌的坑,在这里一个不会少。

所以顺其自然吧,反正CPython水平也就那样,就不是冲着性能去的。。Python想占满多核也简单,做科学计算的话,用Theano/NumbaPro/numexpr优化numpy计算或者Cython裸写多线程都行,做高并发服务器的话,multiprocessing直接spawn出子进程来搞啊,而且像pulsar这种框架都帮你做好了 细粒度锁 + STM + TLS 呀:
  • 用细粒度锁来解决 GIL,如今的 mutex 使用 futex 在用户态实现,比原来快很多了。
  • 用 STM 来消减核心路径细粒度锁的开销
  • 用 TLS 来规避 time.time, random.randint 之类的全局函数访问同一个资源时发生的抢占
工作量问题。
---------------
接 @fleuria Linux内核用了更细粒度锁的问题,为啥 Linux 用更细粒度锁不会影响性能,而 Python用细粒度锁在没有竞争时又会导致额外开销呢?

因为内核用细粒度锁是 spin_lock,即 cmpxchg + while,在多核之间的竞争开销几乎为零,而同核之间并不会出现 spin_lock的开销,因为单核内开始 spin_lock 之前,一般都是把本cpu核心的 irq 给 disable掉的,见Linux irq_disable() / local_irq_disable() 等函数的实现,在允许 irq 前不会发生时钟中断导致任务切换。

而用户态能不能用 spin_lock呢?几乎不能,因为单核之间如果进入了 spin_lock 没有 unlock时被时钟中断给切走了,那么新调度进来的任务将会进入 spin_lock 的 while (cmpxchg(...) == xxx) ; 循环并且必须不停循环直到几十毫秒后一个时钟片结束被切回到第一个任务里 spin_unlock了,第二个任务才能获得锁并结束循环。这就是因为用户态无法把 irq 给 disable 掉,不能禁止时钟中断导致的任务被动切换后会进入上面的 spin_lock 单核内耗状态。

所以用户进程还得使用比内核 spin_lock + irq_disable/enable 组合慢很多倍的 mutex。索性 mutex有了用户态实现的 futex ,能在 lock时用 cmpxchg 先检测一下,如果没有竞就不争必进入内核态了,而真正有竞争时才进入内核态,所以 mutex 的占用比原来降低了不少。

所以如今有了 futex后,Python 在用户态用更细粒度锁是有可能的,占用比以前少了几个数量级了。然而即便是 futex 的性能也达不到内核态的 spin_lock + irq_disable,任然存在一些微弱的开销,那么在核心路径使用 stm 来解决,将会是一个比较好的搭配。

而过分暴露 C 接口给应用程序确实会带来一些问题,会使得细粒度锁和 STM 的实现变得更为复杂,工作量非常大,这也是 GIL 存在的真正原因。 -------- 2015-02-17 更新 --------
评论区开始变得有意思了。所以做下补充说明。
关于 ref count 和锁。我举的例子不恰当。
为了避免陷入具体语言实现细节。所以就以简单实际模型来做讨论。
结论是:ref count 来实现 gc 的话,不需要 os 提供的 thread lock 就可以。
gc, 重点是发现 g, 然后 c.

增加引用是不会出现 g 的
。增加引用后,其 rec count 值必然 >= 2 (创造 sth 时 ref count 为 1).
ref count 变成 0 后,是不会再变成 1 的。要让一个资源被人引用,首先资源必须存在才行。如果编译器实现是没问题的话,只有在语义上那个资源也是不可见时 ref count 才会变成 0。

interlocked operation 是什么呢?其实就是一个执行期间保持 lock 信号的,目地操作数为 mem 的一些非串指令(加 减 位运算 位测试等),保证多处理器访问共享内存时的独占问题。
这个指令就能避免这样的情况: ref count 为 2 时,两个不同处理器都对其 dec 后结果是 1(应该为0) 。

如何实现 GC?

<span class="k">if</span> <span class="nf">lockDecr</span><span class="p">(</span><span class="o">&</span><span class="n">sth</span><span class="p">.</span><span class="n">ref</span><span class="p">)</span> <span class="n">then</span> <span class="n">free</span><span class="p">(</span><span class="n">sth</span><span class="p">);</span>   <span class="cm">/* 实际应用的话, free 前需递归地对所有被 sth 引用的其他资源进行「发现,回收」动作。 */</span>
<span class="cm">/* if 发现 sth 为 G then C(sth) */</span>
替代 GIL 的更好设计是 coroutine(协程)。既然同一时间只能有一个 thread 在运行,不如让语言的虚拟机自己操作 coroutine stack。GIL 是个偷懒的做法,用 C runtime stack 来实现语言的 stack,然后用操作系统的内核调度来模拟 coroutine 自己操作 stack 的实现。

推荐一篇文章:Working simultaneously vs waiting simultaneously
注意其中说 GIL isn't great 的部分,提到了 Go 和 Erlang 的 context management。
其中还提到了 Coroutines in one page of C,和 Lua coroutine 的实现类似。

像 Ruby 有 continuation,就可以实现 coroutine 了。不过 full-continuation 的效率比 one-shot continuation (coroutine) 低很多。不知道 Ruby 有没有实现后者。

实在没有 coroutine,可以像 Node.js 那样用回调实现(类似 CPS)。不过 Python 没有 closure 就没办法了。

所以说,GIL 低效,但是还不算太糟糕。糟糕的是 Python 既用 GIL 还没有 closure,Ruby 既有 GIL 还有 continuation,这些放在一起就不搭了。 GIL不是设计,只是对早期没考虑到的问题的fix。所以相反只要考虑到多线程就行了(这里指实现层面,比如拿Ruby打比方jruby是可以支持真正多核的,但是Ruby因为标准库和很多c扩展没有考虑多线程的情况所以加上了GIL)。抛开GIL从语言层面上说的话加入类似Erlang的actor,rust的task之类特性都可以很轻松的写出正确的多线程程序 锁是共享的,更小粒度的lock应当比global 好
성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
Linux 터미널에서 Python 버전을 볼 때 발생하는 권한 문제를 해결하는 방법은 무엇입니까?Linux 터미널에서 Python 버전을 볼 때 발생하는 권한 문제를 해결하는 방법은 무엇입니까?Apr 01, 2025 pm 05:09 PM

Linux 터미널에서 Python 버전을 보려고 할 때 Linux 터미널에서 Python 버전을 볼 때 권한 문제에 대한 솔루션 ... Python을 입력하십시오 ...

HTML을 구문 분석하기 위해 아름다운 수프를 어떻게 사용합니까?HTML을 구문 분석하기 위해 아름다운 수프를 어떻게 사용합니까?Mar 10, 2025 pm 06:54 PM

이 기사에서는 HTML을 구문 분석하기 위해 파이썬 라이브러리 인 아름다운 수프를 사용하는 방법을 설명합니다. 데이터 추출, 다양한 HTML 구조 및 오류 처리 및 대안 (SEL과 같은 Find (), find_all (), select () 및 get_text ()와 같은 일반적인 방법을 자세히 설명합니다.

파이썬의 수학 모듈 : 통계파이썬의 수학 모듈 : 통계Mar 09, 2025 am 11:40 AM

Python의 통계 모듈은 강력한 데이터 통계 분석 기능을 제공하여 생물 통계 및 비즈니스 분석과 같은 데이터의 전반적인 특성을 빠르게 이해할 수 있도록 도와줍니다. 데이터 포인트를 하나씩 보는 대신 평균 또는 분산과 같은 통계를보고 무시할 수있는 원래 데이터에서 트렌드와 기능을 발견하고 대형 데이터 세트를보다 쉽고 효과적으로 비교하십시오. 이 튜토리얼은 평균을 계산하고 데이터 세트의 분산 정도를 측정하는 방법을 설명합니다. 달리 명시되지 않는 한,이 모듈의 모든 함수는 단순히 평균을 합산하는 대신 평균 () 함수의 계산을 지원합니다. 부동 소수점 번호도 사용할 수 있습니다. 무작위로 가져옵니다 수입 통계 Fracti에서

Tensorflow 또는 Pytorch로 딥 러닝을 수행하는 방법은 무엇입니까?Tensorflow 또는 Pytorch로 딥 러닝을 수행하는 방법은 무엇입니까?Mar 10, 2025 pm 06:52 PM

이 기사는 딥 러닝을 위해 텐서 플로와 Pytorch를 비교합니다. 데이터 준비, 모델 구축, 교육, 평가 및 배포와 관련된 단계에 대해 자세히 설명합니다. 프레임 워크, 특히 계산 포도와 관련하여 주요 차이점

인기있는 파이썬 라이브러리와 그 용도는 무엇입니까?인기있는 파이썬 라이브러리와 그 용도는 무엇입니까?Mar 21, 2025 pm 06:46 PM

이 기사는 Numpy, Pandas, Matplotlib, Scikit-Learn, Tensorflow, Django, Flask 및 요청과 같은 인기있는 Python 라이브러리에 대해 설명하고 과학 컴퓨팅, 데이터 분석, 시각화, 기계 학습, 웹 개발 및 H에서의 사용에 대해 자세히 설명합니다.

Python으로 명령 줄 인터페이스 (CLI)를 만드는 방법은 무엇입니까?Python으로 명령 줄 인터페이스 (CLI)를 만드는 방법은 무엇입니까?Mar 10, 2025 pm 06:48 PM

이 기사는 Python 개발자가 CLIS (Command-Line Interfaces) 구축을 안내합니다. Typer, Click 및 Argparse와 같은 라이브러리를 사용하여 입력/출력 처리를 강조하고 CLI 유용성을 향상시키기 위해 사용자 친화적 인 디자인 패턴을 홍보하는 세부 정보.

한 데이터 프레임의 전체 열을 Python의 다른 구조를 가진 다른 데이터 프레임에 효율적으로 복사하는 방법은 무엇입니까?한 데이터 프레임의 전체 열을 Python의 다른 구조를 가진 다른 데이터 프레임에 효율적으로 복사하는 방법은 무엇입니까?Apr 01, 2025 pm 11:15 PM

Python의 Pandas 라이브러리를 사용할 때는 구조가 다른 두 데이터 프레임 사이에서 전체 열을 복사하는 방법이 일반적인 문제입니다. 두 개의 dats가 있다고 가정 해

파이썬에서 가상 환경의 목적을 설명하십시오.파이썬에서 가상 환경의 목적을 설명하십시오.Mar 19, 2025 pm 02:27 PM

이 기사는 프로젝트 종속성 관리 및 충돌을 피하는 데 중점을 둔 Python에서 가상 환경의 역할에 대해 설명합니다. 프로젝트 관리 개선 및 종속성 문제를 줄이는 데있어 생성, 활성화 및 이점을 자세히 설명합니다.

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

뜨거운 도구

MinGW - Windows용 미니멀리스트 GNU

MinGW - Windows용 미니멀리스트 GNU

이 프로젝트는 osdn.net/projects/mingw로 마이그레이션되는 중입니다. 계속해서 그곳에서 우리를 팔로우할 수 있습니다. MinGW: GCC(GNU Compiler Collection)의 기본 Windows 포트로, 기본 Windows 애플리케이션을 구축하기 위한 무료 배포 가능 가져오기 라이브러리 및 헤더 파일로 C99 기능을 지원하는 MSVC 런타임에 대한 확장이 포함되어 있습니다. 모든 MinGW 소프트웨어는 64비트 Windows 플랫폼에서 실행될 수 있습니다.

SublimeText3 영어 버전

SublimeText3 영어 버전

권장 사항: Win 버전, 코드 프롬프트 지원!

에디트플러스 중국어 크랙 버전

에디트플러스 중국어 크랙 버전

작은 크기, 구문 강조, 코드 프롬프트 기능을 지원하지 않음

VSCode Windows 64비트 다운로드

VSCode Windows 64비트 다운로드

Microsoft에서 출시한 강력한 무료 IDE 편집기

ZendStudio 13.5.1 맥

ZendStudio 13.5.1 맥

강력한 PHP 통합 개발 환경