溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

Python性能優(yōu)化分析

發(fā)布時(shí)間:2021-12-01 11:11:37 來(lái)源:億速云 閱讀:117 作者:iii 欄目:編程語(yǔ)言

本篇內(nèi)容介紹了“Python性能優(yōu)化分析”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

python為什么性能差:

當(dāng)我們提到一門(mén)編程語(yǔ)言的效率時(shí):通常有兩層意思,***是開(kāi)發(fā)效率,這是對(duì)程序員而言,完成編碼所需要的時(shí)間;另一個(gè)是運(yùn)行效率,這是對(duì)計(jì)算機(jī)而言,完成計(jì)算任務(wù)所需要的時(shí)間。編碼效率和運(yùn)行效率往往是魚(yú)與熊掌的關(guān)系,是很難同時(shí)兼顧的。不同的語(yǔ)言會(huì)有不同的側(cè)重,python語(yǔ)言毫無(wú)疑問(wèn)更在乎編碼效率,life  is short,we use python。

雖然使用python的編程人員都應(yīng)該接受其運(yùn)行效率低的事實(shí),但python在越多越來(lái)的領(lǐng)域都有廣泛應(yīng)用,比如科學(xué)計(jì)算  、web服務(wù)器等。程序員當(dāng)然也希望python能夠運(yùn)算得更快,希望python可以更強(qiáng)大。

首先,python相比其他語(yǔ)言具體有多慢,這個(gè)不同場(chǎng)景和測(cè)試用例,結(jié)果肯定是不一樣的。這個(gè)網(wǎng)址給出了不同語(yǔ)言在各種case下的性能對(duì)比,這一頁(yè)是python3和C++的對(duì)比,下面是兩個(gè)case:

Python性能優(yōu)化分析

python運(yùn)算效率低,具體是什么原因呢,下列羅列一些

***:python是動(dòng)態(tài)語(yǔ)言

一個(gè)變量所指向?qū)ο蟮念?lèi)型在運(yùn)行時(shí)才確定,編譯器做不了任何預(yù)測(cè),也就無(wú)從優(yōu)化。舉一個(gè)簡(jiǎn)單的例子: r = a +  b?!和b相加,但a和b的類(lèi)型在運(yùn)行時(shí)才知道,對(duì)于加法操作,不同的類(lèi)型有不同的處理,所以每次運(yùn)行的時(shí)候都會(huì)去判斷a和b的類(lèi)型,然后執(zhí)行對(duì)應(yīng)的操作。而在靜態(tài)語(yǔ)言如C++中,編譯的時(shí)候就確定了運(yùn)行時(shí)的代碼。

第二:python是解釋執(zhí)行,但是不支持JIT(just in time compiler)。雖然大名鼎鼎的google曾經(jīng)嘗試Unladen  Swallow 這個(gè)項(xiàng)目,但最終也折了。

第三:python中一切都是對(duì)象,每個(gè)對(duì)象都需要維護(hù)引用計(jì)數(shù),增加了額外的工作。

第四:python GIL

GIL是Python最為詬病的一點(diǎn),因?yàn)镚IL,python中的多線(xiàn)程并不能真正的并發(fā)。如果是在IO bound的業(yè)務(wù)場(chǎng)景,這個(gè)問(wèn)題并不大,但是在CPU  BOUND的場(chǎng)景,這就很致命了。所以筆者在工作中使用python多線(xiàn)程的情況并不多,一般都是使用多進(jìn)程(pre  fork),或者在加上協(xié)程。即使在單線(xiàn)程,GIL也會(huì)帶來(lái)很大的性能影響,因?yàn)閜ython每執(zhí)行100個(gè)opcode(默認(rèn),可以通過(guò)sys.setcheckinterval()設(shè)置)就會(huì)嘗試線(xiàn)程的切換,具體的源代碼在ceval.c::PyEval_EvalFrameEx。

第五:垃圾回收,這個(gè)可能是所有具有垃圾回收的編程語(yǔ)言的通病。python采用標(biāo)記和分代的垃圾回收策略,每次垃圾回收的時(shí)候都會(huì)中斷正在執(zhí)行的程序,造成所謂的頓卡。infoq上有一篇文章,提到禁用Python的GC機(jī)制后,Instagram性能提升了10%。感興趣的讀者可以去細(xì)讀。

