導航:首頁 > 編程語言 > python微分很慢

python微分很慢

發布時間:2022-08-01 07:01:34

① 為啥我的python這么慢

Pythn是動態類型而不是靜態類型的,這意味著,在程序執行時,解釋器並不知道變數的類型。對C語言來說,編譯器在聲明變數的時候就知道其類型了;對Python來說,程序執行時只知道一個變數是某種Python對象。
對於下面的C代碼
int a = 1;

int b = 2;
int c = a + b;
編譯器始終知道a和b是整型,在執行相加運算時,流程如下:
把<int> 1賦值給a

把<int> 2賦值給b
調用binary_add<int, int>(a, b)
把結果賦值給c
實現同樣功能的Python代碼如下
a = 1

b = 2
c = a + b
解釋器只知道1和2是對象,但是並不知道這個對象的類型。所以解釋器必須檢查每個變數的PyObject_HEAD才能知道變數類型,然後執行對應的相加操作,最後要創建一個新的Python對象來保存返回值,大致流程如下:
把1賦值給a

設置a->PyObject_HEAD->typecode為整型

設置a->val = 1
把2賦值給b
設置a->PyObject_HEAD->typecode為整型

設置b->val = 2
調用binary_add<int, int>(a, b)
a->PyObject_HEAD獲取類型編碼

a是一個整型;值為a->val
b->PyObject_HEAD獲取類型編碼
b是一個整型,值為b->val
調用binary_add<int, int>(a->val, b->val)
結果為整型,存在result中
創建對象c
設c->PyObject_HEAD->typecode為整型

設置c->val為result
動態類型意味著任何操作都會涉及更多的步驟。這是Python對數值操作比C語言慢的主要原因
Python是解釋型語言

上面介紹了解釋型代碼和編譯型代碼的一個區別。智能的編譯器可以提前預見並優化重復或不需要的操作,這會帶來性能的提升。編譯器是一個大的話題,這里不會展開。

Python的對象模型會帶來低效的內存訪問

和C語言的整數對比時,我們指出了Python多了額外一層信息。現在來看看數組的情況。在Python中我們可以使用標准庫中提供的List對象;而在C語言中我們會使用基於緩沖區的數組。

最簡單的NumPy數組是圍繞C數據構建的Python對象,也就是說它有一個指向連續數據緩存區的指針。而Python的list具有指向連續的指針緩沖區的指針,這些指針每個都指向一個Python對象,結合上面的例子,這些Python對象是一個整數對象。這個結構像下面這樣

很容易看出,如果你正在執行按順序逐步完成數據的操作,numpy的內存布局比Python的內存布局更為高效,無論是存儲成本還是訪問的時間成本。

為什麼使用Python

鑒於Python天生的低效率,我們為什麼還要使用Python呢?種種理由大致可以歸結為:動態類型使得Python比C更容易使用。Python非常的靈活和寬容,這種靈活性可以有效地利用開發時間,並且在那些確實需要C和Fortran優化的場合,Python可以輕松鏈接到已編譯的庫中。這也是Python在科學社區的使用率不斷增長的原因。經提到了一些結論性的東西,下面我們用Python的一些工具來做一些驗證。

下面的實驗使用到了python, ipython, numpy,版本信息如下:

python:3.6.5

