ãŸããã
ç§ãä»äºããã幞éãªåéã¯ãå¿èã®èšç®é»æ°ççåŠãšåŒã°ããŠããŸã ã å¿è掻åã®ççåŠã¯ãåã ã®å¿ç现èã®ã¬ãã«ã§çºçããé»æ°çããã»ã¹ã«ãã£ãŠæ±ºå®ãããŸãã ãããã®é»æ°ããã»ã¹ã¯ã枬å®ã容æãªé»çãäœãåºããŸãã ããã«ãéé»æ°ã®æ°åŠçã¢ãã«ã®æ çµã¿ã§éåžžã«ãã説æãããŠããŸãã ããã§ãå¿èã®åããå³å¯ã«æ°åŠçã«èšè¿°ãããŠããŒã¯ãªæ©äŒãçããŸããããã¯ãå€ãã®å¿èç ã®æ²»çæ¹æ³ãæ¹åããããšãæå³ããŸãã
ãã®åéã§ã®ä»äºäžã«ãããŸããŸãªã³ã³ãã¥ãŒãã£ã³ã°ãã¯ãããžãŒã䜿çšããçµéšãç©ãã§ããŸããã ãã®åºçç©ã®äžéšãšããŠã ãã§ãªããç§ã«ãšã£ãŠèå³æ·±ããããããªãããã€ãã®è³ªåã«çããããšããŸãã
Scientific Pythonã«ã€ããŠç°¡åã«
倧åŠã®æåã®ã³ãŒã¹ããå§ããŠãæ°å€ã¢ã«ãŽãªãºã ã®è¿ éãªéçºã®ããã®çæ³çãªããŒã«ãèŠã€ããããšããŸããã ããã€ãã®ççŽã«èšã£ãŠéççãªæè¡ãæšãŠãå ŽåãC ++ãšMATLABã®éãèµ°ããŸããã ããã¯ãScientific Python [1]ãçºèŠãããŸã§ç¶ããŸããã
Scientific Pythonã¯ãç§åŠèšç®ãšç§åŠèŠèŠåã®ããã®Pythonã©ã€ãã©ãªã®ã³ã¬ã¯ã·ã§ã³ã§ãã ç§ã®ä»äºã§ã¯ãç§ã®ããŒãºã®çŽ90ïŒ ãã«ããŒãã以äžã®ããã±ãŒãžã䜿çšããŸãã
åœ¹è· | 説æ |
---|---|
ãã³ã㌠| åºæ¬çãªã©ã€ãã©ãªã®1ã€ã䜿çšãããšãMATLABã¹ã¿ã€ã«ã®åäžãªããžã§ã¯ããšããŠå€æ¬¡å
é
åãæäœã§ããŸãã
ç·åœ¢ä»£æ°ãããŒãªãšå€æãä¹±æ°ã®åŠçãªã©ã®åºæ¬æé ã®å®è£ ãå«ãŸããŸãã |
ã·ã㌠| NumPyæ¡åŒµã«ã¯ãæé©åã¡ãœããã®å®è£ ãæŸé»ããããããªãã¯ã¹ã®åŠçãçµ±èšãªã©ãå«ãŸããŸãã |
ãã³ã | å€æ¬¡å ããŒã¿ãšçµ±èšã®åæçšã®åå¥ã®ããã±ãŒãžã |
ã·ã³ã㌠| ã·ã³ããªãã¯æ°åŠã®ããã±ãŒãžã |
ããããããªã | äºæ¬¡å ã°ã©ãã£ãã¯ã¹ã |
ãã€ã2 | VTKã«åºã¥ã3次å ã°ã©ãã£ãã¯ã¹ã |
ã¹ãã€ã㌠| æ°åŠã¢ã«ãŽãªãºã ã®ã€ã³ã¿ã©ã¯ãã£ããªéçºã«äŸ¿å©ãªIDEã |
Pythonã§ã®äžŠåèšç®ã®åé¡ã
ãã®èšäºã®äžŠåã³ã³ãã¥ãŒãã£ã³ã°ã«ãã£ãŠãSMP-å ±æã¡ã¢ãªã䜿çšãã察称åãã«ãããã»ãã·ã³ã°ã«ã€ããŠç解ã§ããŸãã CUDAãšå ±æã¡ã¢ãªãåããã·ã¹ãã ã®äœ¿çšã¯æ±ããŸããïŒMPIæšæºãæããã䜿çšãããŸãïŒã
åé¡ã¯GILã§ãã GILïŒGlobal Interpreter LockïŒã¯ãè€æ°ã®ã¹ã¬ãããåããã€ãã³ãŒããå®è¡ããã®ãé²ããã¥ãŒããã¯ã¹ã§ãã æ®å¿µãªãããCPythonã®ã¡ã¢ãªç®¡çã·ã¹ãã ã¯ã¹ã¬ããã»ãŒãã§ã¯ãªãããããã®ããã¯ãå¿ èŠã§ãã ã¯ããGILã¯Pythonã®åé¡ã§ã¯ãªããCPythonã€ã³ã¿ãŒããªã¿ãŒã®å®è£ ã®åé¡ã§ãã ããããæ®å¿µãªãããPythonã®æ®ãã®å®è£ ã¯ãé«éãªæ°å€ã¢ã«ãŽãªãºã ã®äœæã«ã¯ããŸãé©ããŠããŸããã
幞ããªããšã«ãçŸåšGILã®åé¡ã解決ããæ¹æ³ã¯ããã€ããããŸãã ããããèæ ®ããŠãã ããã
ãã¹ãã¿ã¹ã¯
Nãã¯ãã«ã®2ã€ã®ã»ãããäžããããŸãïŒ3次å ãŠãŒã¯ãªãã空éã®P = {p 1 ãp 2 ã...ãp N }ããã³Q = {q 1 ãq 2 ã...ãq N } ã 次å N x Nã®è¡åRãäœæããå¿ èŠããããŸããåèŠçŽ r iãjã¯æ¬¡ã®åŒã§èšç®ãããŸãã
倧ãŸãã«èšãã°ããã¹ãŠã®ãã¯ãã«éã®ãã¢ã¯ã€ãºè·é¢ã䜿çšããŠè¡åãèšç®ããå¿ èŠããããŸãã ãã®è¡åã¯ãå®éã®èšç®ã§ãã䜿çšãããŸããããšãã°ãRBFè£éãç©åæ¹çšåŒã®æ¹æ³ã«ããç·æ¥ç¶æ ã§ã®å·®åã®è§£æ±ºãªã©ã§ãã
ãã¹ãå®éšã§ã¯ããã¯ãã«ã®æ°ã¯N = 5000ã§ããèšç®ã«ã¯ã4ã³ã¢ã®ããã»ããµã䜿çšãããŸããã çµæã¯ã10ã®éå§ã®å¹³åæéã«ãã£ãŠååŸãããŸãã
ãã¹ãã¿ã¹ã¯ã®å®å šãªå®è£ ã¯ãGitHubã§èŠãããšãã§ããŸã[2]ã
ã@chersayaãããã®ã³ã¡ã³ãã®æ£ããçºèšã ããã§ã¯ããã®ãã¹ãã±ãŒã¹ãäŸãšããŠäœ¿çšããŸãã ãã¢ã¯ã€ãºè·é¢ãæ¬åœã«èšç®ããå¿ èŠãããå Žåã¯ãscipy.spatial.distance.cdisté¢æ°ã䜿çšããããšããå§ãããŸãã
C ++䞊åå®è£
Pythonã§ã®äžŠåèšç®ã®å¹çãæ¯èŒããããã«ããã®ã¿ã¹ã¯ãC ++ã§å®è£ ããŸããã äž»ãªæ©èœã³ãŒãã¯æ¬¡ã®ãšããã§ãã
ãŠãããã»ããµã®å®è£ ïŒ
//! Single thread matrix R calculation void spGetR(vector<Vector3D> & p, vector<Vector3D> & q, MatrixMN & R) { for (int i = 0; i < p.size(); i++) { Vector3D & a = p[i]; for (int j = 0; j < q.size(); j++) { Vector3D & b = q[j]; Vector3D r = b - a; R(i, j) = 1 / (1 + sqrt(r * r)); } } }
ãã«ãããã»ããµã®å®è£ ïŒ
//! OpenMP matrix R calculations void mpGetR(vector<Vector3D> & p, vector<Vector3D> & q, MatrixMN & R) { #pragma omp parallel for for (int i = 0; i < p.size(); i++) { Vector3D & a = p[i]; for (int j = 0; j < q.size(); j++) { Vector3D & b = q[j]; Vector3D r = b - a; R(i, j) = 1 / (1 + sqrt(r * r)); } } }
ããã§é¢çœãã®ã¯äœã§ããïŒ ãŸããæåã«ãå¥ã®Vector3Dã¯ã©ã¹ã䜿çšããŠã3次å 空éã§ãã¯ãã«ãè¡šããŸããã ãã®ã¯ã©ã¹ã®ãªãŒããŒããŒãæŒç®åã*ãã«ã¯ãã¹ã«ã©ãŒç©ã®æå³ããããŸãã ãã¯ãã«ã®ã»ãããè¡šãããã«ãstd :: vectorã䜿çšããŸããã 䞊åã³ã³ãã¥ãŒãã£ã³ã°ã§ã¯ãOpenMPãã¯ãããžãŒã䜿çšãããŸããã ã¢ã«ãŽãªãºã ã䞊ååããã«ã¯ããïŒpragma omp parallel forããã£ã¬ã¯ãã£ãã䜿çšããŸãã
çµæïŒ
ãŠãããã»ããµC ++ | 224ããªç§ |
ãã«ãããã»ããµC ++ | 65ããªç§ |
Pythonã§ã®äžŠåå®è£
1.çŽç²ãªPythonã§ã®åçŽãªå®è£
ãã®ãã¹ãã§ã¯ãç¹å¥ãªããã±ãŒãžã䜿çšããã«ãçŽç²ãªPythonã§ã¿ã¹ã¯ãã©ãã ã解決ããããã確èªãããã£ãã®ã§ãã
ãœãªã¥ãŒã·ã§ã³ã³ãŒãïŒ
def sppyGetR(p, q): R = np.empty((p.shape[0], q.shape[1])) nP = p.shape[0] nQ = q.shape[1] for i in xrange(nP): for j in xrange(nQ): rx = p[i, 0] - q[0, j] ry = p[i, 1] - q[1, j] rz = p[i, 2] - q[2, j] R[i, j] = 1 / (1 + sqrt(rx * rx + ry * ry + rz * rz)) return R
ããã§ã p ã qã¯ã次å ïŒNã3ïŒããã³ïŒ3ãNïŒã®é åã®NumPy圢åŒã®å ¥åããŒã¿ã§ãã ãããŠãè¡åRã®èŠçŽ ãèšç®ããæ£çŽãªPythonã«ãŒããç»å ŽããŸãã
çµæïŒ
ãŠãããã»ããµPython | 57 386ããªç§ |
2ãŠãããã»ããµNumPy
äžè¬ã«ãNumPyã䜿çšããŠPythonã§ã³ã³ãã¥ãŒãã£ã³ã°ããå Žåã䞊ååŠçã«ã€ããŠãŸã£ããèããå¿ èŠããªãå ŽåããããŸãã ãããã£ãŠãããšãã°ã2ã€ã®è¡åã«NumPyãä¹ç®ããæé ã¯ãC ++ïŒMKLãŸãã¯ATLASïŒã®äœã¬ãã«ã®é«æ§èœç·åœ¢ä»£æ°ã©ã€ãã©ãªã䜿çšããŠæçµçã«å®è¡ãããŸãã ããããæ®å¿µãªãããããã¯æãäžè¬çãªæäœã«ã®ã¿åœãŠã¯ãŸããäžè¬çãªã±ãŒã¹ã§ã¯æ©èœããŸããã æ®å¿µãªããããã¹ãã¿ã¹ã¯ã¯é 次å®è¡ãããŸãã
ãœãªã¥ãŒã·ã§ã³ã³ãŒãã¯æ¬¡ã®ãšããã§ãã
def spnpGetR(p, q): Rx = p[:, 0:1] - q[0:1] Ry = p[:, 1:2] - q[1:2] Rz = p[:, 2:3] - q[2:3] R = 1 / (1 + np.sqrt(Rx * Rx + Ry * Ry + Rz * Rz)) return R
ããã4è¡ã§ãµã€ã¯ã«ãªãïŒ ã ããç§ã¯NumPyã倧奜ãã§ãã
çµæïŒ
ãŠãããã»ããµNumPy | 973ããªç§ |
3ãã«ãããã»ããµNumPy
GILã®åé¡ã®è§£æ±ºçãšããŠãåŸæ¥ãè€æ°ã®å®è¡ã¹ã¬ããã®ä»£ããã«è€æ°ã®ç¬ç«ããå®è¡ããã»ã¹ã䜿çšããããšãææ¡ãããŠããŸãã ãã¹ãŠåé¡ãããŸããããåé¡ããããŸãã åããã»ã¹ã«ã¯ç¬ç«ããã¡ã¢ãªããããçµæã®ãããªãã¯ã¹ãåããã»ã¹ã«è»¢éããå¿ èŠããããŸãã ãã®åé¡ã解決ããããã«ãPythonãã«ãããã»ãã·ã³ã°ã¯RawArrayã¯ã©ã¹ãå°å ¥ããããã»ã¹éã§åäžã®ããŒã¿é åãåå²ããæ©èœãæäŸããŸãã RawArrayã®åºç€ã¯æ£ç¢ºã«ã¯ããããŸããã ãããã¯ã¡ã¢ãªã«ãããããããã¡ã€ã«ã®ããã§ãã
ãœãªã¥ãŒã·ã§ã³ã³ãŒãã¯æ¬¡ã®ãšããã§ãã
def mpnpGetR_worker(job): start, stop = job p = np.reshape(np.frombuffer(mp_share.p), (-1, 3)) q = np.reshape(np.frombuffer(mp_share.q), (3, -1)) R = np.reshape(np.frombuffer(mp_share.R), (p.shape[0], q.shape[1])) Rx = p[start:stop, 0:1] - q[0:1] Ry = p[start:stop, 1:2] - q[1:2] Rz = p[start:stop, 2:3] - q[2:3] R[start:stop, :] = 1 / (1 + np.sqrt(Rx * Rx + Ry * Ry + Rz * Rz)) def mpnpGetR(p, q): nP, nQ = p.shape[0], q.shape[1] sh_p = mp.RawArray(ctypes.c_double, p.ravel()) sh_q = mp.RawArray(ctypes.c_double, q.ravel()) sh_R = mp.RawArray(ctypes.c_double, nP * nQ) nCPU = 4 jobs = utils.generateJobs(nP, nCPU) pool = mp.Pool(processes=nCPU, initializer=mp_init, initargs=(sh_p, sh_q, sh_R)) pool.map(mpnpGetR_worker, jobs, chunksize=1) R = np.reshape(np.frombuffer(sh_R), (nP, nQ)) return R
å ¥åããŒã¿ãšåºåãããªãã¯ã¹çšã«åå²ãããé åãäœæããã³ã¢ã®æ°ã«å¿ããŠããã»ã¹ã®ããŒã«ãäœæããã¿ã¹ã¯ããµãã¿ã¹ã¯ã«åå²ãã䞊è¡ããŠè§£æ±ºããŸãã
çµæïŒ
ãã«ãããã»ããµnumpy | 795ããªç§ |
ããã§ãPythonã ãã䜿çšããŠç§ã«ç¥ãããŠãã䞊åããã°ã©ãã³ã°ã®ãœãªã¥ãŒã·ã§ã³ã¯çµäºããŸããã ããã«ãGILãåãé€ãã«ã¯ãC ++ã¬ãã«ãŸã§äžããå¿ èŠããããŸãã ããããããã¯èŠãç®ã»ã©æããªãã
4ã·ãã³
Cython [3]ã¯ãPythonã³ãŒãã«Cåœä»€ãåã蟌ãããšãã§ããPythonæ¡åŒµã§ãã ãããã£ãŠãPythonã³ãŒããååŸããããã€ãã®åœä»€ãè¿œå ããŠãããã©ãŒãã³ã¹ã®ããã«ããã¯ãå€§å¹ ã«ã¹ããŒãã¢ããã§ããŸãã Cythonã¢ãžã¥ãŒã«ã¯Cã³ãŒãã«å€æãããPythonã¢ãžã¥ãŒã«ã«ã³ã³ãã€ã«ãããŸãã Cythonã§åé¡ã解決ããããã®ã³ãŒãã¯æ¬¡ã®ãšããã§ãã
ãŠãããã»ããµCythonïŒ
@cython.boundscheck(False) @cython.wraparound(False) def spcyGetR(pp, pq): pR = np.empty((pp.shape[0], pq.shape[1])) cdef int i, j, k cdef int nP = pp.shape[0] cdef int nQ = pq.shape[1] cdef double[:, :] p = pp cdef double[:, :] q = pq cdef double[:, :] R = pR cdef double rx, ry, rz with nogil: for i in xrange(nP): for j in xrange(nQ): rx = p[i, 0] - q[0, j] ry = p[i, 1] - q[1, j] rz = p[i, 2] - q[2, j] R[i, j] = 1 / (1 + sqrt(rx * rx + ry * ry + rz * rz)) return R
ãã«ãããã»ããµCythonïŒ
@cython.boundscheck(False) @cython.wraparound(False) def mpcyGetR(pp, pq): pR = np.empty((pp.shape[0], pq.shape[1])) cdef int i, j, k cdef int nP = pp.shape[0] cdef int nQ = pq.shape[1] cdef double[:, :] p = pp cdef double[:, :] q = pq cdef double[:, :] R = pR cdef double rx, ry, rz with nogil, parallel(): for i in prange(nP, schedule='guided'): for j in xrange(nQ): rx = p[i, 0] - q[0, j] ry = p[i, 1] - q[1, j] rz = p[i, 2] - q[2, j] R[i, j] = 1 / (1 + sqrt(rx * rx + ry * ry + rz * rz)) return R
ãã®ã³ãŒããçŽç²ãªPythonå®è£ ãšæ¯èŒããå Žåã䜿çšããå€æ°ã®åãæå®ããã ãã§æžã¿ãŸããã GILã¯1è¡ã§ãªãªãŒã¹ãããŸãã 䞊åã«ãŒãã¯ãxrangeã®ä»£ããã«prangeã¹ããŒãã¡ã³ãã«ãã£ãŠç·šæãããŸãã ç§ã®æèŠã§ã¯ãããã¯éåžžã«ã·ã³ãã«ã§çŸããã§ãïŒ
çµæïŒ
ãŠãããã»ããµCython | 255ããªç§ |
ãã«ãããã»ããµCython | 75ããªç§ |
5 numba
Numba [4]ã¯ããªãæ°ããã©ã€ãã©ãªã§ã掻çºã«éçºãããŠããŸãã ããã§ã®ã¢ã€ãã¢ã¯ãCythonãšã»ãŒåãã§ããPythonã³ãŒãã§C ++ã¬ãã«ã«å°éããè©Šã¿ã§ãã ããããã¢ã€ãã¢ã¯ã¯ããã«ãšã¬ã¬ã³ãã«å®è£ ãããŠããŸãã
Numbaã¯ãããã°ã©ã ã®å®è¡äžã«çŽæ¥ã³ã³ãã€ã«ïŒJITã³ã³ãã€ã«ïŒã§ããLLVMã³ã³ãã€ã©ã«åºã¥ããŠããŸãã ããšãã°ãPythonã§ããã·ãŒãžã£ãã³ã³ãã€ã«ããã«ã¯ãjitã¢ãããŒã·ã§ã³ãè¿œå ããã ãã§ãã ããã«ã泚éã䜿çšãããšãå ¥å/åºåããŒã¿ã®ã¿ã€ããæå®ã§ãããããJITã³ã³ãã€ã«ã®å¹çãå€§å¹ ã«åäžããŸãã
ã¿ã¹ã¯ãå®è£ ããããã®ã³ãŒãã¯æ¬¡ã®ãšããã§ãã
ãŠãããã»ããµãã³ãïŒ
@jit(double[:, :](double[:, :], double[:, :])) def spnbGetR(p, q): nP = p.shape[0] nQ = q.shape[1] R = np.empty((nP, nQ)) for i in xrange(nP): for j in xrange(nQ): rx = p[i, 0] - q[0, j] ry = p[i, 1] - q[1, j] rz = p[i, 2] - q[2, j] R[i, j] = 1 / (1 + sqrt(rx * rx + ry * ry + rz * rz)) return R
ãã«ãããã»ããµNumbaïŒ
def makeWorker(): savethread = pythonapi.PyEval_SaveThread savethread.argtypes = [] savethread.restype = c_void_p restorethread = pythonapi.PyEval_RestoreThread restorethread.argtypes = [c_void_p] restorethread.restype = None def worker(p, q, R, job): threadstate = savethread() nQ = q.shape[1] for i in xrange(job[0], job[1]): for j in xrange(nQ): rx = p[i, 0] - q[0, j] ry = p[i, 1] - q[1, j] rz = p[i, 2] - q[2, j] R[i, j] = 1 / (1 + sqrt(rx * rx + ry * ry + rz * rz)) restorethread(threadstate) signature = void(double[:, :], double[:, :], double[:, :], int64[:]) worker_ext = jit(signature, nopython=True)(worker) return worker_ext def mpnbGetR(p, q): nP, nQ = p.shape[0], q.shape[1] R = np.empty((nP, nQ)) nCPU = utils.getCPUCount() jobs = utils.generateJobs(nP, nCPU) worker_ext = makeWorker() threads = [threading.Thread(target=worker_ext, args=(p, q, R, job)) for job in jobs] for thread in threads: thread.start() for thread in threads: thread.join() return R
çŽç²ãªPythonãšæ¯èŒããŠãNumbaã®ã·ã³ã°ã«ããã»ããµãœãªã¥ãŒã·ã§ã³ã«è¿œå ããã泚éã¯1ã€ã ãã§ãïŒ æ®å¿µãªããããã«ãããã»ããµããŒãžã§ã³ã¯ããã»ã©çŸãããããŸããã ã¹ã¬ããã®ããŒã«ãæŽçããGILãæåã§æäŸããå¿ èŠããããŸãã Numbaã®ä»¥åã®ãªãªãŒã¹ã§ã¯ãåäžã®åœä»€ã§äžŠåã«ãŒããå®è£ ããããšããŸããããåŸç¶ã®ãªãªãŒã¹ã®å®å®æ§ã®åé¡ã«ããããã®æ©èœã¯åé€ãããŸããã æéãçµã€ã«ã€ããŠããã®æ©äŒã¯ä¿®åŸ©ããããšç¢ºä¿¡ããŠããŸãã
å®è¡çµæïŒ
ãŠãããã»ããµãã³ã | 359ããªç§ |
ãã«ãããã»ããµnumba | 180ããªç§ |
çµè«
çµæã次ã®å³ã§èª¬æããŸãã
å³ 1.ãŠãããã»ããµã³ã³ãã¥ãŒãã£ã³ã°ã®çµæ
å³ 2.ãã«ãããã»ããµã³ã³ãã¥ãŒãã£ã³ã°ã®çµæ
Pythonã®æ°å€èšç®ã«é¢ããGILã®åé¡ã¯å®éã«å æãããŠããããã«æããŸãã ãããŸã§ã®ãšããã䞊åã³ã³ãã¥ãŒãã£ã³ã°ãã¯ãããžãŒãšããŠCythonããå§ãããŸãã ããããç§ã¯æ¬åœã«NumbaãããèŠãŸãã
åç §è³æ
[1]ç§åŠPythonïŒ scipy.org
[2]ãã¹ãã®å®å šãªãœãŒã¹ã³ãŒãïŒ github.com/alec-kalinin/open-nuance
[3] CythonïŒ cython.org
[4] NumbaïŒ numba.pydata.org
PSã³ã¡ã³ãã@chersayaãã¯ã䞊åã³ã³ãã¥ãŒãã£ã³ã°ã®å¥ã®æ¹æ³ãæ£ããææããŸããã ããã¯ãnumexprã©ã€ãã©ãªã®äœ¿çšã§ãã Numexprã¯ãCã§èšè¿°ãããç¬èªã®ä»®æ³ãã·ã³ãšç¬èªã®JITã³ã³ãã€ã©ã䜿çšããŸãã ããã«ããã圌ã¯åçŽãªæ°åŒãæååãšããŠååŸããã³ã³ãã€ã«ããŠãã°ããèšç®ããããšãã§ããŸãã
䜿çšäŸïŒ
import numpy as np import numexpr as ne a = np.arange(1e6) b = np.arange(1e6) result = ne.evaluate("sin(a) + arcsinh(a/b)")