Be pythonic

我們都知道  過(guò)早的優(yōu)化是罪惡之源,一切優(yōu)化都需要基于profile。但是,作為一個(gè)python開(kāi)發(fā)者應(yīng)該要pythonic,而且pythonic的代碼往往比non-pythonic的代碼效率高一些,比如:

  • 使用迭代器iterator,for example:

dict的iteritems 而不是items(同itervalues,iterkeys)

使用generator,特別是在循環(huán)中可能提前break的情況

  • 判斷是否是同一個(gè)對(duì)象使用 is 而不是 ==

  • 判斷一個(gè)對(duì)象是否在一個(gè)集合中,使用set而不是list

  • 利用短路求值特性,把“短路”概率過(guò)的邏輯表達(dá)式寫(xiě)在前面。其他的lazy ideas也是可以的

  • 對(duì)于大量字符串的累加,使用join操作

  • 使用for else(while else)語(yǔ)法

  • 交換兩個(gè)變量的值使用: a, b = b, a

基于profile的優(yōu)化

即使我們的代碼已經(jīng)非常pythonic了,但可能運(yùn)行效率還是不能滿(mǎn)足預(yù)期。我們也知道80/20定律,絕大多數(shù)的時(shí)間都耗費(fèi)在少量的代碼片段里面了,優(yōu)化的關(guān)鍵在于找出這些瓶頸代碼。方式很多:到處加log打印時(shí)間戳、或者將懷疑的函數(shù)使用timeit進(jìn)行單獨(dú)測(cè)試,但最有效的是使用profile工具。

python profilers

對(duì)于python程序,比較出名的profile工具有三個(gè):profile、cprofile和hotshot。其中profile是純python語(yǔ)言實(shí)現(xiàn)的,Cprofile將profile的部分實(shí)現(xiàn)native化,hotshot也是C語(yǔ)言實(shí)現(xiàn),hotshot與Cprofile的區(qū)別在于:hotshot對(duì)目標(biāo)代碼的運(yùn)行影響較小,代價(jià)是更多的后處理時(shí)間,而且hotshot已經(jīng)停止維護(hù)了。需要注意的是,profile(Cprofile  hotshot)只適合單線(xiàn)程的python程序。

對(duì)于多線(xiàn)程,可以使用yappi,yappi不僅支持多線(xiàn)程,還可以精確到CPU時(shí)間

對(duì)于協(xié)程(greenlet),可以使用greenletprofiler,基于yappi修改,用greenlet context hook住thread  context

下面給出一段編造的”效率低下“的代碼,并使用Cprofile來(lái)說(shuō)明profile的具體方法以及我們可能遇到的性能瓶頸。

# -*- coding: UTF-8 -*-     from cProfile import Profile  import math  def foo():      return foo1()     def foo1():      return foo2()     def foo2():      return foo3()     def foo3():      return foo4()     def foo4():      return "this call tree seems ugly, but it always happen"     def bar():      ret = 0      for i in xrange(10000):          ret += i * i + math.sqrt(i)      return ret     def main():      for i in range(100000):          if i % 10000 == 0:              bar()          else:              foo()     if __name__ == '__main__':      prof = Profile()      prof.runcall(main)      prof.print_stats()      #prof.dump_stats('test.prof') # dump profile result to test.prof     code for profile

運(yùn)行結(jié)果如下:

Python性能優(yōu)化分析

對(duì)于上面的的輸出,每一個(gè)字段意義如下:

ncalls 函數(shù)總的調(diào)用次數(shù)

tottime 函數(shù)內(nèi)部(不包括子函數(shù))的占用時(shí)間

percall(***個(gè)) tottime/ncalls

cumtime 函數(shù)包括子函數(shù)所占用的時(shí)間

percall(第二個(gè))cumtime/ncalls

filename:lineno(function) 文件:行號(hào)(函數(shù))

代碼中的輸出非常簡(jiǎn)單,事實(shí)上可以利用pstat,讓profile結(jié)果的輸出多樣化,具體可以參見(jiàn)官方文檔python profiler。

