何_md5.md5は、なぜhashlib.md5はそんなに遅いですか?

WIM:

発見これは文書化されていない_md5遅いSTDLIBのに不満を取得する際hashlib.md5の実装。

MacBookのオン:

>>> timeit hashlib.md5(b"hello world")
597 ns ± 17.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
>>> timeit _md5.md5(b"hello world")
224 ns ± 3.18 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
>>> _md5
<module '_md5' from '/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload/_md5.cpython-37m-darwin.so'>

Windowsのボックスで:

>>> timeit hashlib.md5(b"stonk overflow")
328 ns ± 21.8 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
>>> timeit _md5.md5(b"stonk overflow")
110 ns ± 12.5 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
>>> _md5
<module '_md5' (built-in)>

Linuxマシンの場合:

>>> timeit hashlib.md5(b"https://adventofcode.com/2016/day/5")
259 ns ± 1.33 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
>>> timeit _md5.md5(b"https://adventofcode.com/2016/day/5")
102 ns ± 0.0576 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
>>> _md5
<module '_md5' from '/usr/local/lib/python3.8/lib-dynload/_md5.cpython-38-x86_64-linux-gnu.so'>

短いメッセージをハッシュするために、それが道高速です。長いメッセージ、同様のパフォーマンスのために。

なぜそれがアンダースコア拡張モジュールに隠され、そしてなぜhashlibにデフォルトで使用されるこの速い実装ではありませんか? 何である_md5モジュールは、なぜそれが公開APIを持っていませんか?

CristiFati:

ゴマのPython 2. 5(例えば、ハッシュとダイジェストが独自のモジュールで実施された: - MD5メッセージダイジェストアルゴリズムMD5が[Pythonの2.Docsは])。
始まるV2.5[Pythonの2.6.Docs]:hashlib -セキュアハッシュおよびメッセージダイジェストを添加しました。その目的は、することでした。

  1. (自分の名前を経由して)ハッシュ/ダイジェストに統一されたアクセス方法を提供します
  2. スイッチ(デフォルトでは外部の暗号化プロバイダには)(それはやり過ぎの可能性があり、すべてのこれらのアルゴリズムを維持するよう、その分野に特化一部のエンティティに委任する論理的なステップです)。当時OpenSSLは(そこに類似の束だった、十分に知られており、互換性の成熟:最良の選択だったのJavaプロバイダは、しかし、それらはかなり無用でした)

副作用として#2。Pythonの実装は、公開から隠されたAPI(:それらを改名さ_md5_sha1_sha256_sha512を、後者のものが追加さ:_blake2_sha3)、冗長性はしばしば混乱を作成して。
しかし、別の副作用だった_hashlib.soへの依存のOpenSSLさんの.so * libcryptoなど(これはニックス(少なくともLNX)特定の、上の勝利、静的のlibeay32.libはでリンクされた_hashlib.pyd、そしてまた_ssl.pydまで、(私はラメを検討している)V3。7 +OpenSSLの .dllは sはの一部であるPythonのインストール)。
おそらく上の90 +%として物事は、滑らかでマシンのOpenSSLには、 /デフォルトでインストールされましたが、例えばので、それがない場合、それらのために、多くの物事が壊れてしまうかもしれませんhashlibは、多くのモジュールによってインポートされた(その一例があるランダムそれ自体が他のロット)、これによってインポートされた少なくともしない1人ので(暗号化に全く関連しないコードの些細個目の視力)動作を停止します古い実装が保たれている理由です(しかし、再び、彼らは通りフォールバックだけあるのOpenSSLのバージョンは/よりよく維持されなければならないされています)。

[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q059955854]> ~/sopr.sh
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***

