誰(shui)能真正替代你?AI輔助編碼工具深度對比(chatGPT/Copilot/Cursor/New Bing)
寫在開頭
這幾個月AI相關新聞的(de)火爆程度大家都已經看見了,作為一個被裹挾(xie)在AI時代浪(lang)潮中的(de)程序員(yuan),在這幾個月里我(wo)也是異常興奮(fen)和(he)焦(jiao)慮。甚至都興奮(fen)的(de)不想(xiang)拖(tuo)更(geng)了。不僅僅興奮(fen)于(yu)AI對于(yu)我(wo)們生產力的(de)全面提升,也焦(jiao)慮于(yu)Copilot等AI輔助編碼工具,會將(jiang)程序員(yuan)這個工種和(he)我(wo)們所(suo)熟悉的(de)傳統軟(ruan)件開發流程徹底(di)顛覆,用計算機(ji)的(de)極高(gao)效率(lv)碾(nian)壓(ya)人類(lei)的(de)低效率(lv)。
當然這些(xie)也都(dou)是后話了,我們(men)目前能做的(de),就是保持敏銳(rui)的(de)嗅覺,嘗試去迎接AI時代的(de)來臨。做“未來世界的(de)幸存者(zhe)”。
本文是我深度體驗了Github Copilot, ChatGPT等產品后,對于這些AI輔助開發工具的一次橫向評測。寫本文的初衷是幫助大家快速篩選出一款合適你的AI輔助工具。相信我,請不要再懷疑這些工具(ju)是(shi)否能(neng)給你(ni)帶來效率提升。當你(ni)嘗試使用后,很快就會習(xi)慣它們,甚至是(shi)離不開(kai)它們。
本文評測的工具有:
- Github Copilot
- ChatGPT(GPT-3.5)
- New Bing
- Cursor.so
這些(xie)(xie)工(gong)具可以結合起來使用(yong),提升你的開(kai)發效率。所以這些(xie)(xie)工(gong)具之間并不是互斥(chi)關系(xi)。文章的最后會給出(chu)總結以及(ji)我的一些(xie)(xie)想(xiang)法。
Github Copilot
Github Copilot是(shi)由Github和OpenAI合(he)作推(tui)出的(de)一個人工智(zhi)能(neng)代(dai)碼(ma)輔(fu)助工具,采用了OpenAI的(de)GPT技術,能(neng)夠為開發(fa)人員提供實(shi)時的(de)代(dai)碼(ma)提示和生成(cheng)功能(neng),類似于一個AI助手(shou),幫助開發(fa)人員更快速、更方便地編寫代(dai)碼(ma)。
當前的(de)Github Copilot基于(yu)GPT-3模型,它可以(yi)分析上下(xia)文并根據已有的(de)代(dai)碼(ma)和注釋來推斷出應該寫什么(me)代(dai)碼(ma)。通過使用(yong)Github Copilot,開(kai)發人員可以(yi)減少手動輸入代(dai)碼(ma)的(de)時間,提(ti)高代(dai)碼(ma)的(de)質量和效率。它支持(chi)多種編程語(yu)言,如(ru)Python、JavaScript、TypeScript、Ruby等(deng),并可以(yi)與主流的(de)集(ji)成(cheng)開(kai)發環境(IDE)和文本編輯(ji)器(qi)配合(he)使用(yong)。
使用體驗
我(wo)(wo)已經深度使用(yong)了(le)Copilot接近一個(ge)月(yue),但每當我(wo)(wo)和同事朋友們聊到Copilot的(de)使用(yong)體驗,以及(ji)它在哪方(fang)面能(neng)夠提高(gao)我(wo)(wo)的(de)效(xiao)率時,我(wo)(wo)仍很(hen)難用(yong)語言去(qu)描述,我(wo)(wo)只能(neng)粗(cu)略(lve)的(de)總結(jie)為下(xia)面幾個(ge)結(jie)論:
- 編寫你熟悉的語言時,他僅能幫助你減少一些重復模板代碼的編寫。編寫你不熟悉的語言時,他能夠準確推斷你的意圖,直接生成代碼,免除了查詢如何使用API的耗時工作
- 它順著你的心流生成片段代碼,但很難從0開始為你創造整段代碼,即使它是可以生成整段代碼的,但是也常常是需要你手工修改的。
接下來看(kan)一下我常用的幾種使用方(fang)式(shi)。
1. 根據上下文生成代碼
它可(ke)以根據函數名(ming)(ming),類名(ming)(ming),注釋(shi),來推斷你(ni)想寫(xie)的代碼,幫你(ni)填充。
2. 根據代碼生成注釋
反過來(lai),它可以嘗試理解你(ni)的代碼,為你(ni)生(sheng)成注(zhu)釋(shi),你(ni)只需要給他一(yi)個 // 前(qian)綴(zhui)
3. 幫你起變量名
它可以幫你(ni)器變量(liang)名(ming),這可是很多英語(yu)不(bu)好的程序員的大福音。畢竟(jing),編碼(ma)的30%時間,是在想變量(liang)名(ming)。
4. 和你聊天,當然,是聊代碼!
你(ni)沒有聽錯,copilot可(ke)以(yi)在(zai)你(ni)的(de)代(dai)碼里聊(liao)天,但是顯然不能(neng)和他嘮家常。它并(bing)不是chatGPT,無法給你(ni)常識(shi)回(hui)答(da)(應該是被故意(yi)限制了交流(liu)范圍),只會和你(ni)扯皮(pi)。
正確的(de)(de)使用方式是讓(rang)它(ta)和(he)你(ni)討(tao)論你(ni)寫代碼,他(ta)會總結(jie)你(ni)的(de)(de)上下文代碼,并且給你(ni)一個它(ta)認為“合理”的(de)(de)解釋。
以上就是(shi)我常用的幾(ji)種(zhong)方式(shi),我查閱了很多(duo)資料,基本上面(mian)涵蓋了大部分操作方式(shi)。當然,可能還有我沒挖掘(jue)到的使(shi)用方式(shi)。
編碼能力
說(shuo)完使用體驗(yan),我想要引出(chu)我本次橫向評(ping)測的(de)一個(ge)評(ping)測標準,就(jiu)是通(tong)過相近的(de)試(shi)題,看下這幾個(ge)工具(ju)的(de)編碼能力有何(he)區(qu)別,給大家(jia)直(zhi)觀(guan)地對比。我們就(jiu)先從Copilot開始。
獨立編寫:單例模式
我給(gei)它(ta)們(men)設定的(de)(de)(de)題(ti)(ti)目是獨立寫(xie)一個單例模式,這個題(ti)(ti)目是國內(nei)Java開發者(zhe)“常考題(ti)(ti)”,里面除了基(ji)本的(de)(de)(de)代碼,還有很多(duo)細節需要注(zhu)意,也可以很好的(de)(de)(de)用來(lai)向AI們(men)提(ti)問,看看它(ta)們(men)是否真正地理解(jie)它(ta)們(men)寫(xie)的(de)(de)(de)內(nei)容。
讓我(wo)們歡迎第一位選手,Github Copilot。
上面的(de)編碼動(dong)畫也是我(wo)認(ren)為最(zui)符合我(wo)日(ri)常使(shi)用copilot的(de)案例(li),從創(chuang)建類(lei)文件(jian)后,copilot幫(bang)我(wo)自(zi)動(dong)生成了私有(you)(you)變(bian)量,私有(you)(you)構造(zao)方(fang)法(fa)(fa),以及獲取單例(li)的(de)公有(you)(you)方(fang)法(fa)(fa)。在寫公有(you)(you)方(fang)法(fa)(fa)的(de)期間,它最(zui)開始的(de)代(dai)碼沒有(you)(you)考慮雙重檢查鎖(suo),我(wo)給了它一些提示,它補全(quan)了剩余的(de)代(dai)碼。
需(xu)要注意的(de)是(shi),由(you)于單(dan)例模式(shi)在網上有太多的(de)學習資料,Copilot肯(ken)定也吸取了大(da)量優(you)秀(xiu)代(dai)碼(ma),才能(neng)寫的(de)如此高(gao)效。并(bing)不代(dai)表所(suo)有復雜(za)的(de)代(dai)碼(ma)題都能(neng)夠幫你(ni)自動(dong)補全,并(bing)保證正(zheng)確性。所(suo)以大(da)家(jia)謹慎看待它的(de)獨立編碼(ma)能(neng)力,也不要過分樂觀。
補全項目現有代碼
第(di)二個(ge)(ge)測試,我們讓它(ta)補全項目的(de)現有代(dai)碼,我拿一個(ge)(ge)我自己(ji)寫的(de)代(dai)碼舉例(li),其中有一個(ge)(ge)DiffDTO實(shi)體(ti)類,有一個(ge)(ge)addDiffDTO方法一行都(dou)沒寫,需要(yao)補全。
可以看到(dao)它自動補全(quan)了代碼(ma),并且會用(yong)到(dao)上面(mian)已(yi)有的(de)方法和變(bian)量。這(zhe)也是它的(de)強項,根據上下文信息(xi)推(tui)斷代碼(ma)該怎么補全(quan),寫(xie)出來(lai)的(de)代碼(ma)質量很高,不能(neng)說(shuo)每次都可直接使(shi)用(yong),但80%的(de)情況(kuang)下你(ni)是無需做大幅度修改的(de)。
不足之處
Github Copilot有不(bu)足(zu)之處嗎?我(wo)想了想,有以(yi)下幾點:
- 所有代碼上下文通過https傳到云端(微軟的服務器),盡管我通讀了微軟的隱私保護政策,它承諾不會有任何代碼被泄露。但放在國內網絡安全大環境下,這樣的處理數據方式,在國內公司內使用很可能會受到影響,需要用戶注意。
- 對于復雜的代碼邏輯,比如連你都一下子看不懂的屎山代碼,它的理解能力同樣欠缺。它擅長的是網絡上有很多公開資料的代碼段落和實現方式。
ChatGPT
讓我們(men)來看(kan)下(xia)一(yi)位選(xuan)手(shou),最近火出圈的chatGPT。
chatGPT是(shi)一款基于GPT模型的聊(liao)天機器人(ren)(ren),它可以(yi)像(xiang)人(ren)(ren)類(lei)一樣進行自然(ran)語言對話。它不僅(jin)可以(yi)回(hui)答用戶(hu)的各種問題,還可以(yi)學習和理解用戶(hu)的需求,提供更(geng)恰當(dang)的回(hui)答和建議。
得(de)益于(yu)其基于(yu)GPT模型(xing)的強大語(yu)(yu)言處理能(neng)力。GPT模型(xing)可(ke)以(yi)理解代碼中(zhong)的自(zi)然語(yu)(yu)言描(miao)述部分。其次,chatGPT在訓練階段(duan)使用了大量的代碼相關文(wen)本語(yu)(yu)料,包括代碼庫、API文(wen)檔、程序員交流論壇等,因此具有一定(ding)的代碼理解和識別(bie)能(neng)力。此外,chatGPT還可(ke)以(yi)通過不斷(duan)的學(xue)習和自(zi)我(wo)優化(hua),逐漸提(ti)高其對(dui)于(yu)代碼相關問題的處理能(neng)力和準(zhun)確度。
我(wo)文中使用的chatGPT是GPT-3.5版本(ben)。我(wo)們(men)(men)直接進(jin)入正題(ti),看看它的編碼(ma)能力(li)。由于是對(dui)話方式,所以我(wo)們(men)(men)需要向它描述(shu)我(wo)們(men)(men)的需求。
編碼能力
獨立編寫:單例模式
我:
請(qing)用Java寫(xie)一個單(dan)例模式
ChatGPT:
public class Singleton { ? private static Singleton instance; ? private Singleton() { // 私有構造函數 } ? public static synchronized Singleton getInstance() { // 雙重檢查鎖定 if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } return instance; } } } ? // 使用方法 Singleton s = Singleton.getInstance();
寫(xie)的不錯,很(hen)有面試(shi)題刷多了的開發(fa)人(ren)員的風格,寫(xie)了經常(chang)會(hui)被(bei)問(wen)到的雙重校驗鎖,但是沒有用volatile,讓我們繼續發(fa)問(wen)。
我:
為何要(yao)用雙重檢查鎖(suo)定?
ChatGPT:
雙重檢(jian)查鎖(suo)定(double-checked locking)是(shi)一種常見的單(dan)例(li)模式(shi)的實(shi)(shi)現方式(shi)。它的目的是(shi)在(zai)不使(shi)用同步鎖(suo)的情況下實(shi)(shi)現單(dan)例(li)模式(shi)的線程安全,避(bi)免了每(mei)次獲取對(dui)象(xiang)實(shi)(shi)例(li)時都進行同步鎖(suo)的開銷。但是(shi),這(zhe)種實(shi)(shi)現方式(shi)也有可能(neng)存在(zai)線程安全問(wen)題。
具體(ti)來說(shuo),雙(shuang)重檢查鎖(suo)定是在(zai) getInstance 方法(fa)中(zhong)加入(ru)了(le)(le)雙(shuang)重檢查,即先(xian)判斷(duan)實例(li)是否已(yi)經存(cun)在(zai),如果(guo)不存(cun)在(zai)再進行同步鎖(suo)的(de)操作。這樣(yang)可(ke)以減少同步鎖(suo)的(de)開銷(xiao),提高(gao)了(le)(le)效(xiao)率。
但是,在(zai)多線程(cheng)環境下(xia),雙重檢查(cha)(cha)鎖定也有可能出(chu)現(xian)線程(cheng)安全問題,原因是在(zai)某(mou)些情況(kuang)下(xia),多個線程(cheng)可能同時(shi)通過第一重檢查(cha)(cha),然后同時(shi)進行同步鎖的(de)操(cao)作(zuo),導致出(chu)現(xian)多個實例。
為(wei)了避(bi)免(mian)這(zhe)種(zhong)情況(kuang),需(xu)要(yao)將(jiang) getInstance 方法(fa)設置為(wei) synchronized 關(guan)鍵字,保(bao)證在同(tong)一(yi)時刻只有一(yi)個線(xian)程可以訪問該(gai)方法(fa),從而(er)避(bi)免(mian)多個實(shi)例(li)的出現。但是,這(zhe)樣(yang)會帶來一(yi)定的性能開銷,因(yin)為(wei)每次訪問 getInstance 方法(fa)都需(xu)要(yao)進行同(tong)步鎖操(cao)作。
講得不錯,但很(hen)難不讓人覺得是看(kan)多了面試文章。我(wo)們順著volatile的問題繼(ji)續問問他為啥(sha)不寫volatile。
我:
上面的代碼(ma)依然有漏洞,可(ke)以(yi)優化(hua)下嗎
ChatGPT:
是的(de),即使(shi)(shi)使(shi)(shi)用(yong)了(le)雙重(zhong)檢查鎖(suo)定,上面的(de)代(dai)碼仍然有漏洞,也就是可能出現線程(cheng)安(an)全問題(ti)。為了(le)解決(jue)這個(ge)問題(ti),可以使(shi)(shi)用(yong) Java5 中引(yin)入的(de) volatile 關(guan)鍵(jian)字,通(tong)過禁(jin)止指令重(zhong)排來保(bao)證線程(cheng)安(an)全。
優化后的代碼如下:
public class Singleton { private static volatile Singleton instance; ? private Singleton() { // 私有構造方法 } ? public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } return instance; } } } ?上(shang)面(mian)的(de)代(dai)碼(ma)中(zhong),我們將 instance 字段添加了(le) volatile 關鍵字,這(zhe)樣可以(yi)保證在多線程(cheng)環境中(zhong),instance 變量的(de)讀取和賦值操(cao)作(zuo)是(shi)原子(zi)性的(de),即(ji)保證了(le)線程(cheng)的(de)可見性和有序(xu)性,從而避免了(le)出(chu)現多個實例(li)的(de)情況。
注意,這里的 volatile 關鍵字只是作用于(yu) instance 字段本身,而不是整個 getInstance 方法。這樣(yang)做可以減小同步(bu)鎖的范圍,提高了效率。
它自己想到并解(jie)決(jue)了volatile的細節問(wen)(wen)題,我們再結合他寫的代碼(ma),問(wen)(wen)問(wen)(wen)一些面(mian)試官會問(wen)(wen)的問(wen)(wen)題。
我:
上(shang)面(mian)的(de)(de)代(dai)碼中,同步的(de)(de)是(shi)一個Singleton.class,可以用int變量代(dai)替嗎
ChatGPT:
不(bu)(bu)可以(yi),使用 int 變(bian)量代(dai)替 Singleton.class 進行(xing)同步是不(bu)(bu)可行(xing)的。
因為在 Java 中,每個類都(dou)有一(yi)個唯一(yi)的 Class 對象,多個線程(cheng)使用(yong)同(tong)一(yi)個類的 Class 對象進行(xing)(xing)同(tong)步,可以保(bao)證(zheng)線程(cheng)安全(quan)。而使用(yong) int 變(bian)量進行(xing)(xing)同(tong)步,由于 int 是基本(ben)數據類型,它在內存中的每一(yi)份(fen)拷貝都(dou)可能不(bu)同(tong),因此不(bu)能保(bao)證(zheng)多個線程(cheng)都(dou)使用(yong)同(tong)一(yi)個 int 變(bian)量進行(xing)(xing)同(tong)步,從而無法保(bao)證(zheng)線程(cheng)安全(quan)。
此外,使(shi)用(yong) Class 對(dui)象進行同(tong)步(bu)還有一個(ge)好(hao)處,即可(ke)(ke)以保證實(shi)例變量的可(ke)(ke)見性,因(yin)為同(tong)步(bu)代碼(ma)塊中的讀(du)寫操作都(dou)是在(zai)同(tong)一條線程中執(zhi)行的。
因此(ci),使用 Singleton.class 對象進(jin)行同步是保證線程安全的最(zui)佳方式(shi),不(bu)建議使用其他方式(shi)進(jin)行同步。
回(hui)答的不錯(cuo),理解了我提示語中(zhong)的 Singleton.class 是(shi)指的它自己(ji)代碼(ma)中(zhong)的類,最后(hou)問一個volatile的細(xi)節。
我:
上面的代碼中(zhong),如果不使(shi)用(yong)volatile,有可(ke)能會出(chu)現重排序問題,能具體展開(kai)講(jiang)講(jiang)嗎
ChatGPT:
是的,如果沒有(you)使(shi)用volatile關(guan)鍵字,就有(you)可能出現(xian)重排(pai)序(xu)問題。
在(zai)Java中(zhong)(zhong),編譯(yi)器和處理(li)器都會(hui)對(dui)指令進行(xing)重(zhong)排(pai)(pai)序(xu)(xu),這(zhe)是為(wei)了優化程(cheng)(cheng)序(xu)(xu)性(xing)能。例(li)如,在(zai)一(yi)(yi)個if語句中(zhong)(zhong),為(wei)了避(bi)免(mian)每次都進行(xing)判(pan)(pan)斷,編譯(yi)器可能會(hui)將判(pan)(pan)斷條件提前到if語句外面(mian)。在(zai)單線程(cheng)(cheng)環境下(xia),這(zhe)種重(zhong)排(pai)(pai)序(xu)(xu)是沒(mei)有問(wen)題的(de),因為(wei)程(cheng)(cheng)序(xu)(xu)執行(xing)的(de)結(jie)果是一(yi)(yi)樣的(de)。但是,在(zai)多線程(cheng)(cheng)環境下(xia),重(zhong)排(pai)(pai)序(xu)(xu)可能會(hui)導(dao)致線程(cheng)(cheng)安全問(wen)題。
考慮下面的代碼:
public class Singleton { private static Singleton instance; ? private Singleton() {} ? public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } return instance; } } }在這個(ge)代碼中,如果沒(mei)有使用volatile關鍵字,就有可能(neng)出現下面的重(zhong)排(pai)序:
- 線程A調用getInstance()方法,發現instance為null,進入同步塊。
- 線程B調用getInstance()方法,由于instance還沒有被初始化,也發現instance為null,也進入同步塊。
- 線程A執行instance = new Singleton(),在執行該語句時,可能會被重排序為下面這樣:
memory = allocate(); //1:分配對象的內存空間 ctorInstance(memory); //2:初始化對象 instance = memory; //3:將對象賦值給instance變量這(zhe)(zhe)個(ge)重排(pai)序是允許的,因為(wei)在(zai)單線(xian)程環境(jing)下(xia),這(zhe)(zhe)個(ge)順(shun)序和原來的順(shun)序沒(mei)有區別。但(dan)是,在(zai)多線(xian)程環境(jing)下(xia),這(zhe)(zhe)個(ge)重排(pai)序可(ke)能(neng)會導致線(xian)程安全問題,因為(wei)線(xian)程B此(ci)時檢(jian)查instance不(bu)為(wei)null,就會直接返回(hui)instance,這(zhe)(zhe)個(ge)instance還(huan)沒(mei)有被初始化,就會導致錯誤。
為了避免這種(zhong)情況,可(ke)以使用volatile關鍵(jian)字來確保instance變量的可(ke)見性,從而避免重排序問題。
上面的(de)(de)對話,是一(yi)個(ge)典型的(de)(de)面試題,網(wang)上有很多文章也(ye)會順著這個(ge)思路寫下來,所(suo)以chatGPT回答(da)的(de)(de)得心應手,并不代表在(zai)所(suo)有代碼優化方面都能回答(da)的(de)(de)這么細節和流暢。但不得不說,如果你第一(yi)次用(yong)chatGPT,你還是會被它的(de)(de)能力和思路所(suo)震驚。
接(jie)下來我(wo)們讓chatGPT解決一(yi)個kotlin中并(bing)發接(jie)口的(de)(de)設(she)計,看看它對(dui)于不那么常見(jian)的(de)(de)復雜問題的(de)(de)解決能(neng)力
獨立編寫:Kotlin+Reactor設計并發接口
這是我在實際開發過程中遇到的一個比較難寫的代碼例子,在kotlin中使用reactor框架來寫一個批量查(cha)詢接口,由于性能(neng)要(yao)求,該(gai)接口中的每個查(cha)詢必須并發執(zhi)(zhi)行(線程池執(zhi)(zhi)行)。我(wo)(wo)把這個遇到的實際(ji)問(wen)題(ti)作為測試的case,讓我(wo)(wo)們來問(wen)問(wen)chatGPT。
我:
Kotlin中,如何用reactor框架寫一個并發處理的方法,要求入參是List
,方法中將字符串后面拼接同樣的字符串,輸出List ChatGPT:
您可以使用Reactor框架中的
Flux來實現(xian)這個(ge)要求(qiu)。下面(mian)是(shi)一個(ge)示例代(dai)碼:import reactor.core.publisher.Flux import reactor.core.scheduler.Schedulers ? fun processStrings(strings: List<String>): List<String> { val flux = Flux.fromIterable(strings) .flatMap { str -> Mono.fromCallable { str+str } .subscribeOn(Schedulers.parallel()) } return flux.collectList().block() ?: emptyList() } ?在這個例子中,我們使用
Flux.fromIterable方法創建一個Flux,并使用flatMap方法處理每個字符串。在flatMap中,我們使用Mono.fromCallable方法來創建一個異步執行的函數,該函數將每個字符串轉為大寫。同時我們使用Schedulers.parallel()指定執(zhi)行該函數的調度器(qi),以實現(xian)并發執(zhi)行的效果。最后,我們使用
collectList()方法等待所有異步操作完成并將結果作為List<String>返回。需要注意的是,我們使用了并發執行,因此結果順序可能與輸入順序不同。如果您需要保持結果順序與輸入順序相同,可以使用
concatMap方法代替flatMap。
在它(ta)給出的代(dai)(dai)碼上,我加入(ru)了(le)一個隨機休眠的函數來驗(yan)證是否代(dai)(dai)碼真(zhen)的執行(xing)了(le)異步,并(bing)且(qie)給了(le)它(ta)入(ru)參 [0-9],然后執行(xing),事實證明chatGPT給出的代(dai)(dai)碼直接可以(yi)跑通(tong),并(bing)且(qie)真(zhen)正是并(bing)行(xing)執行(xing)的。給出結果:
Current ThreadName: parallel-1 sleep time: 188
Current ThreadName: parallel-2 sleep time: 483
Current ThreadName: parallel-3 sleep time: 785
Current ThreadName: parallel-4 sleep time: 524
Current ThreadName: parallel-5 sleep time: 175
Current ThreadName: parallel-6 sleep time: 894
Current ThreadName: parallel-7 sleep time: 440
Current ThreadName: parallel-8 sleep time: 158
Current ThreadName: parallel-9 sleep time: 227
Current ThreadName: parallel-10 sleep time: 300
Result:[99, 33, 66, 1010, 22, 11, 44, 77, 88, 55]
但是(shi)它的代(dai)碼中使(shi)用了flatMap并(bing)沒有保(bao)證返回結果List中的順序和(he)入參順序相同(tong),對于我(wo)的需求來說這(zhe)是(shi)不可接(jie)受的,chatGPT在剛才(cai)的回答中考慮到了這(zhe)個問(wen)題,告訴了我(wo)解決方案,使(shi)用concatMap代(dai)替,于是(shi)我(wo)修改為(wei)concatMap,結果如(ru)下:
Current ThreadName: parallel-1 sleep time: 933
Current ThreadName: parallel-2 sleep time: 715
Current ThreadName: parallel-3 sleep time: 278
Current ThreadName: parallel-4 sleep time: 182
Current ThreadName: parallel-5 sleep time: 14
Current ThreadName: parallel-6 sleep time: 313
Current ThreadName: parallel-7 sleep time: 228
Current ThreadName: parallel-8 sleep time: 650
Current ThreadName: parallel-9 sleep time: 554
Current ThreadName: parallel-10 sleep time: 334
Result:[11, 22, 33, 44, 55, 66, 77, 88, 99, 1010]
看上去一切ok,但其實(shi)問題很大。使(shi)用(yong)了concatMap后,實(shi)際(ji)上使(shi)用(yong)concatMap的(de)這段代碼并沒有真正的(de)并發執(zhi)行,而是類(lei)似(si)串行的(de)方式,得(de)到了結果。(本(ben)文不具體展(zhan)開解釋為何(he)使(shi)用(yong)了concatMap后會(hui)有如此效果)
既然(ran)答案和我(wo)想要的有偏差,那就繼續追(zhui)問(wen)chatGPT。
我:
這(zhe)段(duan)代碼如何在(zai)保證返回值順序和(he)入(ru)參順序相同的(de)(de)情況下(xia),真正的(de)(de)并發執行?
ChatGPT:
可(ke)以使用flatMapSequential操作符替換(huan)concatMap操作符來實現真(zhen)正的并發執行,并且保(bao)證返(fan)回值順序和入參(can)順序相同。
修改后的代碼如下:
fun processStrings(strings: List<String>): List<String> { val flux = Flux.fromIterable(strings) .flatMapSequential { str -> Mono.fromCallable { str+str }.subscribeOn(Schedulers.parallel()) } return flux.collectList().block() ?: emptyList() }
Current ThreadName: parallel-1 sleep time: 755
Current ThreadName: parallel-2 sleep time: 342
Current ThreadName: parallel-3 sleep time: 340
Current ThreadName: parallel-4 sleep time: 562
Current ThreadName: parallel-5 sleep time: 405
Current ThreadName: parallel-6 sleep time: 399
Current ThreadName: parallel-7 sleep time: 250
Current ThreadName: parallel-8 sleep time: 351
Current ThreadName: parallel-9 sleep time: 169
Current ThreadName: parallel-10 sleep time: 118
Result:[33, 22, 11, 44, 55, 66, 77, 88, 99, 1010]
chatGPT回答基本正確了(le)。
不(bu)過(guo),有時(shi)候提問同樣的(de)(de)問題(ti),chatGPT會(hui)給出(chu)很多種錯誤(wu)答案,有的(de)(de)是(shi)(shi)編(bian)譯錯誤(wu),有的(de)(de)是(shi)(shi)串行執(zhi)行不(bu)符合(he)我的(de)(de)要(yao)求(qiu),以至于我不(bu)得不(bu)重新提問了好(hao)幾(ji)次。這個(ge)過(guo)程(cheng)中(zhong),我沒有好(hao)好(hao)做截圖(tu)。但我發現它其實就(jiu)是(shi)(shi)在(zai)給出(chu)一些網絡上常(chang)見的(de)(de)博客寫的(de)(de)解法,很多時(shi)候都是(shi)(shi)錯誤(wu)的(de)(de),或者(zhe)說其實并(bing)不(bu)對(dui)應我對(dui)它的(de)(de)訴求(qiu)。
不足之處
chatGPT針對代碼(ma)方面的(de)(de)回答(da)做了很(hen)多(duo)特(te)殊(shu)的(de)(de)調優,所以(yi)它能夠作為一個幫助程序(xu)員編碼(ma)的(de)(de)工具。很(hen)多(duo)時候,我都(dou)更愿意問chatGPT而不是(shi)自(zi)己去(qu)谷(gu)歌答(da)案。這已經證明了我足夠信任它。如果要說有什么不足之處(chu),我想到以(yi)下幾點:
- 面對不常見的復雜代碼設計題,沒有做到優秀的水平,但相信未來不斷地訓練后,會變得更加完善。
- 它不能全程輔助你編碼,這一點比不上Copilot。
- 它不能閱讀你整個項目的代碼,無法和Copilot一樣有強大的上下文能力。當然你可以手動給他很多上下文代碼,但是相比Copilot肯定還是差距很大。畢竟Copilot可能閱讀了你整個項目后給出一些建議。
New Bing
引(yin)用New Bing官網的(de)介(jie)紹,New Bing 就像您(nin)在(zai)搜索(suo)?絡(luo)時身邊有?個研究助理、個?規劃(hua)師(shi)和創(chuang)(chuang)意(yi)伙伴。您(nin)可以問你的(de)實(shi)際問題,當(dang)你提出復雜的(de)問題時,Bing 會給你詳細的(de)答復。 得(de)到(dao)?個實(shi)際的(de)答案(an)。 Bing 會查看?絡(luo)上的(de)搜索(suo)結(jie)果(guo),為(wei)您(nin)提供?個總結(jie)性的(de)答案(an)。 要有創(chuang)(chuang)意(yi)。當(dang)你需要靈感時,必應可以幫(bang)你寫詩、寫故事,甚?為(wei)你創(chuang)(chuang)造?個全新的(de)形象。
總的(de)(de)來說,你可以理解為New Bing是一個(ge)chatGPT + Bing搜索引擎內網絡(luo)信息 的(de)(de)加強版對話(hua)機(ji)器人。
使用體驗
New Bing目前(qian)已經和諧了國(guo)內的(de)IP,用國(guo)內IP訪(fang)問任何new Bing相關的(de)網(wang)頁會(hui)強制跳(tiao)轉的(de)Bing搜(sou)索首頁。所以需(xu)要打開(kai)科學工具后使用,本文不具體(ti)展(zhan)開(kai)。網(wang)上(shang)有很多攻(gong)略可查。
編碼能力
我們(men)仍然(ran)使(shi)用剛(gang)才的測試例子(zi)(單測+Kotlin并發(fa)接口)來(lai)測試NewBing的代碼編寫(xie)能力(li)。Bing有(you)三種對話(hua)模式可選(xuan)(xuan),需要選(xuan)(xuan)到“更多 精確”,這種模式下,它會認認真(zhen)真(zhen)地給(gei)我們(men)寫(xie)代碼。
獨立編寫:單例模式
我(wo)們(men)直接提(ti)問,請看截圖。
給(gei)出的代碼比較基礎,讓(rang)我們追問下,讓(rang)他修改。
它修改的很快,現在(zai)代碼基本已經成型了。讓我們(men)和之前測試chatGPT提問(wen)一(yi)模一(yi)樣的問(wen)題。
可以看到(dao),頁面(mian)下(xia)方,他還會給一些符合(he)你問題上(shang)下(xia)文(wen)的推薦提示。
獨立編寫:Kotlin+Reactor設計并發接口
讓(rang)我們來欣賞(shang)一下NewBing面對復雜問題的編碼能力。
將它寫的代碼放入Demo,編(bian)譯不通過。
繼續質問它
最終它完成了代(dai)碼,但(dan)是寫法在我看來有些奇怪。整體來看,和chatGPT的使用體驗(yan)是相似的。
不足之處
NewBing和chatGPT相比(bi)(bi),給我感覺(jue)不(bu)分伯(bo)仲,面對不(bu)常見的(de)(de)復雜(za)代碼(ma)設計題(ti),依(yi)然(ran)有一些力不(bu)從心。并且,不(bu)知道是(shi)不(bu)是(shi)使用了搜索引(yin)擎內的(de)(de)原因,比(bi)(bi)chatGPT更容易出現錯誤的(de)(de)結(jie)果。
相比Github Copilot,則(ze)和chatGPT一(yi)樣,由于沒(mei)有足夠的代碼上下文,對你(ni)的幫助遠沒(mei)有Copilot那么好(hao)用(yong)。
Cursor.so
Cursor.so 是(shi) OpenAI 最近推出(chu)的一(yi)款IDE,它(ta)可以幫助你提(ti)供想法并(bing)編寫(xie)代碼。最值(zhi)得(de)一(yi)提(ti)的是(shi),它(ta)是(shi)一(yi)款免費軟件(jian),OpenAI 還承諾將持續(xu)更新(xin)和改進,為(wei)用戶帶來更多新(xin)的功能(neng)和體(ti)驗。
為(wei)什么它會受到關注,主(zhu)要是因為(wei)它能夠免費(fei)使用(yong)內(nei)置了類似Github Copilot的(de)插件(jian),畢竟(jing)Copilot是付費(fei)軟件(jian),很多小伙伴(ban)還沒(mei)法免費(fei)體(ti)驗到。
編碼能力
獨立編寫:單例模式
cursor主要有(you)兩(liang)個功能,一個可以自(zi)動生成代碼(ma)edit,一個是(shi)根據當前代碼(ma)進行聊天chat。各自(zi)有(you)獨立的快捷鍵來(lai)啟(qi)動。
我們讓他寫一個Java中(zhong)(zhong)的單例模(mo)式類,快(kuai)捷(jie)鍵(jian)command+K,輸入中(zhong)(zhong)文(wen) ”寫一個線程安(an)全(quan)的單例模(mo)式“,它給出了如(ru)下答案。
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
寫(xie)的不錯,帶上(shang)了雙重校驗和volatile,很(hen)像是(shi)熟(shu)讀面試(shi)(shi)代碼的面試(shi)(shi)者。既然本段(duan)代碼沒什么細節(jie)問題(ti),那就試(shi)(shi)用下(xia)聊天(tian)功能(neng)command+L
解釋(shi)的和chatGPT以及NewBing都類(lei)似,并且(qie)還支(zhi)持聊天歷史記錄展示。
補全項目現有代碼
讓他補全之(zhi)前Copilot補全過的同(tong)樣(yang)測試代碼,寫入threadLocal變量的代碼,可以(yi)完成(cheng)。
當我(wo)準備更進一(yi)步問問他(ta)對于(yu)這個代碼怎么(me)看的時(shi)候(hou),它服(fu)務開始了長時(shi)間的不穩定。
不足之處
服務不穩定
不支持插件
沒有插件(jian)市場,甚至沒有集(ji)成(cheng)版本控制,比如Git,在UI中(zhong)沒法查看改動的代碼(ma),所以幾乎已經告別了開(kai)發大型(xing)項(xiang)目了。只要(yao)稍微做(zuo)一些(xie)代碼(ma)改動,你(ni)就會忘(wang)記了代碼(ma)的改動是(shi)哪里。
基礎功能缺失
比如打開(kai)一(yi)個項(xiang)目(mu)文件(jian)夾后(hou)居然不支持關(guan)閉,文件(jian)類型(xing)沒有高亮區分,整體用下(xia)來感覺Cursor.so還(huan)處于一(yi)個很早期的(de)階段。
總結
最后,總結下這幾(ji)個產(chan)品作(zuo)為(wei)一個AI輔助編碼工具的優缺(que)點。
Github Copilot:
優點:
- 可以根據提示自動生成代碼,提高開發效率。
- 可以學習你項目中的代碼風格,獲取足夠多的上下文,并根據其生成代碼。
- 支持多種編程語言,適用范圍廣。
缺點:
- 可能會存在隱私問題
chatGPT和New Bing:
優點:
- 隨時隨地可用,不依賴代碼項目,是你查詢谷歌時的完美替代品。
缺點:
- 它不能全程輔助你編碼,這一點比不上Copilot,并且無法和Copilot一樣有強大的上下文能力。
- 對于復雜的代碼邏輯,理解能力未必能讓你滿意。
Cursor.so
優點:
- 免費的同時能夠體驗AI輔助編程,就是最大的優點
缺點:
- 基礎功能缺失,不能稱之為一個可靠的IDE
- 服務不穩定
一句話總結,如(ru)果(guo)你(ni)希望(wang)將這幾個產品用于輔(fu)助你(ni)編(bian)(bian)程,提高編(bian)(bian)碼(ma)效率,使(shi)(shi)用Github Copilot結合(he)chatGPT是一個可行(xing)的(de)方式。你(ni)可以在編(bian)(bian)寫代(dai)碼(ma)時使(shi)(shi)用Github Copilot,遇到問(wen)題(ti)時再求助chatGPT。如(ru)果(guo)暫時不(bu)(bu)(bu)想為Github Copilot付(fu)費,可以只(zhi)使(shi)(shi)用chatGPT。不(bu)(bu)(bu)過,由(you)于Cursor.so的(de)使(shi)(shi)用體驗(yan)不(bu)(bu)(bu)夠好(hao),且不(bu)(bu)(bu)易(yi)替代(dai)Github Copilot,建議還是等待Cursor.so之(zhi)后(hou)的(de)版本(ben)。

本文是我深度體驗了Github Copilot, ChatGPT等產品后,對于這些AI輔助開發工具的一次橫向評測。寫本文的初衷是幫助大家快速篩選出一款合適你的AI輔助工具。相信我,**請不要再懷疑**這些工具是否能給你帶來效率提升。當你嘗試使用后,很快就會習慣它們,甚至是離不開它們。