profile GUI tools

雖然Cprofile的輸出已經(jīng)比較直觀,但我們還是傾向于保存profile的結(jié)果,然后用圖形化的工具來(lái)從不同的維度來(lái)分析,或者比較優(yōu)化前后的代碼。查看profile結(jié)果的工具也比較多,比如,visualpytune、qcachegrind、runsnakerun,本文用visualpytune做分析。對(duì)于上面的代碼,按照注釋生成修改后重新運(yùn)行生成test.prof文件,用visualpytune直接打開(kāi)就可以了,如下:

Python性能優(yōu)化分析

字段的意義與文本輸出基本一致,不過(guò)便捷性可以點(diǎn)擊字段名排序。左下方列出了當(dāng)前函數(shù)的calller(調(diào)用者),右下方是當(dāng)前函數(shù)內(nèi)部與子函數(shù)的時(shí)間占用情況。上如是按照cumtime(即該函數(shù)內(nèi)部及其子函數(shù)所占的時(shí)間和)排序的結(jié)果。

造成性能瓶頸的原因通常是高頻調(diào)用的函數(shù)、單次消耗非常高的函數(shù)、或者二者的結(jié)合。在我們前面的例子中,foo就屬于高頻調(diào)用的情況,bar屬于單次消耗非常高的情況,這都是我們需要優(yōu)化的重點(diǎn)。

python-profiling-tools中介紹了qcachegrind和runsnakerun的使用方法,這兩個(gè)colorful的工具比visualpytune強(qiáng)大得多。具體的使用方法請(qǐng)參考原文,下圖給出test.prof用qcachegrind打開(kāi)的結(jié)果。

Python性能優(yōu)化分析

qcachegrind確實(shí)要比visualpytune強(qiáng)大。從上圖可以看到,大致分為三部:。***部分同visualpytune類(lèi)似,是每個(gè)函數(shù)占用的時(shí)間,其中Incl等同于cumtime,  Self等同于tottime。第二部分和第三部分都有很多標(biāo)簽,不同的標(biāo)簽標(biāo)示從不同的角度來(lái)看結(jié)果,如圖上所以,第三部分的“call  graph”展示了該函數(shù)的call tree并包含每個(gè)子函數(shù)的時(shí)間百分比,一目了然。

profile針對(duì)優(yōu)化

知道了熱點(diǎn),就可以進(jìn)行針對(duì)性的優(yōu)化,而這個(gè)優(yōu)化往往根具體的業(yè)務(wù)密切相關(guān),沒(méi)用***鑰匙,具體問(wèn)題,具體分析。個(gè)人經(jīng)驗(yàn)而言,最有效的優(yōu)化是找產(chǎn)品經(jīng)理討論需求,可能換一種方式也能滿(mǎn)足需求,少者稍微折衷一下產(chǎn)品經(jīng)理也能接受。次之是修改代碼的實(shí)現(xiàn),比如之前使用了一個(gè)比較通俗易懂但效率較低的算法,如果這個(gè)算法成為了性能瓶頸,那就考慮換一種效率更高但是可能難理解的算法、或者使用dirty  Flag模式。對(duì)于這些同樣的方法,需要結(jié)合具體的案例,本文不做贅述。

接下來(lái)結(jié)合python語(yǔ)言特性,介紹一些讓python代碼不那么pythonic,但可以提升性能的一些做法

***:減少函數(shù)的調(diào)用層次

每一層函數(shù)調(diào)用都會(huì)帶來(lái)不小的開(kāi)銷(xiāo),特別對(duì)于調(diào)用頻率高,但單次消耗較小的calltree,多層的函數(shù)調(diào)用開(kāi)銷(xiāo)就很大,這個(gè)時(shí)候可以考慮將其展開(kāi)。

對(duì)于之前調(diào)到的profile的代碼,foo這個(gè)call tree非常簡(jiǎn)單,但頻率高。修改代碼,增加一個(gè)plain_foo()函數(shù),  直接返回最終結(jié)果,關(guān)鍵輸出如下:

Python性能優(yōu)化分析

跟之前的結(jié)果對(duì)比:

Python性能優(yōu)化分析

