写在最初
这几个月AI相关新闻的火爆程度咱们都现已看见了,作为一个被裹挟在AI年代浪潮中的程序员,在这几个月里我也是反常振奋和焦虑。乃至都振奋的不想拖更了。不仅仅振奋于AI关于咱们生产力的全面提升,也焦虑于Copilot等AI辅佐编码东西,会将程序员这个工种和咱们所了解的传统软件开发流程彻底颠覆,用计算机的极高功率碾压人类的低功率。
当然这些也都是后话了,咱们目前能做的,便是坚持敏锐的嗅觉,测验去迎候AI年代的来临。做“未来世界的幸存者”。
本文是我深度体会了Github Copilot, ChatGPT等产品后,关于这些AI辅佐开发东西的一次横向评测。写本文的初衷是协助咱们快速筛选出一款适宜你的AI辅佐东西。信任我,请不要再怀疑这些东西是否能给你带来功率提升。当你测验运用后,很快就会习惯它们,乃至是离不开它们。
本文评测的东西有:
- Github Copilot
- ChatGPT(GPT-3.5)
- New Bing
- Cursor.so
这些东西能够结合起来运用,提升你的开发功率。所以这些东西之间并不是互斥联系。文章的最终会给出总结以及我的一些主意。
Github Copilot
Github Copilot是由Github和OpenAI合作推出的一个人工智能代码辅佐东西,采用了OpenAI的GPT技术,能够为开发人员供给实时的代码提示和生成功用,相似于一个AI帮手,协助开发人员更快速、更方便地编写代码。
当时的Github Copilot依据GPT-3模型,它能够分析上下文并依据已有的代码和注释来揣度出应该写什么代码。经过运用Github Copilot,开发人员能够削减手动输入代码的时间,进步代码的质量和功率。它支撑多种编程言语,如Python、JavaScript、TypeScript、Ruby等,并能够与干流的集成开发环境(IDE)和文本编辑器合作运用。
运用体会
我现已深度运用了Copilot接近一个月,但每当我和同事朋友们聊到Copilot的运用体会,以及它在哪方面能够进步我的功率时,我仍很难用言语去描述,我只能粗略的总结为下面几个结论:
- 编写你了解的言语时,他仅能协助你削减一些重复模板代码的编写。编写你不了解的言语时,他能够精确揣度你的意图,直接生成代码,免除了查询如何运用API的耗时工作
- 它顺着你的心流生成片段代码,但很难从0开端为你发明整段代码,即使它是能够生成整段代码的,可是也常常是需求你手艺修正的。
接下来看一下我常用的几种运用办法。
1. 依据上下文生成代码
它能够依据函数名,类名,注释,来揣度你想写的代码,帮你填充。
2. 依据代码生成注释
反过来,它能够测验了解你的代码,为你生成注释,你只需求给他一个 // 前缀
3. 帮你起变量名
它能够帮你器变量名,这可是许多英语不好的程序员的大福音。究竟,编码的30%时间,是在想变量名。
4. 和你谈天,当然,是聊代码!
你没有听错,copilot能够在你的代码里谈天,可是明显不能和他唠家常。它并不是chatGPT,无法给你知识答复(应该是被成心限制了交流规模),只会和你扯皮。
正确的运用办法是让它和你讨论你写代码,他会总结你的上下文代码,而且给你一个它以为“合理”的解释。
以上便是我常用的几种办法,我查阅了许多材料,根本上面涵盖了大部分操作办法。当然,或许还有我没挖掘到的运用办法。
编码才能
说完运用体会,我想要引出我本次横向评测的一个评测规范,便是经过附近的试题,看下这几个东西的编码才能有何差异,给咱们直观地比照。咱们就先从Copilot开端。
独立编写:单例形式
我给它们设定的标题是独立写一个单例形式,这个标题是国内Java开发者“常考题”,里边除了根本的代码,还有许多细节需求留意,也能够很好的用来向AI们发问,看看它们是否真实地了解它们写的内容。
让咱们欢迎榜首位选手,Github Copilot。
上面的编码动画也是我以为最契合我日常运用copilot的案例,从创立类文件后,copilot帮我主动生成了私有变量,私有构造办法,以及获取单例的公有办法。在写公有办法的期间,它最开端的代码没有考虑两层检查锁,我给了它一些提示,它补全了剩下的代码。
需求留意的是,因为单例形式在网上有太多的学习材料,Copilot必定也吸取了许多优异代码,才能写的如此高效。并不代表一切杂乱的代码题都能够帮你主动补全,并确保正确性。所以咱们谨慎看待它的独立编码才能,也不要过分乐观。
补全项目现有代码
第二个测验,咱们让它补全项目的现有代码,我拿一个我自己写的代码举例,其中有一个DiffDTO实体类,有一个addDiffDTO办法一行都没写,需求补全。
能够看到它主动补全了代码,而且会用到上面已有的办法和变量。这也是它的强项,依据上下文信息揣度代码该怎样补全,写出来的代码质量很高,不能说每次都可直接运用,但80%的状况下你是无需做大起伏修正的。
不足之处
Github Copilot有不足之处吗?我想了想,有以下几点:
- 一切代码上下文经过https传到云端(微软的服务器),虽然我通读了微软的隐私维护方针,它承诺不会有任何代码被泄露。但放在国内网络安全大环境下,这样的处理数据办法,在国内公司内运用很或许会遭到影响,需求用户留意。
- 关于杂乱的代码逻辑,比方连你都一下子看不懂的屎山代码,它的了解才能相同欠缺。它拿手的是网络上有许多揭露材料的代码段落和完成办法。
ChatGPT
让咱们来看下一位选手,最近火出圈的chatGPT。
chatGPT是一款依据GPT模型的谈天机器人,它能够像人类相同进行自然言语对话。它不仅能够答复用户的各种问题,还能够学习和了解用户的需求,供给更恰当的答复和主张。
得益于其依据GPT模型的强壮言语处理才能。GPT模型能够了解代码中的自然言语描述部分。其次,chatGPT在练习阶段运用了许多的代码相关文本语料,包括代码库、API文档、程序员交流论坛等,因而具有一定的代码了解和辨认才能。此外,chatGPT还能够经过不断的学习和自我优化,逐渐进步其关于代码相关问题的处理才能和精确度。
我文中运用的chatGPT是GPT-3.5版别。咱们直接进入正题,看看它的编码才能。因为是对话办法,所以咱们需求向它描述咱们的需求。
编码才能
独立编写:单例形式
我:
请用Java写一个单例形式
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();
写的不错,很有面试题刷多了的开发人员的风格,写了经常会被问到的两层校验锁,可是没有用volatile,让咱们持续发问。
我:
为何要用两层检查确定?
ChatGPT:
两层检查确定(double-checked locking)是一种常见的单例形式的完成办法。它的目的是在不运用同步锁的状况下完成单例形式的线程安全,防止了每次获取目标实例时都进行同步锁的开支。可是,这种完成办法也有或许存在线程安全问题。
详细来说,两层检查确定是在 getInstance 办法中加入了两层检查,即先判别实例是否现已存在,假如不存在再进行同步锁的操作。这样能够削减同步锁的开支,进步了功率。
可是,在多线程环境下,两层检查确定也有或许呈现线程安全问题,原因是在某些状况下,多个线程或许一起经过榜首重检查,然后一起进行同步锁的操作,导致呈现多个实例。
为了防止这种状况,需求将 getInstance 办法设置为 synchronized 关键字,确保在同一时间只有一个线程能够拜访该办法,然后防止多个实例的呈现。可是,这样会带来一定的功用开支,因为每次拜访 getInstance 办法都需求进行同步锁操作。
讲得不错,但很难不让人觉得是看多了面试文章。咱们顺着volatile的问题持续问问他为啥不写volatile。
我:
上面的代码仍然有漏洞,能够优化下吗
ChatGPT:
是的,即使运用了两层检查确定,上面的代码仍然有漏洞,也便是或许呈现线程安全问题。为了解决这个问题,能够运用 Java5 中引入的 volatile 关键字,经过制止指令重排来确保线程安全。
优化后的代码如下:
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; } } }
上面的代码中,咱们将 instance 字段添加了 volatile 关键字,这样能够确保在多线程环境中,instance 变量的读取和赋值操作是原子性的,即确保了线程的可见性和有序性,然后防止了呈现多个实例的状况。
留意,这儿的 volatile 关键字只是作用于 instance 字段自身,而不是整个 getInstance 办法。这样做能够减小同步锁的规模,进步了功率。
它自己想到并解决了volatile的细节问题,咱们再结合他写的代码,问问一些面试官会问的问题。
我:
上面的代码中,同步的是一个Singleton.class,能够用int变量替代吗
ChatGPT:
不能够,运用 int 变量替代 Singleton.class 进行同步是不可行的。
因为在 Java 中,每个类都有一个仅有的 Class 目标,多个线程运用同一个类的 Class 目标进行同步,能够确保线程安全。而运用 int 变量进行同步,因为 int 是根本数据类型,它在内存中的每一份拷贝都或许不同,因而不能确保多个线程都运用同一个 int 变量进行同步,然后无法确保线程安全。
此外,运用 Class 目标进行同步还有一个好处,即能够确保实例变量的可见性,因为同步代码块中的读写操作都是在同一条线程中履行的。
因而,运用 Singleton.class 目标进行同步是确保线程安全的最佳办法,不主张运用其他办法进行同步。
答复的不错,了解了我提示语中的 Singleton.class 是指的它自己代码中的类,最终问一个volatile的细节。
我:
上面的代码中,假如不运用volatile,有或许会呈现重排序问题,能详细翻开讲讲吗
ChatGPT:
是的,假如没有运用volatile关键字,就有或许呈现重排序问题。
在Java中,编译器和处理器都会对指令进行重排序,这是为了优化程序功用。例如,在一个if句子中,为了防止每次都进行判别,编译器或许会将判别条件提前到if句子外面。在单线程环境下,这种重排序是没有问题的,因为程序履行的成果是相同的。可是,在多线程环境下,重排序或许会导致线程安全问题。
考虑下面的代码:
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; } } }
在这个代码中,假如没有运用volatile关键字,就有或许呈现下面的重排序:
- 线程A调用getInstance()办法,发现instance为null,进入同步块。
- 线程B调用getInstance()办法,因为instance还没有被初始化,也发现instance为null,也进入同步块。
- 线程A履行instance = new Singleton(),在履行该句子时,或许会被重排序为下面这样:
memory = allocate(); //1:分配目标的内存空间 ctorInstance(memory); //2:初始化目标 instance = memory; //3:将目标赋值给instance变量
这个重排序是答应的,因为在单线程环境下,这个次序和本来的次序没有差异。可是,在多线程环境下,这个重排序或许会导致线程安全问题,因为线程B此时检查instance不为null,就会直接回来instance,这个instance还没有被初始化,就会导致过错。
为了防止这种状况,能够运用volatile关键字来确保instance变量的可见性,然后防止重排序问题。
上面的对话,是一个典型的面试题,网上有许多文章也会顺着这个思路写下来,所以chatGPT答复的称心如意,并不代表在一切代码优化方面都能答复的这么细节和流通。但不得不说,假如你榜首次用chatGPT,你仍是会被它的才能和思路所震惊。
接下来咱们让chatGPT解决一个kotlin中并发接口的规划,看看它关于不那么常见的杂乱问题的解决才能
独立编写:Kotlin+Reactor规划并发接口
这是我在实践开发过程中遇到的一个比较难写的代码比方,在kotlin中运用reactor结构来写一个批量查询接口,因为功用要求,该接口中的每个查询必须并发履行(线程池履行)。我把这个遇到的实践问题作为测验的case,让咱们来问问chatGPT。
我:
Kotlin中,如何用reactor结构写一个并发处理的办法,要求入参是List,办法中将字符串后面拼接相同的字符串,输出List
ChatGPT:
您能够运用Reactor结构中的
Flux
来完成这个要求。下面是一个示例代码:
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()
指定履行该函数的调度器,以完成并发履行的作用。最终,咱们运用
collectList()
办法等候一切异步操作完成并将成果作为List<String>
回来。需求留意的是,咱们运用了并发履行,因而成果次序或许与输入次序不同。假如您需求坚持成果次序与输入次序相同,能够运用
concatMap
办法替代flatMap
。
在它给出的代码上,我加入了一个随机休眠的函数来验证是否代码真的履行了异步,而且给了它入参 [0-9],然后履行,事实证明chatGPT给出的代码直接能够跑通,而且真实是并行履行的。给出成果:
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]
可是它的代码中运用了flatMap并没有确保回来成果List中的次序和入参次序相同,关于我的需求来说这是不可承受的,chatGPT在刚才的答复中考虑到了这个问题,告知了我解决方案,运用concatMap替代,所以我修正为concatMap,成果如下:
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,但其实问题很大。运用了concatMap后,实践上运用concatMap的这段代码并没有真实的并发履行,而是相似串行的办法,得到了成果。(本文不详细翻开解释为何运用了concatMap后会有如此作用)
已然答案和我想要的有偏差,那就持续诘问chatGPT。
我:
这段代码如安在确保回来值次序和入参次序相同的状况下,真实的并发履行?
ChatGPT:
能够运用flatMapSequential操作符替换concatMap操作符来完成真实的并发履行,而且确保回来值次序和入参次序相同。
修正后的代码如下:
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答复根本正确了。
不过,有时分发问相同的问题,chatGPT会给出许多种过错答案,有的是编译过错,有的是串行履行不契合我的要求,以至于我不得不从头发问了好几次。这个过程中,我没有好好做截图。但我发现它其实便是在给出一些网络上常见的博客写的解法,许多时分都是过错的,或者说其实并不对应我对它的诉求。
不足之处
chatGPT针对代码方面的答复做了许多特殊的调优,所以它能够作为一个协助程序员编码的东西。许多时分,我都更乐意问chatGPT而不是自己去谷歌答案。这现已证明了我满足信赖它。假如要说有什么不足之处,我想到以下几点:
- 面临不常见的杂乱代码规划题,没有做到优异的水平,但信任未来不断地练习后,会变得愈加完善。
- 它不能全程辅佐你编码,这一点比不上Copilot。
- 它不能阅读你整个项目的代码,无法和Copilot相同有强壮的上下文才能。当然你能够手动给他许多上下文代码,可是相比Copilot必定仍是差距很大。究竟Copilot或许阅读了你整个项目后给出一些主张。
New Bing
引证New Bing官网的介绍,New Bing 就像您在查找⽹络时身边有⼀个研究助理、个⼈规划师和创意伙伴。您能够问你的实践问题,当你提出杂乱的问题时,Bing 会给你详细的答复。 得到⼀个实践的答案。 Bing 会检查⽹络上的查找成果,为您供给⼀个总结性的答案。 要有创意。当你需求创意时,必应能够帮你写诗、写故事,甚⾄为你发明⼀个全新的形象。
总的来说,你能够了解为New Bing是一个chatGPT + Bing查找引擎内网络信息 的加强版对话机器人。
运用体会
New Bing目前现已调和了国内的IP,用国内IP拜访任何new Bing相关的网页会强制跳转的Bing查找首页。所以需求翻开科学东西后运用,本文不详细翻开。网上有许多攻略可查。
编码才能
咱们仍然运用刚才的测验比方(单测+Kotlin并发接口)来测验NewBing的代码编写才能。Bing有三种对话形式可选,需求选到“更多 精确”,这种形式下,它会仔仔细细地给咱们写代码。
独立编写:单例形式
咱们直接发问,请看截图。
给出的代码比较基础,让咱们诘问下,让他修正。
它修正的很快,现在代码根本现已成型了。让咱们和之前测验chatGPT发问一模相同的问题。
能够看到,页面下方,他还会给一些契合你问题上下文的引荐提示。
独立编写:Kotlin+Reactor规划并发接口
让咱们来赏识一下NewBing面临杂乱问题的编码才能。
将它写的代码放入Demo,编译不经过。
持续质问它
最终它完成了代码,可是写法在我看来有些奇怪。全体来看,和chatGPT的运用体会是相似的。
不足之处
NewBing和chatGPT相比,给我感觉不分伯仲,面临不常见的杂乱代码规划题,仍然有一些力不从心。而且,不知道是不是运用了查找引擎内的原因,比chatGPT更容易呈现过错的成果。
相比Github Copilot,则和chatGPT相同,因为没有满足的代码上下文,对你的协助远没有Copilot那么好用。
Cursor.so
Cursor.so 是 OpenAI 最近推出的一款IDE,它能够协助你供给主意并编写代码。最值得一提的是,它是一款免费软件,OpenAI 还承诺将持续更新和改进,为用户带来更多新的功用和体会。
为什么它会遭到重视,首要是因为它能够免费运用内置了相似Github Copilot的插件,究竟Copilot是付费软件,许多小伙伴还无法免费体会到。
编码才能
独立编写:单例形式
cursor首要有两个功用,一个能够主动生成代码edit,一个是依据当时代码进行谈天chat。各自有独立的快捷键来发动。
咱们让他写一个Java中的单例形式类,快捷键command+K,输入中文 ”写一个线程安全的单例形式“,它给出了如下答案。
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;
}
}
写的不错,带上了两层校验和volatile,很像是熟读面试代码的面试者。已然本段代码没什么细节问题,那就试用下谈天功用command+L
解释的和chatGPT以及NewBing都相似,而且还支撑谈天历史记录展现。
补全项目现有代码
让他补全之前Copilot补全过的相同测验代码,写入threadLocal变量的代码,能够完成。
当我预备更进一步问问他关于这个代码怎样看的时分,它服务开端了长期的不稳定。
不足之处
服务不稳定
不支撑插件
没有插件商场,乃至没有集成版别操控,比方Git,在UI中无法检查改动的代码,所以几乎现已告别了开发大型项目了。只需稍微做一些代码改动,你就会忘记了代码的改动是哪里。
基础功用缺失
比方翻开一个项目文件夹后竟然不支撑封闭,文件类型没有高亮区别,全体用下来感觉Cursor.so还处于一个很前期的阶段。
总结
最终,总结下这几个产品作为一个AI辅佐编码东西的优缺点。
Github Copilot:
长处:
- 能够依据提示主动生成代码,进步开发功率。
- 能够学习你项目中的代码风格,获取满足多的上下文,并依据其生成代码。
- 支撑多种编程言语,适用规模广。
缺点:
- 或许会存在隐私问题
chatGPT和New Bing:
长处:
- 随时随地可用,不依赖代码项目,是你查询谷歌时的完美替代品。
缺点:
- 它不能全程辅佐你编码,这一点比不上Copilot,而且无法和Copilot相同有强壮的上下文才能。
- 关于杂乱的代码逻辑,了解才能未必能让你满意。
Cursor.so
长处:
- 免费的一起能够体会AI辅佐编程,便是最大的长处
缺点:
- 基础功用缺失,不能称之为一个可靠的IDE
- 服务不稳定
一句话总结,假如你希望将这几个产品用于辅佐你编程,进步编码功率,运用Github Copilot结合chatGPT是一个可行的办法。你能够在编写代码时运用Github Copilot,遇到问题时再求助chatGPT。假如暂时不想为Github Copilot付费,能够只运用chatGPT。不过,因为Cursor.so的运用体会不够好,且不易替代Github Copilot,主张仍是等候Cursor.so之后的版别。