[064bit-prompt]> python3 -c "import sys, hashlib as hl, _md5, ssl;print(\"{0:}\n{1:}\n{2:}\n{3:}\".format(sys.version, _md5, hl._hashlib, ssl.OPENSSL_VERSION))"
3.5.2 (default, Oct  8 2019, 13:06:37)
[GCC 5.4.0 20160609]
<module '_md5' (built-in)>
<module '_hashlib' from '/usr/lib/python3.5/lib-dynload/_hashlib.cpython-35m-x86_64-linux-gnu.so'>
OpenSSL 1.0.2g  1 Mar 2016
[064bit-prompt]>
[064bit-prompt]> ldd /usr/lib/python3.5/lib-dynload/_hashlib.cpython-35m-x86_64-linux-gnu.so
        linux-vdso.so.1 =>  (0x00007fffa7d0b000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f50d9e4d000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f50d9a83000)
        libcrypto.so.1.0.0 => /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 (0x00007f50d963e000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f50da271000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f50d943a000)
[064bit-prompt]>
[064bit-prompt]> openssl version -a
OpenSSL 1.0.2g  1 Mar 2016
built on: reproducible build, date unspecified
platform: debian-amd64
options:  bn(64,64) rc4(16x,int) des(idx,cisc,16,int) blowfish(idx)
compiler: cc -I. -I.. -I../include  -fPIC -DOPENSSL_PIC -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -m64 -DL_ENDIAN -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -Wl,-Bsymbolic-functions -Wl,-z,relro -Wa,--noexecstack -Wall -DMD32_REG_T=int -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DMD5_ASM -DAES_ASM -DVPAES_ASM -DBSAES_ASM -DWHIRLPOOL_ASM -DGHASH_ASM -DECP_NISTZ256_ASM
OPENSSLDIR: "/usr/lib/ssl"
[064bit-prompt]>
[064bit-prompt]> python3 -c "import _md5, hashlib as hl;print(_md5.md5(b\"A\").hexdigest(), hl.md5(b\"A\").hexdigest())"
7fc56270e7a70fa81a5935b72eacbe29 7fc56270e7a70fa81a5935b72eacbe29

よるとhashlib:[Pythonの3.Docs]。algorithms_guaranteed

ハッシュアルゴリズムの名前を含むセットは、すべてのプラットフォームで、このモジュールでサポートされることが保証しました。「MD5」は奇数を提供し、いくつかの上流のベンダーにもかかわらず、このリストにある除外していることをPythonのビルド「に準拠しをFIPS」ことに注意してください。

それはの例です以下は、カスタムのPython 2.7のインストール(私はかなりしばらく前に建てられていること、価値、それは動的にリンクしていることに言及するのOpenSSL の.dll秒):

e:\Work\Dev\StackOverflow\q059955854>sopr.bat
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***

[prompt]> "F:\Install\pc064\HPE\OPSWpython\2.7.10__00\python.exe" -c "import sys, ssl;print(\"{0:}\n{1:}\".format(sys.version, ssl.OPENSSL_VERSION))"
2.7.10 (default, Mar  8 2016, 15:02:46) [MSC v.1600 64 bit (AMD64)]
OpenSSL 1.0.2j-fips  26 Sep 2016

[prompt]> "F:\Install\pc064\HPE\OPSWpython\2.7.10__00\python.exe" -c "import hashlib as hl;print(hl.md5(\"A\").hexdigest())"
7fc56270e7a70fa81a5935b72eacbe29

[prompt]> "F:\Install\pc064\HPE\OPSWpython\2.7.10__00\python.exe" -c "import ssl;ssl.FIPS_mode_set(True);import hashlib as hl;print(hl.md5(\"A\").hexdigest())"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ValueError: error:060A80A3:digital envelope routines:FIPS_DIGESTINIT:disabled for fips