可以看到,優(yōu)化了差不多3倍。

第二:優(yōu)化屬性查找

上面提到,python 的屬性查找效率很低,如果在一段代碼中頻繁訪(fǎng)問(wèn)一個(gè)屬性(比如for循環(huán)),那么可以考慮用局部變量代替對(duì)象的屬性。

第三:關(guān)閉GC

在本文的***章節(jié)已經(jīng)提到,關(guān)閉GC可以提升python的性能,GC帶來(lái)的頓卡在實(shí)時(shí)性要求比較高的應(yīng)用場(chǎng)景也是難以接受的。但關(guān)閉GC并不是一件容易的事情。我們知道python的引用計(jì)數(shù)只能應(yīng)付沒(méi)有循環(huán)引用的情況,有了循環(huán)引用就需要靠GC來(lái)處理。在python語(yǔ)言中,  寫(xiě)出循環(huán)引用非常容易。比如:

case 1:  a, b = SomeClass(), SomeClass()  a.b, b.a = b, a     case 2:  lst = []  lst.append(lst)  case 3:  self.handler = self.some_func

當(dāng)然,大家可能說(shuō),誰(shuí)會(huì)這么傻,寫(xiě)出這樣的代碼,是的,上面的代碼太明顯,當(dāng)中間多幾個(gè)層級(jí)之后,就會(huì)出現(xiàn)“間接”的循環(huán)應(yīng)用。在python的標(biāo)準(zhǔn)庫(kù)  collections里面的OrderedDict就是case2:

Python性能優(yōu)化分析

要解決循環(huán)引用,***個(gè)辦法是使用弱引用(weakref),第二個(gè)是手動(dòng)解循環(huán)引用。

第四:setcheckinterval

如果程序確定是單線(xiàn)程,那么修改checkinterval為一個(gè)更大的值,這里有介紹。

第五:使用__slots__

slots最主要的目的是用來(lái)節(jié)省內(nèi)存,但是也能一定程度上提高性能。我們知道定義了__slots__的類(lèi),對(duì)某一個(gè)實(shí)例都會(huì)預(yù)留足夠的空間,也就不會(huì)再自動(dòng)創(chuàng)建__dict__。當(dāng)然,使用__slots__也有許多注意事項(xiàng),最重要的一點(diǎn),繼承鏈上的所有類(lèi)都必須定義__slots__,python  doc有詳細(xì)的描述。下面看一個(gè)簡(jiǎn)單的測(cè)試?yán)樱?/p>

class BaseSlots(object):      __slots__ = ['e', 'f', 'g']     class Slots(BaseSlots):      __slots__ = ['a', 'b', 'c', 'd']      def __init__(self):          self.a = self.b = self.c = self.d = self.e = self.f  = self.g = 0     class BaseNoSlots(object):          pass     class NoSlots(BaseNoSlots):      def __init__(self):          super(NoSlots,self).__init__()          self.a = self.b = self.c = self.d = self.e = self.f  = self.g = 0     def log_time(s):      begin = time.time()      for i in xrange(10000000):          s.a,s.b,s.c,s.d, s.e, s.f, s.g      return time.time() - begin     if __name__ == '__main__':      print 'Slots cost', log_time(Slots())      print 'NoSlots cost', log_time(NoSlots())

輸出結(jié)果:

Slots cost 3.12999987602  NoSlots cost 3.48100018501

python C擴(kuò)展

也許通過(guò)profile,我們已經(jīng)找到了性能熱點(diǎn),但這個(gè)熱點(diǎn)就是要運(yùn)行大量的計(jì)算,而且沒(méi)法cache,沒(méi)法省略。。。這個(gè)時(shí)候就該python的C擴(kuò)展出馬了,C擴(kuò)展就是把部分python代碼用C或者C++重新實(shí)現(xiàn),然后編譯成動(dòng)態(tài)鏈接庫(kù),提供接口給其它python代碼調(diào)用。由于C語(yǔ)言的效率遠(yuǎn)遠(yuǎn)高于python代碼,所以使用C擴(kuò)展是非常普遍的做法,比如我們前面提到的cProfile就是基于_lsprof.so的一層封裝。python的大所屬對(duì)性能有要求的庫(kù)都使用或者提供了C擴(kuò)展,如gevent、protobuff、bson。

