欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

使用Pyrex來擴展和加速Python程序的教程

系統 1674 0

?Pyrex 是一種專門設計用來編寫 Python 擴展模塊的語言。根據 Pyrex Web 站點的介紹,“它被設計用來在友好易用的高級 Python 世界和凌亂的低級 C 世界之間搭建一個橋梁?!彪m然幾乎所有的 Python 代碼都可以作為有效的 Pyrex 代碼使用,但是您可以在 Pyrex 代碼中添加可選的靜態類型聲明,從而使得這些聲明過的對象以 C 語言的速度運行。
加速 Python

從某種意義上來說,Pyrex 只是不斷發展的 Python 類語言系列的一個部分:Jython、IronPython、Prothon、Boo、Vyper(現在沒人用了)、Stackless Python(以一種方式)或 Parrot runtime(以另外一種方式)。按照語言的術語來說,Pyrex 本質上是在 Python 中添加了類型聲明。它的另外幾個變化沒有這么重要(不過對 for 循環的擴展很漂亮)。

然而,您真正希望使用 Pyrex 的原因是它編寫的模塊比純 Python 運行得更快,可能會快很多。

實際上,Pyrex 會從 Pyrex 代碼生成一個 C 程序。中間文件 module.c 依然可以用于手工處理。然而對于“普通的” Pyrex 用戶來說,沒有什么理由需要修改所生成的 C 模塊。Pyrex 本身可以讓您訪問那些對速度至關重要的 C 級代碼,而節省了編寫內存分配、回收、指針運算、函數原型等的工作。Pyrex 還可以無縫地處理 Python 級對象的所有接口;通常它都是通過在必要的地方將變量聲明為 PyObject 結構并使用 Python C-API 調用進行內存處理和類型轉換而實現的。

對于大部分情況來說,Pyrex 不需要不斷對簡單數據類型變量進行裝箱(box) 和 拆箱(unbox) 操作,因此速度比 Python 更快。例如,Python 中的 int 類型是一個具有很多方法的對象。它有一個繼承樹,自己有一個計算好的“方法解析順序(mothod resolution order,MRO)”。它有分配和回收方法可以用于內存處理。它知道何時將自己轉換為一個 long 類型,以及如何對其他類型的值進行數值運算。所有這些額外的功能都意味著在使用 int 對象進行處理時需要經過更多級的間接處理或條件檢查。另外一方面,C 或 Pyrex 的 int 變量只是內存中各個位設置為 1 或 0 的一個區域。使用 C/Pyrex 的 int 類型進行處理不需要涉及 任何 間接操作或條件檢查。一個 CPU “加”操作在硅芯片中就可以執行完了。

在仔細選擇的情況中,Pyrex 模塊的速度可以比 Python 版本的相同模塊的運行速度快 40 到 50 倍。但是與使用 C 本身 編寫的模塊相比,Pyrex 版本的模塊幾乎都不會比 Python 版本的模塊更長,代碼更類似于 Python,而不是 C。

當然,當您開始談論加速(類)Python 模塊時,Pyrex 并不是惟一可用的工具。在 Python 開發者的選擇中,也可以使用 Psyco。Psyco 可以保持代碼非常簡短;它是(x86)機器代碼中的一個 JIT Python 代碼編譯器。與 Pyrex 不同,Psyco 并不會精確地限定變量的類型,而是根據數據 可能 是哪種類型的每種假設為每個 Python 代碼塊創建幾種可能的機器代碼。如果在一個給定的代碼段中數據是是簡單類型,例如 int,那么這段代碼(如果是一個循環,這種情況就更為突出)就可以很快地運行。例如,x 在一個執行一百萬次的循環中可以是 int 類型,但是在循環結束時可以依然是一個 float 類型的值。Psyco 可以使用與在 Pyrex 中顯式指定的類型相同的類型來加速循環。

雖然 Pyrex 也并不難,但是 Psyco 更加簡單易用。使用 Psyco 不過是在模塊的末尾加上幾行;實際上,如果加上正確的代碼,那么即使在 Psyco 不可用時,模塊也可以同樣運行(只是速度較慢)。
清單 1. 只有在 Psyco 可用時才使用 Psyco

            
# Import Psyco if available
try:
  import psyco
  psyco.full()
except ImportError:
  pass


          