スピードの質問については私は推測することができます。

  • Pythonの実装は、(明らかに)のために特別に書かれたPythonのために(はい、これは文法的に間違っている)それは「より多くの最適化」された意味、Pythonのジェネリックバージョンよりも、ともに存在するのpython * .soは(またはPythonの実行可能ファイル自体)
  • OpenSSL実装に存在*の.so libcryptoなど、それがラッパーによってアクセスされてい_hashlib.so間の前後変換し、Pythonのタイプ(PyObject *)とのOpenSSLもの(EVP_MD_CTX *を

上記を考慮すると、前者が(少なくともオーバーヘッド(関数コールやその他の小さなメッセージのために(わずかに)高速であることが理にかなってPythonの基礎となる操作)がハッシュ自体と比較して総時間のかなりの割合を取ります)。考慮すべき他の要因もあります(たとえば、かどうかのOpenSSLアセンブラのスピードアップを使用しました)。



更新#0

以下は、私自身のいくつかのベンチマークです。

code00.py

#!/usr/bin/env python

import sys
from hashlib import md5 as md5_openssl
from _md5 import md5 as md5_builtin
import timeit


def main(*argv):
    base_text = b"A"
    number = 1000000
    print("timeit attempts number: {0:d}".format(number))
    #x = []
    #y = {}
    for count in range(0, 16):
        factor = 2 ** count
        text = base_text * factor
        globals_dict = {"text": text}
        #x.append(factor)
        print("\nUsing a {0:8d} (2 ** {1:2d}) bytes message".format(len(text), count))
        for func in [
            md5_openssl,
            md5_builtin,
        ]:
            globals_dict["md5"] = func

            t = timeit.timeit(stmt="md5(text)", globals=globals_dict, number=number)
            print("    {0:12s} took: {1:11.6f} seconds".format(func.__name__, t))
            #y.setdefault(func.__name__, []).append(t)
    #print(x, y)


if __name__ == "__main__":
    print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    main(*sys.argv[1:])
    print("\nDone.")

出力

  • 勝利10 pc064(上で実行されているのDell Precision 5510ラップトップ):

    [prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe" code00.py
    Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 64bit on win32
    
    timeit attempts number: 1000000
    
    Using a        1 (2 **  0) bytes message
        openssl_md5  took:    0.449134 seconds
        md5          took:    0.120021 seconds
    
    Using a        2 (2 **  1) bytes message
        openssl_md5  took:    0.460399 seconds
        md5          took:    0.118555 seconds
    
    Using a        4 (2 **  2) bytes message
        openssl_md5  took:    0.451850 seconds
        md5          took:    0.121166 seconds
    
    Using a        8 (2 **  3) bytes message
        openssl_md5  took:    0.438398 seconds
        md5          took:    0.118127 seconds
    
    Using a       16 (2 **  4) bytes message
        openssl_md5  took:    0.454653 seconds
        md5          took:    0.122818 seconds
    
    Using a       32 (2 **  5) bytes message
        openssl_md5  took:    0.450776 seconds
        md5          took:    0.118594 seconds
    
    Using a       64 (2 **  6) bytes message
        openssl_md5  took:    0.555761 seconds
        md5          took:    0.278812 seconds
    
    Using a      128 (2 **  7) bytes message
        openssl_md5  took:    0.681296 seconds
        md5          took:    0.455921 seconds
    
    Using a      256 (2 **  8) bytes message
        openssl_md5  took:    0.895952 seconds
        md5          took:    0.807457 seconds
    
    Using a      512 (2 **  9) bytes message
        openssl_md5  took:    1.401584 seconds
        md5          took:    1.499279 seconds
    
    Using a     1024 (2 ** 10) bytes message
        openssl_md5  took:    2.360966 seconds
        md5          took:    2.878650 seconds
    
    Using a     2048 (2 ** 11) bytes message
        openssl_md5  took:    4.383245 seconds
        md5          took:    5.655477 seconds
    
    Using a     4096 (2 ** 12) bytes message
        openssl_md5  took:    8.264774 seconds
        md5          took:   10.920909 seconds
    
    Using a     8192 (2 ** 13) bytes message
        openssl_md5  took:   15.521947 seconds
        md5          took:   21.895179 seconds
    
    Using a    16384 (2 ** 14) bytes message
        openssl_md5  took:   29.947287 seconds
        md5          took:   43.198639 seconds
    
    Using a    32768 (2 ** 15) bytes message
        openssl_md5  took:   59.123447 seconds
        md5          took:   86.453821 seconds
    
    Done.
    
  • Ubtu 16のpc064VMがで実行中のVirtualBox上のマシン上で):

    [064bit-prompt]> python3 code00.py
    Python 3.5.2 (default, Oct  8 2019, 13:06:37) [GCC 5.4.0 20160609] 64bit on linux
    
    timeit attempts number: 1000000
    
    Using a        1 (2 **  0) bytes message
        openssl_md5  took:    0.246166 seconds
        md5          took:    0.130589 seconds
    
    Using a        2 (2 **  1) bytes message
        openssl_md5  took:    0.251019 seconds
        md5          took:    0.127750 seconds
    
    Using a        4 (2 **  2) bytes message
        openssl_md5  took:    0.257018 seconds
        md5          took:    0.123116 seconds
    
    Using a        8 (2 **  3) bytes message
        openssl_md5  took:    0.245399 seconds
        md5          took:    0.128267 seconds
    
    Using a       16 (2 **  4) bytes message
        openssl_md5  took:    0.251832 seconds
        md5          took:    0.136373 seconds
    
    Using a       32 (2 **  5) bytes message
        openssl_md5  took:    0.248410 seconds
        md5          took:    0.140708 seconds
    
    Using a       64 (2 **  6) bytes message
        openssl_md5  took:    0.361016 seconds
        md5          took:    0.267021 seconds
    
    Using a      128 (2 **  7) bytes message
        openssl_md5  took:    0.478735 seconds
        md5          took:    0.413986 seconds
    
    Using a      256 (2 **  8) bytes message
        openssl_md5  took:    0.707602 seconds
        md5          took:    0.695042 seconds
    
    Using a      512 (2 **  9) bytes message
        openssl_md5  took:    1.216832 seconds
        md5          took:    1.268570 seconds
    
    Using a     1024 (2 ** 10) bytes message
        openssl_md5  took:    2.122014 seconds
        md5          took:    2.429623 seconds
    
    Using a     2048 (2 ** 11) bytes message
        openssl_md5  took:    4.158188 seconds
        md5          took:    4.847686 seconds
    
    Using a     4096 (2 ** 12) bytes message
        openssl_md5  took:    7.839173 seconds
        md5          took:    9.242224 seconds
    
    Using a     8192 (2 ** 13) bytes message
        openssl_md5  took:   15.282232 seconds
        md5          took:   18.368874 seconds
    
    Using a    16384 (2 ** 14) bytes message
        openssl_md5  took:   30.681912 seconds
        md5          took:   36.755073 seconds
    
    Using a    32768 (2 ** 15) bytes message
        openssl_md5  took:   60.230543 seconds
        md5          took:   73.237356 seconds
    
    Done.
    

結果はあなたよりもかなり違うように見えます。私の場合:

  • [〜でどこかに開始512B ..〜1KiB ]サイズのメッセージを、のOpenSSLの実装は、より良い組み込みのものよりも実行しているようです
  • 私は、パターンを請求する少なすぎる結果があることを知っているが、両方の実装は、メッセージサイズと(時間的に)直線的に比例するように見えるようです(ただし、組み込みの傾きはビット急勾配のようです - それは悪いことを実行します意味します)長期的に

結論として、すべてのメッセージが小さい、および組み込みの実装は次にそれを使用して、あなたに最適な動作するかどうか。



更新#1

グラフィカルな表現(私は削減しなければならなかったはtimeitそれが大きなメッセージのために非常に時間がかかりすぎるとして、一桁の反復回数を):

IMG0

2つのグラフが交差する領域にズーム:

IMG1

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=8889&siteId=1