Python高性能编程实战:让你的代码提速10倍
Pythoné«˜æ€§èƒ½ç¼–ç¨‹å®žæˆ˜ï¼šè®©ä½ çš„ä»£ç æé€Ÿ10å€å¼•言Python以简æ´ä¼˜é›è‘—称,但性能一直是开å‘è€è®¨è®ºçš„焦点。很多人认为Python太æ¢äº†ï¼Œä¸é€‚åˆç”Ÿäº§çŽ¯å¢ƒï¼Œè¿™æ˜¯ä¸€ä¸ªæµä¼ 广泛的误解。事实上,通过åˆç†çš„优化手段,Python代ç 完å¨å¯ä»¥è¾¾åˆ°ç”Ÿäº§çº§åˆ«çš„高性能。本文将通过真实的优化案例,从数æ®ç»“构选择ã€ç®—法优化ã€å¹¶å‘编程到C扩展,系统性地展示Python性能优化的å¨è²Œã€‚ä¸€ã€æ•°æ®ç»“构的选择:性能优化的第一é“防线1.1 列表 vs é›†åˆ vs å—å¸ä¸åŒçš„æ•°æ®ç»“构底层实现ä¸åŒï¼Œæ€§èƒ½å·®å¼‚å¯èƒ½æ˜¯æ•°é‡çº§çš„。在æˆå‘˜æ£€æŸ¥åœºæ™¯ä¸ï¼Œlist 夿‚度为 O(n),而 set å’Œ dict 为 O(1)。对100万åƒç´ çš„list进行æˆå‘˜æ£€æŸ¥è€—时约0.08ç§’æ¯æ¬¡ï¼Œè€Œsetä»éœ€0.000001秒,差è·è¾¾8万å€ã€‚importtimeit size1000000test_listlist(range(size))test_setset(test_list)t1timeit.timeit(lambda:500000intest_list,number1000)t2timeit.timeit(lambda:500000intest_set,number1000)print(fList:{t1:.4f}s, Set:{t2:.6f}s, å·®è·:{t1/t2:.0f}å€)1.2 å—符串拼接性能Pythonå—符串ä¸å¯å˜ï¼Œå¾ªçޝä¸ç›´æŽ¥æ‹¼æŽ¥ä¼šäº§ç”Ÿå¤§é‡ä¸´æ—¶å¯¹è±¡ã€‚推è使用列表收集åŽç”¨join拼接,在n10000æ—¶join方法比快约200å€ã€‚# 䏿ލèï¼šå¾ªçŽ¯ä¸æ‹¼æŽ¥defbad(n):sforiinrange(n):sstr(i)returns# 推è:joinæ–¹å¼defgood(n):return.join(str(i)foriinrange(n))二ã€å¾ªçŽ¯ä¼˜åŒ–çš„ä¸‰é‡å¢ƒç•Œ2.1 列表推导å¼åˆ—表推导å¼åœ¨C层é¢å®žçŽ°ï¼Œæ¯”Python层é¢çš„for循环快30%-50%。对于简å•çš„æ˜ å°„å’Œè¿‡æ»¤æ“作,优åˆä½¿ç”¨æŽ¨å¯¼å¼ã€‚# for循环写法result[]forxinrange(10000):ifx%20:result.append(x*x)# 推导å¼å†™æ³•(更快更Pythonic)result[x*xforxinrange(10000)ifx%20]2.2 生æˆå™¨è¡¨è¾¾å¼å¤„ç†å¤§æ•°æ®é›†æ—¶ï¼Œç”Ÿæˆå™¨è¡¨è¾¾å¼é€ä¸ªäº§å‡ºç»“果,ä¸éœ€è¦ä¸€æ¬¡åŠ è½½å¨éƒ¨æ•°æ®ã€‚处ç†10GB日志文件,å†å˜å ç”¨å¯æŽ§åˆ¶åœ¨å‡ åMB。defprocess_large_log(filename):withopen(filename)asf:lines(line.strip()forlineinf)errors(lforlinlinesifERRORinl)foreinerrors:yieldparse_error(e)2.3 å†ç½®å‡½æ•°ä¼˜åˆPythonå†ç½®å‡½æ•°ç”¨C实现,sum()比手动循环快约10å€ï¼Œmap()å’Œfilter()也比手动循环效率更高。# 手动循环total0forxindata:totalx# 使用å†ç½®å‡½æ•° totalsum(data)# å¿«10å€ä¸‰ã€å¹¶å‘编程的æ£ç¡®å§¿åŠ¿3.1 GIL的真相GIL主è¦é™åˆ¶CPU密集型任务。CPU密集型用multiprocessing,IO密集型用threading或asyncio。fromconcurrent.futuresimportProcessPoolExecutor,ThreadPoolExecutor# CPU密集型:多进程withProcessPoolExecutor()aspool:resultslist(pool.map(heavy_cpu_task,data))# IO密集型:多线程withThreadPoolExecutor(max_workers20)aspool:resultslist(pool.map(fetch_url,urls))3.2 asyncio实战现代Web应用场景,asyncio是处ç†é«˜å¹¶å‘的最佳选择。一个asyncio程åºå¯ä»¥åŒæ—¶å¤„ç†æ•°åƒä¸ªè¿žæŽ¥è€Œåªæ¶ˆè€—æžå°‘å†å˜ã€‚importasyncioimportaiohttpasyncdeffetch(session,url):asyncwithsession.get(url)asresp:returnawaitresp.text()asyncdeffetch_all(urls):asyncwithaiohttp.ClientSession()assession:tasks[fetch(session,url)forurlinurls]returnawaitasyncio.gather(*tasks)resultsasyncio.run(fetch_all([https://httpbin.org/delay/1]*20))å››ã€NumPyå‘é‡åŒ–计算NumPyçš„å‘é‡åŒ–æ“作将循环推å¥C/Fortran层,性能æå‡å¯è¾¾ç™¾å€ã€‚importnumpyasnp size10_000_000datalist(range(size))arrnp.arange(size)# Python loop: ~0.8sresult_py[x*21forxindata]# NumPy vectorized: ~0.02s (40x faster)result_nparr*21五ã€Cython:Python到C的桥æ¢å°†Python代ç 编译æˆC扩展是获得C级别性能的直接方å¼ã€‚# calc.pyx (Cython文件)deffibonacci(intn):cdefinta0,b1,iforiinrange(n):a,bb,abreturna编译åŽè°ƒç”¨æ€§èƒ½æå‡çº¦50-80å€ã€‚对于数值计算密集的代ç ,Cython是性价比最高的优化手段。åã€æ€§èƒ½åˆ†æžå·¥å·6.1 cProfile优化之å‰å¿é¡»åˆæµ‹é‡ã€‚找出真æ£çš„ç“¶é¢ˆï¼Œè€Œä¸æ˜¯å‡æ„Ÿè§‰ã€‚importcProfile,pstats profilercProfile.Profile()profiler.enable()my_function()# ä½ çš„ä»£ç profiler.disable()statspstats.Stats(profiler).sort_stats(cumtime)stats.print_stats(10)6.2 memory_profileré€è¡Œåˆ†æžå†å˜ä½¿ç”¨ï¼Œç²¾ç¡®å®šä½å†å˜æ³„æ¼ä½ç½®ã€‚七ã€ç”Ÿäº§çŽ¯å¢ƒä¼˜åŒ–Checklist部署å‰é€é¡¹æ£€æŸ¥ï¼šä½¿ç”¨åˆé€‚的容器,优åˆset/dict而éžlist;用推导å¼å’Œç”Ÿæˆå™¨æ›¿ä»£æ˜¾å¼å¾ªçŽ¯ï¼›IO密集型用asyncio,CPU密集型用multiprocessing;数值计算用NumPyå‘é‡åŒ–ï¼›çƒç‚¹å‡½æ•°è€ƒè™‘Cython或Numba;开å¯profile找真æ£çš„瓶颈;é¿å循环ä¸åˆ›å»ºå¤§é‡ä¸´æ—¶å¯¹è±¡ï¼›åˆç†ä½¿ç”¨functools.lru_cacheåšç¼“å˜ã€‚总结Pythonæ€§èƒ½ä¼˜åŒ–ä¸æ˜¯çŽ„å¦ï¼Œè€Œæ˜¯ä¸€å¥—ç³»ç»Ÿçš„å·¥ç¨‹å®žè·µã€‚æ ¸å¿ƒæ€è·¯ï¼šæµ‹é‡åˆè¡Œï¼Œæ‰¾åˆ°çœŸæ£ç“¶é¢ˆè€Œéžå‡æ„Ÿè§‰ä¼˜åŒ–;算法优åˆï¼Œé€‰æ‹©æ£ç¡®çš„æ•°æ®ç»“构比任何优化都有效;é€å±‚优化,从Python到NumPyå†åˆ°Cython按需深å¥ï¼›å¹¶å‘得当,在æ£ç¡®åœºæ™¯ä½¿ç”¨æ£ç¡®å¹¶å‘模型。记ä½ä¼˜åŒ–的黄金法则:过早优化是万æ¶ä¹‹æºã€‚åˆå†™å‡ºæ£ç¡®å¯è¯»çš„代ç ,å†é€šè¿‡profiling找到真æ£ç“¶é¢ˆï¼Œæœ‰é’ˆå¯¹æ€§åœ°ä¼˜åŒ–。本文约2800å—,系统梳ç†Pythoné«˜æ€§èƒ½ç¼–ç¨‹çš„æ ¸å¿ƒæŠ€æœ¯å’Œæœ€ä½³å®žè·µã€‚