要使用 Pyrex,需要對代碼進行的修改會更多(但也不過是多一點而已),系統中還需要安裝一個 C 編譯器,并正確對生成 Pyrex 模塊的系統進行配置。雖然您 可以 分發二進制的 Pyrex 模塊,但是為了能使您的模塊在其他地方也可以運行,Python 的版本、架構和終端用戶需要的優化選項必須匹配。

速度初體驗

我最近為 developerWorks 的文章 Beat spam using hashcash 創建了一個純 Python 的 hashcash 實現,但是基本上來說,hashcash 是一種使用 SHA-1 提供 CPU 工作的技術。Python 有一個標準的模塊 sha,這使得編寫 hashcash 非常簡單。

與我編寫的 95% 的 Python 程序不同,hashcash 模塊緩慢的速度讓我心煩,至少有那么一點點心煩。按照設計,這個協議就是要吃光所有的 CPU 周期,因此運行效率非常關鍵。hashcash.c 的 ANSI C 二進制文件運行的速度是這個 hashcash.py 腳本的 10 倍。而且啟用了 PPC/Altivec 的優化后的 hashcash.c 二進制文件的速度是普通的 ANSI C 版本的 4 倍(1Ghz 的 G4/Altivec 在處理 hashcash/SHA 操作時的速度相當于 3Ghz 的 Pentium4?/MMX;G5 的速度會更快)。因此在我的 TiPowerbook 上的測試顯示,這個模塊的速度比優化后的 C 版本速度慢 40 倍(不過在 x86 上的差距沒有這么大)。

由于這個模塊的運行速度很慢,可能 Pyrex 會是一個比較好的加速方法。至少我認為是如此?!癙yrex 化” hashcash.py 的第一件事情(當然是在安裝 Pyrex 之后)是簡單地將其拷貝為 hashcash_pyx.pyx,并試圖這樣處理:

            
$ pyrexc hashcash_pyx.pyx


          

創建二進制模塊

運行這個命令會生成一個 hashcash.c 文件(這會對源文件進行一些微小的改動)。不幸的是,調整 gcc 開關剛好適合我的平臺需要點技巧,因此我決定采用推薦的捷徑,讓 distutils 為我做一些工作。標準的 Python 安裝知道如何在模塊安裝過程中使用本地的 C 編譯器,以及如何使用 distutils 來簡化 Pyrex 模塊的共享。我創建了一個 setup_hashcash.py 腳本,如下所示:
清單 2. setup_hashcash.py 腳本

            
from distutils.core import setup
from distutils.extension import Extension
from Pyrex.Distutils import build_ext
setup(
 name = "hashcash_pyx",
 ext_modules=[
  Extension("hashcash_pyx", ["hashcash_pyx.pyx"], libraries = [])
  ],
 cmdclass = {'build_ext': build_ext}
)


          

運行下面的命令,完整地編譯一個基于 C 的擴展模塊 hashcash:

            
$ python2.3 prime_setup.py build_ext --inplace


          

代碼修改

我把從 hashcash.pyx 生成基于 C 的模塊的工作有些簡化了。實際上,我需要對源代碼進行兩處修改;通過查找 pyrexc 抱怨的位置來找到要修改的位置。在代碼中,我使用了一個不支持的列表,將其放入一個普通的 for 循環。這非常簡單。我還將增量賦值從 counter+=1 修改為 counter=counter+1。

就這么多了。這就是我的第一個 Pyrex 模塊。

測試速度

為了可以簡單地測試要開發的模塊的速度提高情況,我編寫了一個簡單的測試程序來運行不同版本的模塊:
清單 3. 測試程序 hashcash_test.py

            
#!/usr/bin/env python2.3
import time, sys, optparse
hashcash = __import__(sys.argv[1])
start = time.time()
print hashcash.mint('mertz@gnosis.cx', bits=20)
timer = time.time()-start
sys.stderr.write("%0.4f seconds (%d hashes per second)\n" %
    (timer, hashcash.tries[0]/timer))


          