1.13.3
In [1]: import sys
In [2]: import numpy
In [3]: sys.version[:5]
Out[3]: '3.6.5'
In [4]: numpy.__version__
Out[4]: '1.13.3'
本次實驗使用機器64位的機器,如果是32位的機器話,下面提到的一些struct可能會有所不同。
整數
Python的整數使用起來非常簡單。
In [5]: x = 42
In [6]: print(x)
42
介面的簡單性掩蓋了底層的復雜。在之前的內容里有提到過Python整數的內存布局。現在我們使用Python內置的ctypes模塊來自省整數類型,前提是需要知道在C API中Python整數類型的定義。
在CPython中,變數x存儲在一個名為_longobject的struct中,源碼見Include/longintrepr.h
struct _longobject {
PyObject_VAR_HEAD
digit ob_digit[1];
}
其中PyObject_VAR_HEAD是一個宏,在Include/object.h中定義的結構如下
typedef struct {
PyObject ob_base;
Py_ssize_t ob_size;
} PyVarObject;
其中PyObject在Include/object.h中定義如下
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
}
其中_PyObject_HEAD_EXTRA是一個在Python版本中通常不使用的宏。
將上面的信息結合起來,可以得到下面的結構
struct _longobject {
long ob_refcnt;
PyTypeObject *ob_type;
size_t ob_size;
long ob_digit[1];
}
這裡面ob_refcnt變數是對象的引用計數,ob_type是指向包含該對象所有類型信息和方法定義的結構的指針,ob_digit保存實際的數值。
有了上面的知識,可以開始使用ctypes模塊來觀察時間的對象結構並提取上面的信息。
現在來用Python定義一個C的struct
In [7]: import ctypes
In [9]: class IntStruct(ctypes.Structure):
...: _fields_ = [
...: ("ob_refcnt", ctypes.c_long),
...: ("ob_type", ctypes.c_void_p),
...: ("ob_size", ctypes.c_ulong),
...: ("ob_digit", ctypes.c_long)
...: ]
...:
...: def __repr__(self):
...: return (
...: "IntStruct(ob_digit)={self.ob_digit}, refcount={self.ob_refcnt}"
...: ).format(self=self)
...:
現在用42來做實驗。在Python中,id方法會返回對象的內存地址:
In [10]: num = 42
In [11]: IntStruct.from_address(id(42))
Out[11]: IntStruct(ob_digit)=42, refcount=61
可以看到ob_digit指向了內存中的正確位置。但是這里只創建了一個對象,為什麼引用次數是61呢?
事實證明,這是一個性能優化,Python使用了很多小的整數,如果為每一個數字都創建一個PyObject,會消耗掉不少內存。出於這個考慮,Python將一些常用的數字做了單例實現,這樣每個數字在內存中只有一份拷貝。換句話說,如果在這個范圍內創建一個新的Python整數時,只是創建了一個對該數值對象的引用。
In [16]: x = 42
In [17]: y = 42
In [18]: id(x) == id(y)
Out[18]: True
上面的例子中,x和y都指向了同一個內存地址。在使用更大的數的時候,等式就不成立了
In [19]: x = 1234
In [20]: y = 1234
In [21]: id(x) == id(y)
Out[21]: False
Python解釋器啟動時候會創建很多的整數對象;可以看看這些對象的引用分布
%matplotlib osx
import matplotlib.pyplot as plt
import sys
plt.loglog(range(1000), [sys.getrefcount(i) for i in range(1000)])
plt.xlabel('integer value')
plt.ylabel('reference count')
Out[8]: Text(0,0.5,'reference count')
可以看到0被引用了數千次,一般情況下,引用的頻率隨著整數值的增加而減少。
再來看看ob_digit對應的值
In [8]: all(i == IntStruct.from_address(id(i)).ob_digit for i in range(256))
Out[8]: True
如果更細心一點,就可以發現,對於大於256的值,ob_digit就不能對應到正確的值了:在Objects/longobject.c中對數值有一些移位操作,這也是Python對一些大整數的處理方式。
比如
In [11]: 2 ** 100
Out[11]:
這個值顯然是超過了long類型的范圍了。
List類型
現在來看看一個更復雜的數據類型:List。同樣能在Include/listobject.h中找到List類型的struct結構:
typedef struct {
PyObject_VAR_HEAD
PyObject **ob_item;
Py_ssize_t allocated;
} PyListObject;
和之前一樣,得到有效的結構體如下:
typedef struct {
long ob_refcnt;
PyTypeObject *ob_type;
Py_ssize_t ob_size;
PyObject **ob_item;
long allocated;
} PyListObject;
其中PyObject **ob_item指向list的數據,ob_size指出list中數據的個數。
In [3]: class ListStruct(ctypes.Structure):
...: _fields_ = [("ob_refcnt", ctypes.c_long),
...: ("ob_type", ctypes.c_void_p),
...: ("ob_size", ctypes.c_ulong),
...: ("ob_item", ctypes.c_long), # PyObject** pointer cast t
...: o long
...: ("allocated", ctypes.c_ulong)]
...:
...: def __repr__(self):
...: return ("ListStruct(len={self.ob_size}, "
...: "refcount={self.ob_refcnt})").format(self=self)
試驗一下
In [8]: L = [1, 2, 3, 4]
In [9]: ListStruct.from_address(id(L))
Out[9]: ListStruct(len=4, refcount=1)
為確保得到的結果是正確的,對這個list增加幾個引用,看看會不會影響引用計數:
In [10]: tup = [L, L]
In [11]: ListStruct.from_address(id(L))
Out[11]: ListStruct(len=4, refcount=3)
使用ctypes可以創建由之前IntStruct對象組成的復合結構
In [20]: Lstruct = ListStruct.from_address(id(L))
In [21]: PtrArray = Lstruct.ob_size * ctypes.POINTER(IntStruct)
In [22]: L_values = PtrArray.from_address(Lstruct.ob_item)
看看每個元素的值
In [23]: [ptr[0] for ptr in L_values]
Out[23]:
[IntStruct(ob_digit=1, refcount=4705),
IntStruct(ob_digit=2, refcount=1102),
IntStruct(ob_digit=3, refcount=559),
IntStruct(ob_digit=4, refcount=726)]
NumPy的數組
同樣的,我們來看看numpy中的數組。其C-API定義的結構見numpy/core/include/numpy/ndarraytypes.h,這里用的numpy版本是1.13.3,不同版本的結構可能有所不同。
In [25]: np.__version__
Out[25]: '1.13.3'
現在用ctypes來創建一個numpy數組的結構吧。
In [31]: class NumpyStruct(ctypes.Structure):
...: _fields_ = [("ob_refcnt", ctypes.c_long),
...: ("ob_type", ctypes.c_void_p),
...: ("ob_data", ctypes.c_long), # char* pointer cast to long
...: ("ob_ndim", ctypes.c_int),
...: ("ob_shape", ctypes.c_voidp),
...: ("ob_strides", ctypes.c_voidp)]
...:
...: @property
...: def shape(self):
...: return tuple((self.ob_ndim * ctypes.c_int64).from_address(self.ob_shape))
...:
...: @property
...: def strides(self):
...: return tuple((self.ob_ndim * ctypes.c_int64).from_address(self.ob_strides))
...:
...: def __repr__(self):
...: return ("NumpyStruct(shape={self.shape}, "
...: "refcount={self.ob_refcnt})").format(self=self)
新建一個numpy數組試試In [32]: x = np.random.random((10, 20)) In [33]: xstruct = NumpyStruct.from_address(id(x)) In [34]: xstruct Out[34]: NumpyStruct(shape=(10, 20), refcount=1)
可以看到已經拿到了正確的shape。現在看看引用計數的情況
In [35]: L = [x, x, x]
In [36]: xstruct
Out[36]: NumpyStruct(shape=(10, 20), refcount=4)
現在可以看看裡面的數據了。
In [37]: x = np.arange(10)
In [38]: xstruct = NumpyStruct.from_address(id(x))
In [39]: size = np.prod(xstruct.shape)
In [40]: arraytype = size * ctypes.c_long
In [41]: data = arraytype.from_address(xstruct.ob_data)
In [42]: [d for d in data]
Out[42]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
上面的data變數存儲了Numpy數組中定義的連續的內存塊,可以改一下其中的值。
In [43]: x[4] = 555
In [44]: [d for d in data]
Out[44]: [0, 1, 2, 3, 555, 5, 6, 7, 8, 9]
上面的例子可以證明x和data指向了同一塊連續內存。
比較Python的list和numpy的ndarray的內部結構,明顯可以看出numpy的數據對於表示同類型的數據列表來說簡單的多。
結語
Python很慢,正如上面所說,一個重要原因是類型的間接定址,這也是Python對開發者友好的原因。同時,Python本身也提供了可用於破解Python對象的工具,通過使用這些工具可以解析CPython的一些內部行為。

