C 怎么读取Cpp文件_python之调用C加速计算(一)
一、前言
python語言是目前比較火的語言,很容易上手,對數據處理也比較友好,可以用幾行代碼就能進行一些簡單的數據處理工作。但是對于稍微大型的數值計算,或者一些涉及到大量循環的數值計算python的計算速度有點讓人失望。
即使是使用numpy對算法算法進行優化,能提升的空間都非常有限了,當然網上有一行代碼就提升100倍這種帖子,就是使用numba,但用了這個之后感受并不是太好,對于一個簡單循環或許可以加速,對于復雜的循環效果也許并不好(可能是我不會使用,對numba沒有太深入的研究)。
想要提升python的數值計算,目前看到比較好的解決方案就是用C給python寫計算的lib,如果僅是數值計算,其實涉及到的C++語言的知識并不太多,基本就是for循環,if判斷,以及一些STL庫,個人覺得性價比還是挺高的。
python調用C進行數值計算最大難點就是如何進行數據交換,也就是如何把輸入參數的指針傳遞給C,以及如何返回輸出參數指針給python。有大神早就將這些都封裝好了,就是使用pybind11。
二、pybind11簡介
pybind11定義了一些python數據結構和C++的對應關系,使得數據交換變得非常簡單,接下來簡單介紹數值計算主要用的一些pybind11用到的知識。
1)獲取pybind11
想要用到pybind11的功能當然得有pybind11的代碼,網上可以直接下載到pybind11-master.rar文件,只需要下載到本地,然后解壓就可以了。
2)cpp基本結構介紹
接下來直接就寫代碼吧,用Visual Studio創建個cpp文件(不會安裝vs的可以自行百度安裝一下,這個不是本文重點就不介紹了),本文使用的是VS2015。
首先include一些頭文件。Emm…反正感覺會用的先include進來應該沒錯了。
?
接下來include本文的重點也就是pybind11的頭文件。
然后修改下命名空間,只是為了書寫方便和import pandas as pd一個道理。
接下來要定義模塊的入口。
其中calc就是在python調用時候的模塊名稱,m是在C++文件中的模塊實例,可以通過m.doc()給這個模塊寫下文檔,一般就是這個模塊是干什么的,可以在python環境help該模塊名來查看,接下來通過m.def定義函數名,第一個參數是字符串,是在python調用時候的函數名,第二個參數是C++文件中的對應函數名稱,三個參數是該函數的介紹。
以上寫完之后就可以愉快的開始寫函數了,整個cpp的結構大致就是下面這樣了。
分別是頭文件,命名空間,函數區,函數導出區。本文也按照常規套路hello world一把。
3)編譯cpp成pyd文件
接下來需要通過編譯器將cpp文件編譯成python的pyd文件,首先要找到vs的x64本機工具命令提示符,本文以vs2015為例,在開始菜單直接搜索vs2015會出來以下選項(前提是你已經裝了正確安裝vs)。然后選擇VS2014 x64 本機工具命令提示符(注意一定要是x64的)。
打開之后通過cd /d 路徑,這個命令將路徑切換到cpp所在路徑。然后輸入以下命令
其中calc.cpp是待編譯的cpp文件,路徑1需要替換成前文獲取到的pybind11-master文件夾下的include文件夾的所在路徑,路徑2需要替換成python安裝路徑的include文件夾的所在路徑,路徑3替換成python安裝路徑下的libs文件夾的所在路徑,calc.pyd是生成的pyd名稱,需要和cpp中模塊名一致。
輸入上述代碼回車之后,編譯成功會有如下信息打印,同時在cpp所在路徑會產生四個文件,我們需要的只是后綴為.pyd的文件。
4)python執行
之后打開前文編譯所有的python環境來進行測試,注意這里編譯的pyd不同python版本是不能共用的(即python3.5編譯出來的文件,python3.6并不能調用),有時同樣是3.6編譯出來的也不能使用,這個還沒研究是怎么一個兼容關系。
python調用pyd文件,一種簡單的方法是用sys模塊直接加入pyd文件所在路徑,就可以直接調用,或者也可拷貝pyd文件到python能找到的路徑下,比如python的安裝路徑下。然后執行寫好的函數就有如下結果。
?
好的到此咱們已經完成了整個從下載所需文件,到cpp文件書寫,然后對cpp進行編譯,最后在python執行的全過程。
5)pybind11數據結構介紹
在數值計算用的最多的結構是array_t<>,可以是array_t,array_t或者array_t,當然用的最多的肯定是array_t了。
以上是一個兩個矩陣輸入,同時輸出函數也是一個矩陣的函數聲明,在array_t里面封裝了數據矩陣的指針以及數據矩陣的大小。
通過以上方法獲得了兩個數據指針ptr1和ptr2,以及第一個矩陣的大小。
接下來定義輸出參數,申請內存并獲得數據指針。
?
上述簡單介紹了py::array_t的基本用法,pybind11還定義了py::list等等數據封裝內容這些可以自行查看pybind11文檔或者相應的pdf文檔。
pybind11文檔https://pybind11.readthedocs.io/en/master/intro.html6)讀取和數據存儲
為了方便代碼書寫,本文會獲得的指針進行宏定義,使得代碼更有可讀性,這里就涉及到了數據存儲方式的問題。
這里示范的書寫方法是默認輸入矩陣和輸出矩陣都是按行存儲,這一點特別需要注意,其中numpy里的array數據默認是按行存儲,也就是不管何種存儲方式,只要對array數據進行copy操作之后,返回的數據都是按行存儲。所以一般用array數據矩陣作為C函數輸入時,進行copy操作是比較穩妥的方式,但是當矩陣較大時,進行矩陣的深拷貝的速度往往會很慢,甚至可能大于計算所需要的時間。
python常用的庫還有pandas,DataFrame數據的存儲默認是按列存儲,也就是從通過某個dataframe數據.values的方法獲得的array數據矩陣,默認是按列存儲。
那么如何知道一個array數據矩陣是按行存儲還是按列存儲呢,array數據有相應參數進行說明。
array數據矩陣的flags屬性下,有f_contiguous和c_contiguous這兩個布爾類型的屬性,當c_contiguous為真時,矩陣是按行存儲,當f_contiguous為真時,矩陣是按列存儲。其中f好像是表示Fortran語言,這種語言主要用來進行科學計算,是按列存儲,據網上說超大型的數值計算都是用這種語言。c表示C語言,c語言是按行存儲。平時用的比較多的數值計算的還有matlab,matlab是按列存儲的。貌似對于截面數據來說,進行時間序列上的操作確實是按列存儲比較占優。
7)C++函數結構
對于一個常用的輸入為矩陣,輸出也為矩陣的函數來說,大致的函數結構如下。
分為輸入參數獲取,輸出參數定義,數據矩陣宏定義,運算邏輯,以及結果的返回。基本上的函數書寫,僅需要關注運算邏輯如何書寫,其他幾部分內容都是相對固定的。
三、后語
前文內容詳細介紹了如何構建整個cpp的內容結構,C++函數的內容結構,以及pybind11最常用的py::array_t的介紹。在給python寫調用函數時,只需要專注于C++函數內容的運算邏輯區的代碼即可,可以說已經非常簡單了。
文中提到的pybind11-master.rar,pybind11用戶pdf文檔,關注本公眾號回復【加速一】就可以獲得。
這個只是本系列的第一篇介紹,后續有使得你運行速度更快的詳細介紹,期待你的運算速度可以起飛~~~~
總結
以上是生活随笔為你收集整理的C 怎么读取Cpp文件_python之调用C加速计算(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 全球首款!TCL华星UHD R1000电
- 下一篇: 手机上怎么办理信用卡 手机上怎样申请信用