令人興奮的是,我決定來看一下只通過 Pyrex 編譯可以怎樣提高速度。注意在下面所有的例子中,真實的時間變化很大,都是隨機的。我們要看的內容是“hashes per second”,它可以精確可靠地測量速度。因此比較一下純粹的 Python 和 Pyrex:
清單 4. 純 Python 和 “純 Pyrex”的比較

            
$ ./hashcash_test.py hashcash
1:20:041003:mertz@gnosis.cx::I+lyNUpV:167dca
13.7879 seconds (106904 hashes per second)
$ ./hashcash_test.py hashcash_pyx > /dev/null
6.0695 seconds (89239 hashes per second)


          

噢!使用 Pyrex 幾乎慢了 20%。這并不是我期望的。現在應該來分析一下代碼可能加速的地方了。下面這個簡短的函數會試圖消耗所有的時間:
清單 5. hashcash.py 中的函數

            
def _mint(challenge, bits):
  "Answer a 'generalized hashcash' challenge'"
  counter = 0
  hex_digits = int(ceil(bits/4.))
  zeros = '0'*hex_digits
  hash = sha
  while 1:
    digest = hash(challenge+hex(counter)[2:]).hexdigest()
    if digest[:hex_digits] == zeros:
      tries[0] = counter
      return hex(counter)[2:]
    counter += 1


          

我需要利用 Pyrex 變量聲明的優點來進行加速。有些變量顯然是整數,另外一些變量顯然是字符串 ―― 我們可以指定這些類型。在進行修改時,我將使用 Pyrex 的經過改進的 for 循環:
清單 6. 經過最低限度 Pyrex 改進的 mint 函數

            
cdef _mint(challenge, int bits):
  # Answer a 'generalized hashcash' challenge'"
  cdef int counter, hex_digits, i
  cdef char *digest
  hex_digits = int(ceil(bits/4.))
  hash = sha
  for counter from 0 <= counter < sys.maxint:
    py_digest = hash(challenge+hex(counter)[2:]).hexdigest()
    digest = py_digest
    for i from 0 <= i < hex_digits:
      if digest[i] != c'0': break
    else:
      tries[0] = counter
      return hex(counter)[2:]


          

到現在為止一切都非常簡單。我只聲明了早已知道的一些變量類型,并使用最干凈的 Pyrex counter 循環。一個小技巧是將 py_digest(一個 Python 字符串)賦值給 digest(一個 C/Pyrex 字符串),目的是確定其類型。經過實驗,我還發現循環字符串比較操作速度都非??臁_@些會帶來什么好處呢?
清單 7. Pyrex 化 mint 函數的速度結果

            
$ ./hashcash_test.py hashcash_pyx2 >/dev/null
20.3749 seconds (116636 hashes per second)


          

這下好多了。我已經對原有的 Python 進行了一些細微的改進,這可以稍微提高最初的 Pyrex 模塊的速度。不過效果還不明顯,僅僅提高了很少的百分比。
剖析

有些東西似乎不對。速度提高幾個百分比和 Pyrex 主頁(以及很多 Pyrex 用戶)那樣提高 40 倍有很大的差距?,F在應該來看一下 這個 Python _mint() 函數中 哪些 地方真正消耗了時間。有一個 quick 腳本(此處沒有給出)可以分解復雜操作 sha(challenge+hex(counter)[2:]).hexdigest():
清單 8. hashcash 的 mint 函數的時間消耗

            
1000000 empty loops:   0.559
------------------------------
1000000 sha()s:     2.332
1000000 hex()[2:]s:   3.151
  just hex()s:     <2.471>
1000000 concatenations: 0.855
1000000 hexdigest()s:  3.742
------------------------------
Total:         10.079


          

顯然,我并不能將這個循環從 _mint() 函數中刪除。雖然 Pyrex 改進后的 for 循環可能有一點加速,但是整個函數主要是一個循環。我也不能刪除對 sha() 的調用,除非要使用 Pyrex 重新實現 SHA-1(即使我要這樣做,也沒有自信自己可以比 Python 標準的 sha 模塊的作者做得更好)。而且,如果我希望得到一個 sha.SHA 對象的 hash 值,就只能調用 .hexdigest() 或 .digest();前者的速度更快。