② 終端運行Python過慢啊

PYTHON確實是要慢一點,不知道你環境怎樣,19秒也太慢了,我的老破電腦三五秒能響應。
小程序我一般用PERL,速度飛快。

③ 為什麼我的cmd調用python特別慢,大概有十幾秒時間

  1. 直接用滑鼠點python.exe運行怎麼樣呢

  2. 是不是環境變數太多太多了導致電腦尋找命令過慢

  3. 配置怎麼樣噢

④ python運行速度慢怎麼辦

yxhtest7772017-07-18

關注

分享

 697     2

python運行速度慢怎麼辦?6個Python性能優化技巧



Python是一門非常酷的語言,因為很少的Python代碼可以在短時間內做很多事情,並且,Python很容易就能支持多任務和多重處理。

Python的批評者聲稱Python性能低效、執行緩慢,但實際上並非如此:嘗試以下6個小技巧,可以加快Python應用程序。

關鍵代碼可以依賴於擴展包

Python使許多編程任務變得簡單,但是對於很關鍵的任務並不總是提供最好的性能。使用C、C++或者機器語言擴展包來執行關鍵任務能極大改善性能。這些包是依賴於平台的,也就是說,你必須使用特定的、與你使用的平台相關的包。簡而言之,該解決方案提供了一些應用程序的可移植性,以換取性能,您可以獲得只有通過直接向底層主機編程。

