<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          5分鐘掌握在 Cython 中使用 C++

          共 1177字,需瀏覽 3分鐘

           ·

          2020-11-25 22:29

          1. 在Jupyter Notebook上使用C++

          • 首先加載Cython擴(kuò)展,使用魔術(shù)命令 %load_ext Cython
          • 接下來運(yùn)行Cython代碼,使用魔術(shù)命令 %%cython --cplus
          • 如果使用MacOS,使用魔術(shù)命令 %%cython --cplus --compile-args=-stdlib=libc++ --link-args=-stdlib=libc++,詳情請參考https://stackoverflow.com/questions/57367764/cant-import-cpplist-into-cython
          %load_ext?Cython
          %%cython?--cplus?--compile-args=-stdlib=libc++?--link-args=-stdlib=libc++
          #?注意:?使用?'cimport'?而不是?'import'
          from?libcpp.string?cimport?string
          cdef?string?s
          s?=?b"Hello?world!"
          print(s.decode("utf-8"))
          Hello?world!

          2. C++和Python類型的相互轉(zhuǎn)換

          3. 使用C++ STL

          3.1 使用C++ Vector

          可以替代Python的List。

          1. 初始化 - 通過Python的可迭代對象進(jìn)行初始化,需要聲明變量的嵌套類型

          2. 遍歷 - 讓index自增,通過while循環(huán)進(jìn)行遍歷

          3. 訪問 - 和Python一樣使用'[]'操作符對元素進(jìn)行訪問

          4. 追加 - 與Python list的append方法相似,使用C++ Vector的push_back方法追加元素

          最后,我們通過分別實(shí)現(xiàn)Python和C++版本的元素計數(shù)函數(shù)來對比性能,C++大約快240倍左右。

          注意: 為了公平起見,函數(shù)沒有傳入?yún)?shù),而是直接訪問函數(shù)體外部的變量。避免計入C++版本把Python列表轉(zhuǎn)換為C++ Vector的耗時。如果計入這部分耗時,C++的版本大約快4倍左右。

          %%cython?--cplus?--compile-args=-stdlib=libc++?--link-args=-stdlib=libc++
          from?libcpp.vector?cimport?vector
          #?通過Python對象初始化
          cdef?vector[int]?vec?=?range(5)
          #?遍歷
          cdef:
          ????int?i?=?0
          ????int?n?=?vec.size()
          print("開始遍歷...")
          while?i?????#?訪問
          ????print("\t第%d個位置的元素是%d"?%?(i,?vec[i]))
          ????i?+=?1
          print()
          #?追加
          vec.push_back(5)
          print("追加元素之后vec變?yōu)?,?vec)
          開始遍歷...
          ????????第0個位置的元素是0
          ????????第1個位置的元素是1
          ????????第2個位置的元素是2
          ????????第3個位置的元素是3
          ????????第4個位置的元素是4

          ????追加元素之后vec變?yōu)?[0,?1,?2,?3,?4,?5]
          arr?=?[x?//?100?for?x?in?range(1000)]
          target?=?6

          def?count_py():
          ????return?sum(1?for?x?in?arr?if?x?==?target)

          print("用Python來實(shí)現(xiàn),計算結(jié)果為%d!"%?count_py())
          用Python來實(shí)現(xiàn),計算結(jié)果為100!
          %%cython?--cplus?--compile-args=-stdlib=libc++?--link-args=-stdlib=libc++
          from?libcpp.vector?cimport?vector

          cdef:
          ????int?target?=?6
          ????vector[int]?v?=?[x?//?100?for?x?in?range(1000)]

          cdef?int?_count_cpp():
          ????cdef:
          ????????int?i?=?0
          ????????int?n?=?v.size()
          ????????int?ret?=?0
          ????while?i?????????if?v[i]?==?target:
          ????????????ret?+=?1
          ????????i?+=?1
          ????return?ret

          def?count_cpp():
          ????return?_count_cpp()

          print("用Cython(C++)來實(shí)現(xiàn),計算結(jié)果為%d!"%?count_cpp())
          用Cython(C++)來實(shí)現(xiàn),計算結(jié)果為100!
          print("對比Python版本與C++版本的性能...")
          %timeit?count_py()
          %timeit?count_cpp()
          對比Python版本與C++版本的性能...??
          30.8?μs?±?254?ns?per?loop?(mean?±?std.?dev.?of?7?runs,?10000?loops?each)??
          130?ns?±?6.4?ns?per?loop?(mean?±?std.?dev.?of?7?runs,?10000000?loops?each)

          3.2 使用C++ Unordered Map

          可以替代Python的Dict。

          1. 初始化 - 通過Python的可迭代對象進(jìn)行初始化,需要聲明變量的嵌套類型

          2. 遍歷 - 讓泛型指針自增,通過while循環(huán)進(jìn)行遍歷

          3. 訪問 - 使用deref(C++中的'*'操作符)來解引用,返回pair對象,通過.first來訪問key, .second來訪問Value

          4. 查找 - 使用unordered_map.count,返回1或0;或者用unordered_map.find,返回一個泛型指針,如果指針指向unordered_map.end,則表示未找到。

          5. 追加/修改 - unordered_map[key] = value。如果Key不存在,'[]'操作符會添加一個Key,并賦值為默認(rèn)的Value,比如0.0。所以,除非確定不會產(chǎn)生錯誤,否則在修改Key對應(yīng)的Value之前,要先判斷Key是否存在。這與Python的DecaultDict有點(diǎn)相似。

          最后,我們通過分別實(shí)現(xiàn)Python和C++版本的map條件求和函數(shù)來對比性能,C++大約快40倍左右。

          %%cython?--cplus?--compile-args=-stdlib=libc++?--link-args=-stdlib=libc++
          from?cython.operator?cimport?dereference?as?deref,?preincrement?as?inc
          from?libcpp.unordered_map?cimport?unordered_map
          #?通過Python對象初始化
          cdef?unordered_map[int,?float]?mymap?=?{i:?i/10?for?i?in?range(10)}
          #?遍歷
          cdef:
          ????unordered_map[int,?float].iterator?it?=?mymap.begin()
          ????unordered_map[int,?float].iterator?end?=?mymap.end()
          print("開始遍歷...")
          while?it?!=?end:
          ????#?訪問
          ????print("\tKey?is?%d,?Value?is?%.1f"?%?(deref(it).first,?deref(it).second))
          ????inc(it)
          print()

          #?查找
          print("開始查找...")
          if?mymap.count(-2):
          ????print("\t元素-2存在!")
          else:
          ????print("\t元素-2不存在!")

          it?=?mymap.find(3)
          if?it?!=?end:
          ????print("\t元素3存在,?它的值是%.1f!"?%?deref(it).second)
          else:
          ????print("\t元素3不存在!")
          print()

          #?修改
          print("修改元素...")
          if?mymap.count(3):
          ????mymap[3]?+=?1.0
          mymap[-2]??#?Key?-2不存在,會被添加一個默認(rèn)值0.0
          print("\tKey?is?3,?Value?is?%.1f"?%?mymap[3])
          print("\tKey?is?-2,?Value?is?%.1f"?%?mymap[-2])
          開始遍歷...
          ????????Key?is?0,?Value?is?0.0
          ????????Key?is?1,?Value?is?0.1
          ????????Key?is?2,?Value?is?0.2
          ????????Key?is?3,?Value?is?0.3
          ????????Key?is?4,?Value?is?0.4
          ????????Key?is?5,?Value?is?0.5
          ????????Key?is?6,?Value?is?0.6
          ????????Key?is?7,?Value?is?0.7
          ????????Key?is?8,?Value?is?0.8
          ????????Key?is?9,?Value?is?0.9

          開始查找...
          ????????元素-2不存在!
          ????????元素3存在,?它的值是0.3!

          修改元素...
          ????????Key?is?3,?Value?is?1.3
          ????????Key?is?-2,?Value?is?0.0
          my_map?=?{x:?x?for?x?in?range(100)}
          target?=?50

          def?sum_lt_py():
          ????return?sum(my_map[x]?for?x?in?my_map?if?x?
          print("用Python來實(shí)現(xiàn),計算結(jié)果為%d!"%?sum_lt_py())
          用Python來實(shí)現(xiàn),計算結(jié)果為1225!
          %%cython?--cplus?--compile-args=-stdlib=libc++?--link-args=-stdlib=libc++
          from?libcpp.unordered_map?cimport?unordered_map
          from?cython.operator?cimport?dereference?as?deref,?preincrement?as?inc

          cdef:
          ????unordered_map[int,?int]?my_map?=?{x:?x?for?x?in?range(100)}
          ????int?target?=?50

          cdef?_sum_lt_cpp():
          ????cdef:
          ????????unordered_map[int,?int].iterator?it?=?my_map.begin()
          ????????int?ret
          ????while?it?!=?my_map.end():
          ????????if?deref(it).first?????????????ret?+=?deref(it).second
          ????????inc(it)
          ????return?ret

          def?sum_lt_cpp():
          ????return?_sum_lt_cpp()
          print("用Cython(C++)來實(shí)現(xiàn),計算結(jié)果為%d!"%?sum_lt_cpp())
          用Cython(C++)來實(shí)現(xiàn),計算結(jié)果為1225!
          print("對比Python版本與C++版本的性能...")
          %timeit?sum_lt_py()
          %timeit?sum_lt_cpp()
          對比Python版本與C++版本的性能...
          ????6.63?μs?±?183?ns?per?loop?(mean?±?std.?dev.?of?7?runs,?100000?loops?each)
          ????157?ns?±?3.13?ns?per?loop?(mean?±?std.?dev.?of?7?runs,?10000000?loops?each)

          3.3 使用C++ Unordered Set

          可以替代Python的Set。

          1. 初始化 - 通過Python的可迭代對象進(jìn)行初始化,需要聲明變量的嵌套類型

          2. 遍歷 - 讓泛型指針自增,通過while循環(huán)進(jìn)行遍歷

          3. 訪問 - 使用deref(C++中的'*'操作符)來解引用

          4. 查找 - 使用unordered_set.count,返回1或0

          5. 追加 - 使用unordered_set.insert,如果元素已經(jīng)存在,則元素不會被追加

          6. 交集、并集、差集 - 據(jù)我所知,unordered_set的這些操作需要開發(fā)者自己去實(shí)現(xiàn),不如Python的Set用起來方便。

          最后,我們通過分別實(shí)現(xiàn)Python和C++版本的set求交集對比性能,C++大約慢20倍左右。詳情可參考https://stackoverflow.com/questions/54763112/how-to-improve-stdset-intersection-performance-in-c如果只是求兩個集合相同元素的數(shù)量,C++的性能大約是Python的6倍。不難推測,C++的unordered set查詢很快,但是創(chuàng)建很慢。

          %%cython?--cplus?--compile-args=-stdlib=libc++?--link-args=-stdlib=libc++
          from?cython.operator?cimport?dereference?as?deref,?preincrement?as?inc
          from?libcpp.unordered_set?cimport?unordered_set
          #?通過Python對象初始化
          cdef?unordered_set[int]?myset?=?{i?for?i?in?range(5)}
          #?遍歷
          cdef:
          ????unordered_set[int].iterator?it?=?myset.begin()
          ????unordered_set[int].iterator?end?=?myset.end()
          print("開始遍歷...")
          while?it?!=?end:
          ????#?訪問
          ????print("\tValue?is?%d"?%?deref(it))
          ????inc(it)
          print()

          #?查找
          print("開始查找...")
          if?myset.count(-2):
          ????print("\t元素-2存在!")
          else:
          ????print("\t元素-2不存在!")

          print()

          #?追加
          print("追加元素...")
          myset.insert(0)
          myset.insert(-1)

          print("\tMyset?is:?",?myset)
          開始遍歷...
          ????????Value?is?0
          ????????Value?is?1
          ????????Value?is?2
          ????????Value?is?3
          ????????Value?is?4

          開始查找...
          ????????元素-2不存在!

          追加元素...
          ????????Myset?is:??{0,?1,?2,?3,?4,?-1}
          myset1?=?{x?for?x?in?range(100)}
          myset2?=?{x?for?x?in?range(50,?60)}

          def?intersection_py():
          ????return?myset1?&?myset2

          print("用Python來實(shí)現(xiàn),計算結(jié)果為%s!"%?intersection_py())
          用Python來實(shí)現(xiàn),計算結(jié)果為{50,?51,?52,?53,?54,?55,?56,?57,?58,?59}!
          %%cython?--cplus?--compile-args=-stdlib=libc++?--link-args=-stdlib=libc++
          from?cython.operator?cimport?dereference?as?deref,?preincrement?as?inc
          from?libcpp.unordered_set?cimport?unordered_set

          cdef:
          ????unordered_set[int]?myset1?=?{x?for?x?in?range(100)}
          ????unordered_set[int]?myset2?=?{x?for?x?in?range(50,?60)}

          cdef?unordered_set[int]?_intersection_cpp():
          ????cdef:
          ????????unordered_set[int].iterator?it?=?myset1.begin()
          ????????unordered_set[int]?ret
          ????while?it?!=?myset1.end():
          ????????if?myset2.count(deref(it)):
          ????????????ret.insert(deref(it))
          ????????inc(it)
          ????return?ret

          def?intersection_cpp():
          ????return?_intersection_cpp()

          print("用Cython(C++)來實(shí)現(xiàn),計算結(jié)果為%s!"%?intersection_cpp())
          用Cython(C++)來實(shí)現(xiàn),計算結(jié)果為{50,?51,?52,?53,?54,?55,?56,?57,?58,?59}!
          print("對比Python版本與C++版本的性能...")
          %timeit?intersection_py()
          %timeit?intersection_cpp()
          對比Python版本與C++版本的性能...
          ????244?ns?±?2.96?ns?per?loop?(mean?±?std.?dev.?of?7?runs,?1000000?loops?each)
          ????4.87?μs?±?100?ns?per?loop?(mean?±?std.?dev.?of?7?runs,?100000?loops?each)
          myset1?=?{x?for?x?in?range(100)}
          myset2?=?{x?for?x?in?range(50,?60)}

          def?count_common_py():
          ????return?len(myset1?&?myset2)

          print("用Python(C++)來實(shí)現(xiàn),計算結(jié)果為%s!"%?count_common_py())
          用Python(C++)來實(shí)現(xiàn),計算結(jié)果為10!
          %%cython?--cplus?--compile-args=-stdlib=libc++?--link-args=-stdlib=libc++
          from?cython.operator?cimport?dereference?as?deref,?preincrement?as?inc
          from?libcpp.unordered_set?cimport?unordered_set

          cdef:
          ????unordered_set[int]?myset2?=?{x?for?x?in?range(100)}
          ????unordered_set[int]?myset1?=?{x?for?x?in?range(50,?60)}

          cdef?int?_count_common_cpp():
          ????if?myset1.size()?>?myset2.size():
          ????????myset1.swap(myset2)
          ????cdef:
          ????????unordered_set[int].iterator?it?=?myset1.begin()
          ????????int?ret?=?0
          ????while?it?!=?myset1.end():
          ????????if?myset2.count(deref(it)):
          ????????????ret?+=?1
          ????????inc(it)
          ????return?ret

          def?count_common_cpp():
          ????return?_count_common_cpp()

          print("用Cython(C++)來實(shí)現(xiàn),計算結(jié)果為%s!"%?count_common_cpp())
          用Cython(C++)來實(shí)現(xiàn),計算結(jié)果為10!
          print("對比Python版本與C++版本的性能...")
          %timeit?count_common_py()
          %timeit?count_common_cpp()
          對比Python版本與C++版本的性能...
          276?ns?±?3.18?ns?per?loop?(mean?±?std.?dev.?of?7?runs,?1000000?loops?each)
          46.2?ns?±?0.845?ns?per?loop?(mean?±?std.?dev.?of?7?runs,?10000000?loops?each)

          4. 傳值與傳引用

          Python的函數(shù),如果是容器類對象(如List, Set),傳遞的是引用,否則傳遞的是值(如int, float),如果不希望讓函數(shù)修改容器類對象,可以用deepcopy函數(shù)先拷貝一份容器的副本。但在C++里默認(rèn)都是傳值,如果需要傳引用需要聲明。

          以int型Vector為例,可以看到v1的值沒有被pass_value修改,但被pass_reference修改了。

          • 傳值使用 vector[int],pass_value函數(shù)只是傳入了v1的一份拷貝,所以函數(shù)無法修改v1

          • 傳引用使用 vector[int]&,pass_reference傳入了v1的引用,函數(shù)可以修改v1。

          下面的兩塊代碼可以展示Python與C++的不同之處。

          from?copy?import?deepcopy

          def?pass_value(v):
          ????v?=?deepcopy(v)
          ????v[0]?=?-1

          def?pass_reference(v):
          ????v[0]?=?-1

          v1?=?[0,?0,?0]
          print("v1的初始值是%s"?%?v1)
          pass_value(v1)
          print("執(zhí)行pass_value函數(shù)后,v1的值是%s"?%?v1)
          pass_reference(v1)
          print("執(zhí)行pass_reference函數(shù)后,v1的值是%s"?%?v1)
          v1的初始值是[0,?0,?0]
          執(zhí)行pass_value函數(shù)后,v1的值是[0,?0,?0]
          執(zhí)行pass_reference函數(shù)后,v1的值是[-1,?0,?0]
          %%cython?--cplus?--compile-args=-stdlib=libc++?--link-args=-stdlib=libc++

          from?libcpp.vector?cimport?vector

          cdef?void?pass_value(vector[int]?v):
          ????v[0]?=?-1

          cdef?void?pass_reference(vector[int]&?v):
          ????v[0]?=?-1

          cdef?vector[int]?v1?=?[0,?0,?0]
          print("v1的初始值是%s"?%?v1)
          pass_value(v1)
          print("執(zhí)行pass_value函數(shù)后,v1的值是%s"?%?v1)
          pass_reference(v1)
          print("執(zhí)行pass_reference函數(shù)后,v1的值是%s"?%?v1)
          v1的初始值是[0,?0,?0]
          執(zhí)行pass_value函數(shù)后,v1的值是[0,?0,?0]
          執(zhí)行pass_reference函數(shù)后,v1的值是[-1,?0,?0]

          5. 數(shù)字的范圍

          Python只有int型,而且int的范圍可以認(rèn)為是無限大的,只要沒有超出內(nèi)存限制,所以Python使用者一般不太關(guān)心數(shù)值溢出等問題。但使用C++的時候就需要謹(jǐn)慎,C++各個數(shù)字類型對應(yīng)的范圍如下:

          比如下面的函數(shù)就會造成錯誤。

          %%cython?--cplus?--compile-args=-stdlib=libc++?--link-args=-stdlib=libc++
          def?sum_py(num1,?num2):
          ????print("The?result?by?python?is:",?num1?+?num2)

          cdef?int?_sum_cpp(int?num1,?int?num2):??#?int?->?long?int
          ????return?num1?+?num2

          def?sum_cpp(num1,?num2):
          ????print("The?result?by?cpp?is:",?_sum_cpp(num1,?num2))
          sum_py(2**31-1,?1)
          sum_cpp(2**31-1,?1)
          The?result?by?python?is:?2147483648
          The?result?by?cpp?is:?-2147483648
          %%cython?--cplus?--compile-args=-stdlib=libc++?--link-args=-stdlib=libc++
          from?libcpp?cimport?bool

          def?lt_py(num1,?num2):
          ????print("The?result?by?python?is:",?num1?
          cdef?bool?_lt_cpp(float?num1,?float?num2):??#?float?->?double
          ????return?num1?>?num2

          def?lt_cpp(num1,?num2):
          ????print("The?result?by?cpp?is:",?_lt_cpp(num1,?num2))
          lt_py(1234567890.0,?1234567891.0)
          lt_cpp(1234567890.0,?1234567891.0)
          The?result?by?python?is:?True
          The?result?by?cpp?is:?False


          作者:李小文,先后從事過數(shù)據(jù)分析、數(shù)據(jù)挖掘工作,主要開發(fā)語言是Python,現(xiàn)任一家小型互聯(lián)網(wǎng)公司的算法工程師。

          Github:?https://github.com/tushushu


          推薦閱讀



          如何在 Matplotlib 中更改繪圖背景


          Python 解釋器 PyPy 7.3.3 版本發(fā)布!


          有人在代碼里下毒!慎用 pip install 命令


          點(diǎn)擊下方閱讀原文加入社區(qū)會員



          點(diǎn)贊鼓勵一下

          瀏覽 60
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩日逼网站 | 轻轻操免费视频 | 一级特黄色 | 免费黄色片视频网站 | 成人欧美A |