現在真正要解決的是 hex() 對 counter 變量的轉換,以及結果中時間片的消耗情況。我可能需要使用 Pyrex/C 的字符串連接操作,而不是 Python 的字符串對象。然而,我見過的惟一一種避免 hex() 轉換的方法是手工在嵌套循環之外構建一個后綴。雖然這樣做可以避免 int 到 char 類型的轉換,但是需要生成更多代碼:
清單 9. 完全 Pyrex 優化過的 mint 函數

            
cdef _mint(char *challenge, int bits):
  cdef int hex_digits, i0, i1, i2, i3, i4, i5
  cdef char *ab, *digest, *trial, *suffix
  suffix = '******'
  ab = alphabet
  hex_digits = int(ceil(bits/4.))
  hash = sha
  for i0 from 0 <= i0 < 55:
    suffix[0] = ab[i0]
    for i1 from 0 <= i1 < 55:
      suffix[1] = ab[i1]
      for i2 from 0 <= i2 < 55:
        suffix[2] = ab[i2]
        for i3 from 0 <= i3 < 55:
          suffix[3] = ab[i3]
          for i4 from 0 <= i4 < 55:
            suffix[4] = ab[i4]
            for i5 from 0 <= i5 < 55:
              suffix[5] = ab[i5]
              py_digest = hash(challenge+suffix).hexdigest()
              digest = py_digest
              for i from 0 <= i < hex_digits:
                if digest[i] != c'0': break
              else:
                return suffix


          

雖然這個 Pyrex 函數看起來仍然比對應的 C 函數更加簡單易讀,但是它實際上最初的純 Python 的版本更為復雜。通過這種方式,在純 Python 中展開后綴生成與最初的版本相比會對總體速度有些負面的影響。在 Pyrex 中,正如您期望的一樣,這些嵌套的循環都是很少花費時間的,因而我節省了轉換和分時調度的代價:
清單 10. mint 函數 Pyrex 化優化后的速度結果

            
$ ./hashcash_test.py hashcash_pyx3 >/dev/null
13.2270 seconds (166125 hashes per second)


          

當然,這比我開始的時候好多了。但是速度提高也不過是兩倍。大部分時間的問題是(此處也是)消耗了太多的時間在對 Python 庫的調用上,而我并不能對這些調用編寫代碼來提高速度。
令人失望的比較

速度提高 50% 到 60% 似乎是值得的。達到這個目標我并沒有編寫 多少 代碼。但是如果您認為是在原來的 Python 版本中添加 兩條 語句 import psyco;psyco.bind(_mint),那么這種加速方法就不會給您多深的印象:
清單 11. mint 函數 Psyco 化的加速結果

            
$ ./hashcash_test.py hashcash_psyco >/dev/null
15.2300 seconds (157550 hashes per second)


          

換而言之,Psyco 之不過添加了兩行通用的代碼,就幾乎能實現相同的目標。當然,Psyco 只能用于 x86 平臺,而 Pyrex 可以在具有 C 編譯器的所有環境上執行。但是對于這個特定的例子來說,os.popen('hashcash -m '+options) 的速度會比 Pyrex 和 Psyco 都快很多倍(當然,假設可以使用 C 工具 hashcash)。

?


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!?。?/p>

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 在线观看中文字幕 | 美国三级日本三级久久99 | 人人爽久久涩噜噜噜蜜桃 | 韩国在线精品福利视频在线观看 | 精品国产乱码久久久久久88av | 一级毛片一级毛片一级毛片 | 国产午夜精品一区二区三区嫩草 | 国产天堂网 | 国内久久精品 | 日本一区二区三区中文字幕 | 午夜视频在线免费观看 | 国产综合50p | 日韩在线观看视频一区 | 十六以下岁女子毛片免费 | 黄网站涩免费蜜桃网站 | 久草中文在线 | 国产毛A片啊久久久久久A | 免费性生活视频 | 99在线精品视频 | 91你懂的| 久久久日韩精品一区二区 | 国产激情 | 国产不卡视频在线 | 国产免费又色又爽又黄的网站 | 久久精品伊人网 | 国产成人一级 | 91天堂网| 91精品国产综合久久福利软件 | 国产精品一区视频 | 高清国产一区二区三区四区五区 | 中文字幕视频在线观看 | 三级中文字幕 | av在线成人 | 亚洲成a人片77777在线播放 | 欧美老妇交乱视频 | cheaperapp.work直接访问 | 国产精品99一区二区三区 | 精品国产一区二区亚洲人成毛片 | 欧美日韩亚洲一区 | 性色网站 | 亚洲AV综合AV国产AV |