"일꾼이 일을 잘하려면 먼저 도구를 갈고 닦아야 한다." - 공자, 『논어』.
첫 장 > 프로그램 작성 > Python과 ArkScript 비동기 모델 비교

Python과 ArkScript 비동기 모델 비교

2024-11-08에 게시됨
검색:241

Comparing Python and ArkScript asynchronous models

파이썬은 최근 많은 주목을 받고 있습니다. 올해 10월로 예정된 3.13 릴리스에서는 GIL을 제거하는 대규모 작업이 시작됩니다. (거의) GIL이 없는 Python을 사용해 보고 싶어하는 호기심 많은 사용자를 위한 사전 릴리스가 이미 출시되었습니다.

이 모든 과대 광고로 인해 과거에 전역 VM 잠금(2020년 버전 3.0.12에 추가, 2022년 3.1.3에서 제거됨)이 있었기 때문에 내 언어인 ArkScript를 파헤치게 되었습니다. 사물을 비교하고 Python GIL의 방법과 이유를 더 깊이 파고들도록 강요합니다.

정의

  1. 시작하려면 GIL(전역 인터프리터 잠금)이 무엇인지 정의해 보겠습니다.

GIL(전역 인터프리터 잠금)은 스레드 실행을 동기화하기 위해 컴퓨터 언어 인터프리터에서 사용되는 메커니즘으로, 프로세스당 하나의 기본 스레드만 한 번에 기본 작업(예: 메모리 할당 및 참조 계산)을 실행할 수 있습니다. 시간.

Wikipedia — 전역 인터프리터 잠금

  1. 동시성은 두 개 이상의 작업이 겹치는 기간에 시작, 실행 및 완료될 수 있지만 그렇다고 두 작업이 동시에 실행된다는 의미는 아닙니다.

  2. 병렬성은 작업이 말 그대로 동시에 실행되는 경우입니다(예: 멀티코어 프로세서에서).

자세한 설명을 보려면 이 스택 오버플로 답변을 확인하세요.

파이썬의 GIL

GIL은 모든 데이터 구조에 대한 잠금을 획득하고 해제할 필요가 없기 때문에 단일 스레드 프로그램의 속도를 높일 수 있습니다. 전체 인터프리터가 잠겨 있으므로 기본적으로 안전합니다.

그러나 인터프리터당 하나의 GIL이 있으므로 병렬 처리가 제한됩니다. 두 개 이상의 코어를 사용하려면 별도의 프로세스(스레딩 대신 멀티프로세싱 모듈 사용)에서 완전히 새로운 인터프리터를 생성해야 합니다! 이는 무시할 수 없는 오버헤드를 추가하는 프로세스 간 통신에 대해 걱정해야 하기 때문에 단순히 새 스레드를 생성하는 것보다 더 큰 비용이 듭니다(벤치마크는 GeekPython — Python 3.13에서 GIL이 선택 사항이 됨 참조).

Python의 비동기에 어떤 영향을 미치나요?

Python의 경우 스레드로부터 안전한 메모리 관리 기능이 없는 주요 구현인 CPython에 달려 있습니다. GIL이 없으면 다음 시나리오에서 경쟁 조건이 발생합니다.

  1. 공유 변수 생성 개수 = 5
  2. 스레드 1: 개수 *= 2
  3. 스레드 2: 개수 = 1

스레드 1이 먼저 실행되면 카운트는 11이 됩니다(카운트 * 2 = 10, 그 다음 카운트 1 = 11).

스레드 2가 먼저 실행되면 개수는 12가 됩니다(개수 1 = 6, 그 다음 개수 * 2 = 12).

실행 순서는 중요하지만 더 나쁜 경우도 발생할 수 있습니다. 두 스레드가 동시에 카운트를 읽는 경우 하나는 다른 스레드의 결과를 지우고 카운트는 10 또는 6이 됩니다!