下面這些擴展包你可以考慮添加到你的個人擴展庫中:

Cython

PyInlne

PyPy

Pyrex

這些包有不同的作用和執行方式。例如,Pyrex 讓Python處理一些內存任務變得簡單高效;PyInline可以直接讓你在Python應用程序中使用C代碼,雖然內聯代碼被單獨編譯,但是如果你能高效的利用C代碼,它可以在同一個地方處理每一件事情。

使用關鍵字排序

有很多古老的Python代碼在執行時將花費額外的時間去創建一個自定義的排序函數。最好的排序方式是使用關鍵字和默認的sort()方法。

優化循環

每一種編程語言都強調循環語句的優化,Python也是一樣的。盡管你可以依賴於豐富的技術讓循環運行的更快,然而,開發者經常忽略的一個方法是避免在循環內部使用點拼接字元串。

使用新版本

任何一個在線上搜索Python資料的人都會發現無數關於Python版本遷移的信息。通常,Python每一個版本都針對之前的一個版本做了優化和改進,以讓Python運行的更快。限制因素是你喜歡的函數庫是否也針對Python的新版本做了改進。

當你使用了新的函數庫,獲得了Python的新版本,你需要保證代碼依然能夠運行,檢查應用,修正差異。然後,如果你僅僅是

⑤ Python 執行速度慢只是因為它是解釋型語言嗎

Python 不是解釋型語言,事實上也沒有「解釋型」語言這個分類。

Python 性能略有不佳的原因可能有幾個:

首先是 Python 希望自己是一個簡單和優雅的語言,需要性能的組件通常用 C 實現,沒有太多改進性能的動力。

然後 Python 具有垃圾回收和自動的內存管理功能,並且採用動態類型系統,會在運行時進行類型檢查,這會不可避免地略微影響性能,使其不如靜態類型(Java)或沒有垃圾回收(C/C++)的語言。

摘自維基網路:

⑥ 為什麼python多線程這么慢

差不多是這樣子。多線程目前僅用於網路多線程採集, 以及性能測試。

其它的語言也有類似的情況,線程本身的特點導致線程的適用范圍是受限的。只有CPU過剩,而其它的任務很慢,此時用線程才是有益的,可以很好平衡等待時間,提高並發性能。

線程的問題主要是線程的安全穩定性。線程無法強制中止,同時線程與主進程共享內存,可能會影響主進程的內存管理。

在python里線程出問題,可能會導致主進程崩潰。 雖然python里的線程是操作系統的真實線程。

那麼怎麼解決呢?通過我們用進程方式。子進程崩潰後,會完全的釋放所有的內存和錯誤狀態。所以進程更安全。 另外通過進程,python可以很好的繞過GIL,這個全局鎖問題。

但是進程也是有局限的。不要建立超過CPU總核數的進程,否則效率也不高。

簡單的總結一下。
當我們想實現多任務處理時,首先要想到使用multiprocessing, 但是如果覺著進程太笨重,那麼就要考慮使用線程。 如果多任務處理中需要處理的太多了,可以考慮多進程,每個進程再採用多線程。如果還處理不要,就要使用輪詢模式,比如使用poll event, twisted等方式。如果是GUI方式,則要通過事件機制,或者是消息機制處理,GUI使用單線程。

所以在python里線程不要盲目用, 也不要濫用。 但是線程不安全是事實。如果僅僅是做幾個後台任務,則可以考慮使用守護線程做。如果需要做一些危險操作,可能會崩潰的,就用子進程去做。 如果需要高度穩定性,同時並發數又不高的服務。則強烈建議用多進程的multiprocessing模塊實現。

linux或者是unix里,進程的使用代價沒有windows高。還是可以接受的。

⑦ python程序每次運行開始都比較慢

