[英]Optimizing python for loops
這是兩個程序,天真地計算素數<= n。
一個是Python,另一個是Java。
public class prime{
public static void main(String args[]){
int n = Integer.parseInt(args[0]);
int nps = 0;
boolean isp;
for(int i = 1; i <= n; i++){
isp = true;
for(int k = 2; k < i; k++){
if( (i*1.0 / k) == (i/k) ) isp = false;
}
if(isp){nps++;}
}
System.out.println(nps);
}
}
`#!/usr/bin/python`
import sys
n = int(sys.argv[1])
nps = 0
for i in range(1,n+1):
isp = True
for k in range(2,i):
if( (i*1.0 / k) == (i/k) ): isp = False
if isp == True: nps = nps + 1
print nps
在n = 10000上運行它我得到以下時間。
shell:〜$ time python prime.py 10000 && time java prime 10000
1230
真實的0m49.833s
用戶0m49.815s
sys 0m0.012s
1230
真正的0m1.491s
用戶0m1.468s
sys 0m0.016s
我在python中以不正確的方式使用for循環或者python實際上只是這么慢嗎?
我不是在尋找專門用於計算素數的答案,而是我想知道python代碼是否通常以更智能的方式使用。
Java代碼是使用javac 1.6.0_20編譯的
使用java版本“1.6.0_18”運行
OpenJDK運行時環境(IcedTea6 1.8.1)(6b18-1.8.1-0ubuntu1~9.10.1)OpenJDK客戶端VM(內置16.0-b13,混合模式,共享)
Python是:
Python 2.6.4(r264:75706,2009年12月7日,18:45:15)
正如已經指出的那樣,直接Python真的不是為了這種事情。 主要檢查算法是天真的也不是重點。 但是,通過兩個簡單的事情,我可以在使用原始算法時大大減少Python的時間。
首先,將所有內容放在函數內部,將其稱為main()
或其他內容。 這使我的機器在Python上的時間從20.6秒減少到14.54秒。 全局做事比在函數中做得慢。
其次,使用JIT編譯器Psyco。 這需要在文件頂部添加兩行(當然還安裝了psyco):
import psyco
psyco.full()
這使最后時間達到2.77秒。
最后一點。 我決定在此使用Cython並將時間降至0.8533。 但是,知道如何進行一些更改以使其快速Cython代碼不是我推薦給臨時用戶的東西。
是的,Python很慢,比C慢大約一百倍。你可以使用xrange
而不是range來獲得一個小的加速,但xrange
它沒關系。
最終你做錯的是你用普通的Python做到這一點,而不是使用像Numpy或Psyco這樣的優化庫。
Java附帶了一個jit編譯器,在你只是處理數字時會產生很大的不同。
通過使用替換復雜的測試,您可以使Python的速度提高兩倍
if i % k == 0: isp = False
您也可以比isp = False之后添加一個中斷快八倍(對於n = 10000)。
另外,幫自己一個忙,跳過偶數(加一到nps開始包括2)。
最后,你只需要k來達到sqrt(i)。
當然,如果您在Java中進行相同的更改,它仍然比優化的Python快10倍。
男孩,當你說這是一個天真的實施,你肯定不是在開玩笑!
但是,當將JIT編譯的優化機器代碼與解釋語言進行比較時,性能上的一到兩個數量級的差異並不出乎意料。 在Java VM上運行的替代Python實現(如Jython)可能會更快地執行此任務; 你可以給它一個旋轉。 Cython,允許您向Python添加靜態類型並在某些情況下獲得類似C的性能,也可能值得研究。
即使在考慮標准的Python解釋器CPython時,問題是:Python是否足夠快以完成手頭的任務? 您是否節省了使用Python等動態語言編寫代碼的時間,以彌補運行它所花費的額外時間? 如果你不得不用Java編寫一個給定的程序,那么看起來太值得做的事情值得嗎?
例如,考慮在現代計算機上運行的Python程序與在具有10年歷史的計算機上運行的Java程序一樣快。 你十年前的電腦對很多東西都足夠快,不是嗎?
Python確實具有許多功能,使其非常適合數字工作。 這些包括支持無限數量的數字的整數類型,具有無限精度的十進制類型,以及專門用於計算的名為NumPy的可選庫。 然而,執行速度通常不是其聲名鵲起的主要原因之一。 它擅長的地方在於讓計算機以最小的認知摩擦做你想做的事。
如果你想快速做到這一點,Python可能不是前進的方法,但你可以加快一點。 首先,你使用相當慢的方法來測試可分性。 Modulo更快。 您也可以在檢測到匹配后立即停止內循環(使用k)。 我會做這樣的事情:
nps = 0
for i in range(1, n+1):
if all(i % k for k in range(2, i)): # i.e. if divisible by none of them
nps += 1
這讓我從25秒減少到1.5秒。 使用xrange將其降低到0.9秒。
您可以通過保留已經找到的素數列表來進一步加速它,並且只測試那些,而不是每個數字直到i(如果我不能被2整除,它將不能被4,6整除) ,8 ......)。
你為什么不發布關於內存使用的東西 - 而不僅僅是速度? 試圖在tomcat上獲得一個簡單的servlet在我的服務器上浪費了3GB。
你對這些例子做了什么並不是很好。 你需要使用numpy。 用while循環替換/ range,從而避免創建列表。
最后,python非常適合數字運算,至少是以正確的方式執行它的人,並且知道Eratosthenes的Sieve是什么,或mod操作是什么。
你可以對這個算法做很多事情來加速它,但是大多數它們也會加速Java版本。 其中一些將比Java更加加速Python,所以它們值得測試。
這里只是一些變化,可以在我的系統上將其從11.4加速到2.8秒:
nps = 0
for i in range(1,n+1):
isp = True
for k in range(2,i):
isp = isp and (i % k != 0)
if isp: nps = nps + 1
print nps
具有諷刺意味的是,Python是一種非常適合開發算法的語言。 即使是這樣的修改算法:
# See Thomas K for use of all(), many posters for sqrt optimization
nps = 0
for i in xrange(1, n+1):
if all(i % k for k in xrange(2, 1 + int(i ** 0.5))):
nps += 1
大約在一秒鍾內運行。 像這樣的代碼:
def eras(n):
last = n + 1
sieve = [0,0] + range(2, last)
sqn = int(round(n ** 0.5))
it = (i for i in xrange(2, sqn + 1) if sieve[i])
for i in it:
sieve[i*i:last:i] = [0] * (n//i - i + 1)
return filter(None, sieve)
還是更快。 或試試這些 。
問題是,python通常足夠快以設計您的解決方案。 如果它的生產速度不夠快,可以使用numpy或Jython來提高性能。 或者將其移動到編譯語言,隨身攜帶在python中學習的算法觀察。
是的,Python是您將遇到的最慢的實用語言之一。 While
循環比for i in xrange()
循環快一些,但最終Python總是比其他任何東西慢得多。
Python有它的位置:原型理論和思想,或者在任何快速生成代碼的能力比代碼性能更重要的情況下。
Python是一種腳本語言。 不是編程語言。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.