전반적으로 GIL을 사용하면 일반적인 경우에 (CPython) 구현이 더 쉽고 빨라집니다.

  • 단일 스레드의 경우 더 빠릅니다(모든 작업에 대해 잠금을 획득/해제할 필요가 없음)
  • IO 바인딩 프로그램의 멀티 스레드 경우 더 빠릅니다(GIL 외부에서 발생하기 때문입니다)
  • C에서 계산 집약적인 작업을 수행하는 CPU 바인딩 프로그램의 멀티 스레드 경우 더 빠릅니다(GIL은 C 코드를 호출하기 전에 릴리스되기 때문입니다)

또한 GIL 덕분에 스레드 안전성이 보장되므로 C 라이브러리 래핑이 더 쉬워집니다.

단점은 코드가 동시처럼 비동기이지만 병렬이 아니라는 점입니다.

[!메모]
Python 3.13에서 GIL이 제거됩니다!

PEP 703은 --disable-gil 빌드 구성을 추가하여 Python 3.13을 설치하면 멀티스레드 프로그램의 성능 향상 혜택을 누릴 수 있습니다.

Python 비동기/대기 모델

파이썬에서 함수는 색상을 취해야 합니다. 색상은 "정상"이거나 "비동기"입니다. 이것이 실제로 무엇을 의미하나요?

>>> def foo(call_me):
...     print(call_me())
... 
>>> async def a_bar():
...     return 5
... 
>>> def bar():
...     return 6
... 
>>> foo(a_bar)

:2: RuntimeWarning: coroutine 'a_bar' was never awaited
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
>>> foo(bar)
6

비동기 함수는 값을 즉시 반환하지 않고 오히려 코루틴을 호출하기 때문에 호출하는 함수가 비동기 콜백을 받도록 설계되지 않는 한 코루틴을 어디에서나 콜백으로 사용할 수 없습니다.

'일반' 함수는 비동기 함수를 호출하는 데 필요한 wait 키워드를 사용하여 비동기화해야 하기 때문에 함수의 계층 구조를 얻습니다.

         can call
normal -----------> normal

         can call
async - -----------> normal
       |
       .-----------> async                    

호출자를 신뢰하는 것 외에는 콜백이 비동기인지 아닌지 알 수 있는 방법이 없습니다(예외를 확인하기 위해 try/exc 블록 내에서 먼저 콜백을 호출하지 않는 한, 보기 흉합니다).

ArkScript 병렬성

처음에 ArkScript는 전역 VM 잠금(Python의 GIL과 유사)을 사용했습니다. 왜냐하면 http.arkm 모듈(HTTP 서버를 생성하는 데 사용됨)이 멀티스레드이고 변수 수정을 통해 상태를 변경하여 ArkScript의 VM에 문제를 일으켰기 때문입니다. 여러 스레드에서 함수를 호출합니다.

그런 다음 2021년에는 VM 상태를 쉽게 병렬화할 수 있도록 VM 상태를 처리하는 새로운 모델 작업을 시작했고 이에 대한 기사를 썼습니다. 이후 2021년 말까지 구현되었으며 전역 VM 잠금이 제거되었습니다.

ArkScript 비동기/대기

ArkScript는 비동기 함수에 색상을 할당하지 않습니다. 왜냐하면 함수가 언어에 존재하지 않기 때문입니다. 함수나 클로저가 있고 둘 다 추가 구문 없이 서로를 호출할 수 있습니다(클로저는 불쌍한 객체입니다. 이 언어에서는 변경 가능한 상태를 유지하는 함수입니다.

모든 함수는 호출 사이트에서 비동기화될 수 있습니다(선언 대신):

(let foo (fun (a b c)
    (  a b c)))

(print (foo 1 2 3))  # 6

(let future (async foo 1 2 3))
(print future)          # UserType
(print (await future))  # 6
(print (await future))  # nil

비동기 내장 기능을 사용하여 내부적으로 std::future를 생성하여(std::async 및 스레드 활용) 일련의 인수가 주어지면 함수를 실행합니다. 그런 다음 대기(또 다른 내장 기능)를 호출하고 원할 때마다 결과를 얻을 수 있으며, 이는 함수가 반환될 때까지 현재 VM 스레드를 차단합니다.

따라서 모든 함수와 스레드에서 대기하는 것이 가능합니다.

특이성

이 모든 것은 단일 스레드에 연결된 Ark::internal::ExecutionContext 내부에 포함된 상태에서 작동하는 단일 VM이 있기 때문에 가능합니다. VM은 컨텍스트가 아닌 스레드 간에 공유됩니다!

        .---> thread 0, context 0
        |            ^
VM  thread 1, context 1              

비동기를 사용하여 미래를 만들 때 우리는 다음을 수행합니다.

  1. 모든 인수를 새 컨텍스트에 복사합니다.
  2. 새로운 스택과 범위 생성,
  3. 마침내 별도의 스레드를 만듭니다.

ArkScript는 참조나 공유할 수 있는 모든 종류의 잠금을 노출하지 않기 때문에 스레드 간의 모든 종류의 동기화를 금지합니다(언어가 다소 미니멀하지만 여전히 사용 가능하도록 목표를 두기 때문에 이는 단순성을 위해 수행되었습니다).

그러나 이 접근 방식은 호출당 새 스레드를 생성하고 CPU당 스레드 수가 제한되어 약간 비용이 많이 들기 때문에 Python보다 좋지도 나쁘지도 않습니다. 운 좋게도 나는 그것을 해결해야 할 문제로 보지 않습니다. 수백 또는 수천 개의 스레드를 동시에 생성하거나 수백 또는 수천 개의 비동기 Python 함수를 동시에 호출해서는 안 되기 때문입니다. 두 가지 모두 프로그램 속도를 크게 저하시킬 수 있습니다.

첫 번째 경우, OS가 모든 스레드에 시간을 주기 위해 저글링하므로 프로세스(심지어 컴퓨터) 속도가 느려집니다. 두 번째 경우에는 모든 코루틴 사이를 저글링해야 하는 것이 Python의 스케줄러입니다.

[!메모]
기본적으로 ArkScript는 스레드 동기화를 위한 메커니즘을 제공하지 않지만 UserType(type-erased C 개체 위에 있는 래퍼)을 함수에 전달하더라도 기본 개체는 복사되지 않았습니다.

주의 깊게 코딩하면 UserType 구성을 사용하여 잠금을 생성하여 스레드 간 동기화를 허용할 수 있습니다.

(let lock (module:createLock))
(let foo (fun (lock i) {
  (lock true)
  (print (str:format "hello {}" i))
  (lock false) }))
(async foo lock 1)
(async foo lock 2)

결론

ArkScript와 Python은 두 가지 매우 다른 종류의 async/await를 사용합니다. 첫 번째는 호출 사이트에서 async를 사용해야 하고 자체 컨텍스트로 새 스레드를 생성하는 반면, 후자는 프로그래머가 함수를 비동기로 표시해야 합니다. 대기를 사용할 수 있으며 해당 비동기 함수는 인터프리터와 동일한 스레드에서 실행되는 코루틴입니다.

출처

  1. Stack Exchange — Python이 GIL로 작성된 이유는 무엇입니까?
  2. 파이썬 위키 — GlobalInterpreterLock
  3. stuffwithstuff - 귀하의 기능은 어떤 색상입니까?

원래 출처: lexp.lt

릴리스 선언문 이 기사는 https://dev.to/lexplt/comparing-python-and-arkscript-asynchronous-models-3l60?1에서 복제됩니다.1 침해 내용이 있는 경우, [email protected]에 연락하여 삭제하시기 바랍니다.
최신 튜토리얼 더>

부인 성명: 제공된 모든 리소스는 부분적으로 인터넷에서 가져온 것입니다. 귀하의 저작권이나 기타 권리 및 이익이 침해된 경우 자세한 이유를 설명하고 저작권 또는 권리 및 이익에 대한 증거를 제공한 후 이메일([email protected])로 보내주십시오. 최대한 빨리 처리해 드리겠습니다.

Copyright© 2022 湘ICP备2022001581号-3