pycharm本來就是一個龐大的軟體,因為功能強大,用很方便,所以運行起來才慢,可以用來做大項目,
python自帶的idle就是一個輕量級的小工具,但是初學或做些小東西足夠了

⑧ 請問大佬們,為什麼我python運行程序特別慢啊,我這個程序怎麼改一下可以運行的更快呢

您好,茫茫人海之中,能為君排憂解難實屬朕的榮幸,在下拙見,若有錯誤,還望見諒!。展開全部
yxhtest7772017-07-18

關注

分享

697 2

python運行速度慢怎麼辦?6個Python性能優化技巧



Python是一門非常酷的語言,因為很少的Python代碼可以在短時間內做很多事情,並且,Python很容易就能支持多任務和多重處理。

Python的批評者聲稱Python性能低效、執行緩慢,但實際上並非如此:嘗試以下6個小技巧,可以加快Python應用程序。

關鍵代碼可以依賴於擴展包

Python使許多編程任務變得簡單,但是對於很關鍵的任務並不總是提供最好的性能。使用C、C++或者機器語言擴展包來執行關鍵任務能極大改善性能。這些包是依賴於平台的,也就是說,你必須使用特定的、與你使用的平台相關的包。簡而言之,該解決方案提供了一些應用程序的可移植性,以換取性能,您可以獲得只有通過直接向底層主機編程。

下面這些擴展包你可以考慮添加到你的個人擴展庫中:

Cython

PyInlne

PyPy

Pyrex

這些包有不同的作用和執行方式。例如,Pyrex 讓Python處理一些內存任務變得簡單高效;PyInline可以直接讓你在Python應用程序中使用C代碼,雖然內聯代碼被單獨編譯,但是如果你能高效的利用C代碼,它可以在同一個地方處理每一件事情。

使用關鍵字排序

有很多古老的Python代碼在執行時將花費額外的時間去創建一個自定義的排序函數。最好的排序方式是使用關鍵字和默認的sort()方法。

優化循環

每一種編程語言都強調循環語句的優化,Python也是一樣的。盡管你可以依賴於豐富的技術讓循環運行的更快,然而,開發者經常忽略的一個方法是避免在循環內部使用點拼接字元串。

使用新版本

任何一個在線上搜索Python資料的人都會發現無數關於Python版本遷移的信息。通常,Python每一個版本都針對之前的一個版本做了優化和改進,以讓Python運行的更快。限制因素是你喜歡的函數庫是否也針對Python的新版本做了改進。

當你使用了新的函數庫,獲得了Python的新版本,你需要保證代碼依然能夠運行,檢查應用,修正差異。然後,如果你僅僅是非常感謝您的耐心觀看,如有幫助請採納,祝生活愉快!謝謝!

⑨ 為什麼說Python運行慢

越高級的需要封裝的東西越多,用起來簡單,但是運行起來考慮了很多東西。他的庫是通用性的,運行起來就慢。

⑩ 使用python編程處理大量數據,效率太慢怎麼解決

既然存有上千萬個數據,為什麼不使用資料庫呢?
使用小型的sqlite資料庫,加上適當的索引、篩選,肯定能大大提高數據處理速度。
python也自身帶有處理sqlite資料庫的模塊,極其方便。

閱讀全文

與python微分很慢相關的資料

熱點內容
程序員年會打籃球 瀏覽:487
app的意見怎麼寫 瀏覽:294
企業app營銷應該如何做 瀏覽:583
app資源庫里圖標怎麼移動 瀏覽:539
雲優采安卓如何下載 瀏覽:655
主升黃金線源碼 瀏覽:518
如何在第二個手機上登錄理想app 瀏覽:945
個人主頁靜態網頁源碼 瀏覽:476
蘋果相冊文件夾怎麼恢復 瀏覽:859
中國雲通訊伺服器 瀏覽:457
小白學python看什麼書 瀏覽:957
只讀文件夾可以上傳文件 瀏覽:732
python不列印換行符 瀏覽:160
程序員那麼可愛公司吻戲 瀏覽:676
伺服器硬碟背板有什麼作用 瀏覽:556
壓縮機裝什麼網 瀏覽:677
單片機如何實現奇偶校驗 瀏覽:696
如何下載廣西防貧app 瀏覽:57
熊團子的解壓視頻 瀏覽:556
程序員坐太久腸胃不好 瀏覽:95