筆者曾經(jīng)測(cè)試過(guò)純python版本的bson和cbson的效率,在綜合的情況下,cbson快了差不多10倍!

python的C擴(kuò)展也是一個(gè)非常復(fù)雜的問(wèn)題,本文僅給出一些注意事項(xiàng):

***:注意引用計(jì)數(shù)的正確管理

這是最難最復(fù)雜的一點(diǎn)。我們都知道python基于指針技術(shù)來(lái)管理對(duì)象的生命周期,如果在擴(kuò)展中引用計(jì)數(shù)出了問(wèn)題,那么要么是程序崩潰,要么是內(nèi)存泄漏。更要命的是,引用計(jì)數(shù)導(dǎo)致的問(wèn)題很難debug。。。

C擴(kuò)展中關(guān)于引用計(jì)數(shù)最關(guān)鍵的三個(gè)詞是:steal reference,borrowed reference,new  reference。建議編寫(xiě)擴(kuò)展代碼之前細(xì)讀python的官方文檔。

第二:C擴(kuò)展與多線(xiàn)程

這里的多線(xiàn)程是指在擴(kuò)展中new出來(lái)的C語(yǔ)言線(xiàn)程,而不是python的多線(xiàn)程,出了python doc里面的介紹,也可以看看《python  cookbook》的相關(guān)章節(jié)。

第三:C擴(kuò)展應(yīng)用場(chǎng)景

僅適合與業(yè)務(wù)代碼的關(guān)系不那么緊密的邏輯,如果一段代碼大量業(yè)務(wù)相關(guān)的對(duì)象 屬性的話(huà),是很難C擴(kuò)展的

將C擴(kuò)展封裝成python代碼可調(diào)用的接口的過(guò)程稱(chēng)之為binding,Cpython本身就提供了一套原生的API,雖然使用最為廣泛,但該規(guī)范比較復(fù)雜。很多第三方庫(kù)做了不同程度的封裝,以便開(kāi)發(fā)者使用,比如boost.python、cython、ctypes、cffi(同時(shí)支持pypy  cpython),具體怎么使用可以google。

beyond CPython

盡管python的性能差強(qiáng)人意,但是其易學(xué)易用的特性還是贏得越來(lái)越多的使用者,業(yè)界大牛也從來(lái)沒(méi)有放棄對(duì)python的優(yōu)化。這里的優(yōu)化是對(duì)python語(yǔ)言設(shè)計(jì)上、或者實(shí)現(xiàn)上的一些反思或者增強(qiáng)。這些優(yōu)化項(xiàng)目一些已經(jīng)夭折,一些還在進(jìn)一步改善中,在這個(gè)章節(jié)介紹目前還不錯(cuò)的一些項(xiàng)目。

cython

前面提到cython可以用到binding c擴(kuò)展,但是其作用遠(yuǎn)遠(yuǎn)不止這一點(diǎn)。

Cython的主要目的是加速python的運(yùn)行效率,但是又不像上一章節(jié)提到的C擴(kuò)展那么復(fù)雜。在Cython中,寫(xiě)C擴(kuò)展和寫(xiě)python代碼的復(fù)雜度差不多(多虧了Pyrex)。Cython是python語(yǔ)言的超集,增加了對(duì)C語(yǔ)言函數(shù)調(diào)用和類(lèi)型聲明的支持。從這個(gè)角度來(lái)看,cython將動(dòng)態(tài)的python代碼轉(zhuǎn)換成靜態(tài)編譯的C代碼,這也是cython高效的原因。使用cython同C擴(kuò)展一樣,需要編譯成動(dòng)態(tài)鏈接庫(kù),在linux環(huán)境下既可以用命令行,也可以用distutils。

如果想要系統(tǒng)學(xué)習(xí)cython,建議從cython  document入手,文檔寫(xiě)得很好。下面通過(guò)一個(gè)簡(jiǎn)單的示例來(lái)展示cython的使用方法和性能(linux環(huán)境)。

首先,安裝cython:

pip install Cython

下面是測(cè)試用的python代碼,可以看到這兩個(gè)case都是運(yùn)算復(fù)雜度比較高的例子:

def f():      return x**2-x     def integrate_f(a, b, N):      s = 0      dx = (b-a)/N      for i in range(N):          s += f(a+i*dx)      return s * dx     def main():      import time      begin = time.time()      for i in xrange(10000):          for i in xrange(100):f(10)      print 'call f cost:', time.time() - begin      begin = time.time()      for i in xrange(10000):          integrate_f(1.0, 100.0, 1000)      print 'call integrate_f cost:', time.time() - begin     if __name__ == '__main__':      main()

運(yùn)行結(jié)果:

call f cost: 0.215116024017  call integrate_f cost: 4.33698010445

不改動(dòng)任何python代碼也可以享受到cython帶來(lái)的性能提升,具體做法如下:

  • step1:將文件名(cython_example.py)改為cython_example.pyx

  • step2:增加一個(gè)setup.py文件,添加一下代碼:

from distutils.core import setup  from Cython.Build import cythonize     setup(    name = 'cython_example',    ext_modules = cythonize("cython_example.pyx"),  )
  • step3:執(zhí)行python setup.py build_ext –inplace

Python性能優(yōu)化分析

可以看到 增加了兩個(gè)文件,對(duì)應(yīng)中間結(jié)果和***的動(dòng)態(tài)鏈接庫(kù)

  • step4:執(zhí)行命令 python -c “import cython_example;cython_example.main()”(注意:  保證當(dāng)前環(huán)境下已經(jīng)沒(méi)有 cython_example.py)

運(yùn)行結(jié)果:

call f cost: 0.0874309539795  call integrate_f cost: 2.92381191254

性能提升了大概兩倍,我們?cè)賮?lái)試試cython提供的靜態(tài)類(lèi)型(static  typing),修改cython_example.pyx的核心代碼,替換f()和integrate_f()的實(shí)現(xiàn)如下:

def f(double x): # 參數(shù)靜態(tài)類(lèi)型      return x**2-x     def integrate_f(double a, double b, int N):      cdef int i      cdef double s, dx      s = 0      dx = (b-a)/N      for i in range(N):          s += f(a+i*dx)      return s * dx

然后重新運(yùn)行上面的第三 四步:結(jié)果如下

call f cost: 0.042387008667  call integrate_f cost: 0.958620071411

上面的代碼,只是對(duì)參數(shù)引入了靜態(tài)類(lèi)型判斷,下面對(duì)返回值也引入靜態(tài)類(lèi)型判斷。

替換f()和integrate_f()的實(shí)現(xiàn)如下:

cdef double f(double x): # 返回值也有類(lèi)型判斷      return x**2-x     cdef double integrate_f(double a, double b, int N):      cdef int i      cdef double s, dx      s = 0      dx = (b-a)/N      for i in range(N):          s += f(a+i*dx)      return s * dx

然后重新運(yùn)行上面的第三 四步:結(jié)果如下

call f cost: 1.19209289551e-06  call integrate_f cost: 0.187038183212

Amazing!

pypy

pypy是CPython的一個(gè)替代實(shí)現(xiàn),其最主要的優(yōu)勢(shì)就是pypy的速度,下面是官網(wǎng)的測(cè)試結(jié)果:

Python性能優(yōu)化分析

在實(shí)際項(xiàng)目中測(cè)試,pypy大概比cpython要快3到5倍!pypy的性能提升來(lái)自JIT Compiler。在前文提到google的Unladen  Swallow  項(xiàng)目也是想在CPython中引入JIT,在這個(gè)項(xiàng)目失敗后,很多開(kāi)發(fā)人員都開(kāi)始加入pypy的開(kāi)發(fā)和優(yōu)化。另外pypy占用的內(nèi)存更少,而且支持stackless,基本等同于協(xié)程。

pypy的缺點(diǎn)在于對(duì)C擴(kuò)展方面支持的不太好,需要使用CFFi來(lái)做binding。對(duì)于使用廣泛的library來(lái)說(shuō),一般都會(huì)支持pypy,但是小眾的、或者自行開(kāi)發(fā)的C擴(kuò)展就需要重新封裝了。

“Python性能優(yōu)化分析”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI