PyNaCI 是否释放了 GIL,是否应该与多线程一起使用

zet*_*had 3 python multithreading gil libsodium pynacl

PyNaCI会释放全局解释器锁吗?使用 PyNaCI 的多线程加密是否适合SecretBox

我想使用 PyNaCl 加密相对大量的数据(~500 MB)。为此,我将其分成大约 5MB 的块并使用ThreadPoolExecutor(). 这是理想的吗?我不知道 PyNaCl 是否发布了 Python 的 GIL,也不知道它是否会真正并行加密块,从而提高性能。

编辑:为了避免混淆,让我澄清一下实验结果。在对当前的实现进行了多次测试之后,我发现它比直接加密整个数据要快一些,并且比简单的for循环要快得多。然而,我需要确凿的证据(参考文档,或者某种测试)来证明任务确实是并行运行的并且GIL不会阻塞性能。

这是我当前使用的实现ThreadPoolExecutor()

from concurrent.futures import ThreadPoolExecutor
from os import urandom
from typing import Tuple

from nacl import secret
from nacl.bindings import sodium_increment
from nacl.secret import SecretBox


def encrypt_chunk(args: Tuple[bytes, SecretBox, bytes, int]):
    chunk, box, nonce, macsize = args
    try:
        outchunk = box.encrypt(chunk, nonce).ciphertext
    except Exception as e:
        err = Exception("Error encrypting chunk")
        err.__cause__ = e
        return err
    if not len(outchunk) == len(chunk) + macsize:
        return Exception("Error encrypting chunk")
    return outchunk


def encrypt(
    data: bytes,
    key: bytes,
    nonce: bytes,
    chunksize: int,
    macsize: int,
):
    box = SecretBox(key)
    args = []
    total = len(data)
    i = 0
    while i < total:
        chunk = data[i : i + chunksize]
        nonce = sodium_increment(nonce)
        args.append((chunk, box, nonce, macsize,))
        i += chunksize
    executor = ThreadPoolExecutor(max_workers=4)
    out = executor.map(encrypt_chunk, args)
    executor.shutdown(wait=True)
    return out
Run Code Online (Sandbox Code Playgroud)

zet*_*had 5

PyNaCI使用通用外部函数接口 (CFFI)提供与 C 库Libsodium的绑定。我们可以看到该SecretBox函数基本上是Libsodium库函数的绑定crypto_secretbox()

根据CFFI 文档

[2] C 函数调用是在释放 GIL 的情况下完成的。

由于 PyNaCI 的大多数函数都是使用 CFFI 绑定到 Libsodium 库,因此它们将在 C 函数执行期间释放全局解释器锁。

这应该可以解释多线程的性能改进。