paint-brush
ChatGPT vs Copiloto vs Programadores: ¿Quién se destaca?por@anywhichway
7,288 lecturas
7,288 lecturas

ChatGPT vs Copiloto vs Programadores: ¿Quién se destaca?

por Simon Y. Blackwell20m2023/01/26
Read on Terminal Reader

Demasiado Largo; Para Leer

Este artículo compara las sugerencias de OpenAI [ChatGPT] y [Codex] con Microsoft [Copilot] con el código escrito a mano con un enfoque en los algoritmos que son clave para procesar grandes volúmenes de datos de manera eficiente. Descubre que la IA produce código promedio o defectuoso. y proporciona sugerencias para la mejora general.
featured image - ChatGPT vs Copiloto vs Programadores: ¿Quién se destaca?
Simon Y. Blackwell HackerNoon profile picture

Con la mayor disponibilidad de entornos sin código/de código bajo y el advenimiento de la IA aplicada a la programación, se han publicado varios artículos que dicen que el final está cerca para los programadores, ya sea porque los usuarios finales crearán sus propias aplicaciones o porque la IA lo hará. escribir aplicaciones a partir de descripciones. Quizás… pero todavía no.


En este artículo, exploro el estado actual de la codificación asistida por IA al comparar las sugerencias de OpenAI ChatGPT y Codex con Microsoft Copilot con el código escrito a mano. Las IA producen código promedio o defectuoso.


Dado que hoy en día se presta tanta atención al procesamiento de grandes volúmenes de datos (incluidos los grandes modelos de lenguaje utilizados para ChatGPT y Copilot), me concentro en los algoritmos que son clave para procesar los datos de manera eficiente. ¡Con suerte, las IA "entienden" su propia naturaleza mejor que el código en general!


Estos algoritmos son:


  • intersección

  • producto cruzado

  • memorizando


Proporcioné a ChatGPT instrucciones para el formulario:


Muéstrame <alguna descripción de la función>


Proporcioné Codex con entrada:


<alguna descripción de la función>


Proporcioné comentarios a Copilot en un IDE de WebStorm


// <alguna descripción de la función>


Las descripciones de las funciones eran las mismas para cada uno.

Metodología de prueba

Utilicé Jasmine como el arnés de prueba de unidad complementado con un código similar al que se encuentra en Benchtest para obtener velocidad más uso de memoria y CPU.

Intersección

Muéstrame una función de JavaScript que tome un número variable de matrices y devuelva su intersección.

OpenAI ChatGPT

 function intersection(...arrays) { if (arrays.length < 2) { throw new Error("At least 2 arrays are required!"); } let result = new Set(arrays.shift()); for (let arr of arrays) { result = new Set([...result].filter(x => arr.includes(x))); } return [...result]; }

Códice OpenAI

 function intersection(arrays) { var result = []; var args = Array.prototype.slice.call(arguments); var arrays = args.slice(0, args.length - 1); var callback = args[args.length - 1]; var minLength = Math.min.apply(null, arrays.map(function(arr) { return arr.length; })); for (var i = 0; i < minLength; i++) { var item = arrays[0][i]; var isInAll = arrays.every(function(arr) { return arr.indexOf(item) !== -1; }); if (isInAll) { result.push(item); } } return callback(result); }

copiloto de microsoft

 const intersection = (...arrays) => { const [first, ...rest] = arrays; return first.filter(x => rest.every(y => y.includes(x))); }


ChatGPT asumió que proporcionar menos de una matriz no es válido. Puedo imaginar situaciones en las que proporcionar un argumento podría ser válido.


Mediante el uso de un conjunto , ChatGPT garantiza que no existan duplicados en los resultados. Se supone que la intersección es una operación establecida y los duplicados deben eliminarse. El código de Copilot devolverá una matriz que puede contener duplicados.


Ni ChatGPT ni Copilot ordenan los argumentos originales de forma ascendente por longitud. ¡Esta es una optimización trivial que podría marcar una gran diferencia!


Si algún argumento tiene una longitud cero, no hay intersección; independientemente, acorta los bucles porque la intersección máxima es la misma que el argumento de matriz más corto.


Codex proporcionó un código complejo que, según mi conocimiento, parecía bastante eficiente. Sin embargo, su uso de argumentos es oscuro e implica un estilo de programación algo arcaico.


Tenga en cuenta que se debe proporcionar una devolución de llamada como último argumento para devolver la intersección al código de llamada, pero el argumento no se muestra.


Luego le pedí a ChatGPT y Copilot que me dieran una función eficiente.


Muéstrame una función de JavaScript eficiente que tome un número variable de matrices y devuelva su intersección.

ChatGPT

 function intersection(...arrays) { if (arrays.length < 2) { throw new Error("At least 2 arrays are required!"); } let result = new Set(); for (let item of arrays[0]) { let include = true; for (let i = 1; i < arrays.length; i++) { if(!arrays[i].includes(item)){ include = false; break; } } if(include){ result.add(item); } } return [...result]; }


Copilot generó el mismo código que la primera solicitud.


ChatGPT también agregó un comentario que decía que la función no funcionaría como se esperaba para los objetos, aunque podría decirse que sí. Entonces, pedí una función que admitiera objetos:


Muéstreme una función de JavaScript eficiente que tome un número variable de arreglos que contengan objetos o primitivos y devuelva su intersección.


 function intersection(...arrays) { if (arrays.length < 2) { throw new Error("At least 2 arrays are required!"); } let result = new Set(); for (let item of arrays[0]) { let include = true; for (let i = 1; i < arrays.length; i++) { if(!arrays[i].find(x => JSON.stringify(x) === JSON.stringify(item))){ include = false; break; } } if(include){ result.add(item); } } return [...result]; } This function takes an arbitrary number of arrays as arguments and first checks if at least 2 arrays are passed. Then it uses a nested loop. The outer loop iterates over the first array and the inner loop iterates over the remaining arrays. It compares each item of the first array with each item of the remaining arrays using Array.prototype.find() method and JSON.stringify() to check if the objects are the same. If an item is found in all arrays, it is added to


ChatGPT encadena objetos para compararlos. Hay una serie de problemas con esto:


  1. Dos objetos que tienen la misma representación de cadena pueden ser legítimamente objetos separados.


  2. Incluso si la encadenación es válida, los diferentes órdenes de propiedad darán como resultado cadenas diferentes y los objetos podrían representar la misma entidad.


  3. JSON.stringify seguido de una comparación de cadenas es una operación lenta.


Y ahora, ¡la verdadera prueba! A continuación, se encuentran los resultados de las pruebas comparativas de velocidad y memoria utilizando código generado por ChatGPT, Codex, Copilot y las dos bibliotecas de intersección más rápidas disponibles a través de NPM, fastArrayIntersect e intersector .


El punto de referencia intersectó 3 matrices idénticas de 10 000 enteros cada una y una matriz final de 5000 enteros con un tamaño de muestra de 100. También se ejecutó una prueba para confirmar que las funciones devolvían conjuntos cuando existían entradas duplicadas en las matrices de origen.


Fuente

Duplicados

Montón usado

Seg. de operaciones

CPU de usuario

CPU del sistema

ChatGPT

No

39768

6.65

152170

780

Códice

No

5475888

16.00

69070

160

Copiloto

30768

4.16

345190

940

intersector

No

37304

73.02

21020

160

Intersección de matriz rápida

No

42064

20.33

18990

630


Copilot utiliza la menor cantidad de almacenamiento dinámico, pero también es el más lento y el que consume más CPU.


ChatGPT es el que menos utiliza la CPU, pero también es lento.


Fast Array Intersect es de 3 a 5 veces más rápido que Copilot y Chat GPT y un 20 % más rápido que Codex, a la vez que consume menos CPU.


Intersector es 15 veces más rápido que ChatGPT y Copilot, 4 veces más rápido que Codex y más de 3 veces más rápido que Fast Array Intersect, además de tener la menor utilización de CPU.


En resumen:


  1. Ni ChatGPT ni Copilot generan código eficiente.


  2. ChatGPT hace suposiciones que pueden no ser válidas.


  3. Copilot genera código que no produce un conjunto cuando sus argumentos contienen valores duplicados.


  4. Codex produce un rendimiento razonable, aunque un código oscuro.


  5. Los programadores ganan.


Tanto ChatGPT como Copilot podrían mejorarse aplicando su "inteligencia" para hacerle al usuario algunas preguntas aclaratorias, por ejemplo, ¿busca velocidad o eficiencia de la memoria, desea requerir al menos dos argumentos?

Producto cruzado

Para aclarar el objetivo de esta sección, estaba buscando todas las combinaciones posibles de elementos en matrices, no un producto cruzado aritmético de vectores.


Muéstreme una función de JavaScript para productos cruzados cartesianos.

OpenAI ChatGPT

 function cartesianCrossProduct(v1, v2) { var x = v1.y*v2.z - v1.z*v2.y; var y = v1.z*v2.x - v1.x*v2.z; var z = v1.x*v2.y - v1.y*v2.x; return {x: x, y: y, z: z}; }

Códice OpenAI

 function crossProduct(arrays) { var result = []; var args = Array.prototype.slice.call(arguments); var arrays = args.slice(0, args.length - 1); var callback = args[args.length - 1]; var maxLength = Math.max.apply(null, arrays.map(function(arr) { return arr.length; })); for (var i = 0; i < maxLength; i++) { var item = arrays[0][i]; var isInAll = arrays.every(function(arr) { return arr.indexOf(item) !== -1; }); if (isInAll) { result.push(item); } } return callback(result); }

copiloto de microsoft

 const crossProduct = (...arrays) => { const [first, ...rest] = arrays; return first.flatMap(x => rest.map(y => [x, y])); }


Desafortunadamente, ChatGPT proporcionó una función para el producto cruzado aritmético. En mi opinión, debería haber proporcionado ambos tipos de funciones o pedir una aclaración.


Después de varios intentos en diferentes consultas, pude hacer que ChatGPT generara los tres siguientes:


 function crossProduct(...arrays) { if (arrays.length < 2) { throw new Error("At least 2 arrays are required!"); } let result = arrays[0].reduce((acc, x) => { return acc.concat(arrays.slice(1).reduce((acc, y) => { return acc.concat(y.map(z => [x].concat(z))); }, [])) }, []); return result; }
 function allCombinations(...args) { var result = []; var f = function(prefix, arr) { for (var i = 0; i < arr.length; i++) { var newPrefix = prefix.concat(arr[i]); result.push(newPrefix); if (args.length > 0) { f(newPrefix, args.shift()); } } } f([], args[0]); return result; }
 function crossProduct(...args) { var result = []; for(let i = 0; i < args.length; i++){ for(let j = i + 1; j < args.length; j++){ for(let ii = 0; ii < args[i].length; ii++){ for(let jj = 0; jj < args[j].length; jj++){ result.push([args[i][ii], args[j][jj]]); } } } } return result; }

Copiloto

 const crossProduct = (...arrays) => { const [first, ...rest] = arrays; return first.flatMap(x => rest.map(y => [x, y])); }


Al igual que con la intersección, Codex proporcionó una función usando un estilo de programación antiguo con un manejo de argumentos oscuro.


Quienes estén familiarizados con los productos cartesianos sabrán que pueden ser explosivos tanto desde la perspectiva de la utilización de la memoria como del rendimiento.


Las implementaciones ingenuas consumirán suficiente RAM para almacenar todas las permutaciones y no devolverán un resultado hasta que se hayan generado todas las permutaciones (lo que podría llevar mucho tiempo). Las funciones ChatGPT y Copilot sufren de estas deficiencias.


Entonces, posteriormente pedí una función de JavaScript eficiente en memoria para el producto cartesiano .

ChatGPT

 function cartesianProduct(...args) { const iterators = args.map(arg => arg[Symbol.iterator]()); const next = () => { const values = iterators.map(iterator => iterator.next().value); return values.length > 1 ? values : null; }; return { [Symbol.iterator]: () => ({ next }) }; }


Copilot proporcionó los mismos resultados que antes.


Supuse que la versión de "memoria eficiente" de ChatGPT usaba menos RAM que la primera. Sin embargo, sabía que no era óptimo. Después de varios intentos, se me ocurrió una función de JavaScript eficiente en memoria para productos cartesianos utilizando un generador .

ChatGPT

 function* cartesianProduct(...args) { const recursion = (arrays, i = 0) => { if (i === arrays.length - 1) { yield* arrays[i]; } else { for (let item of arrays[i]) { for (let result of recursion(arrays, i + 1)) { yield [item, ...result]; } } } } yield* recursion(args); }


Para ser honesto, me sorprendió gratamente esta respuesta de ChatGPT. Sin embargo, los intentos de ejecutar este código fallaron porque yield [item,...result] no está dentro de un generador, está dentro de una función anidada llamada recursion .


También recibí una ligera variación a la que perdí la consulta:


 function* crossProduct(...arrays) { if (arrays.length < 2) { throw new Error("At least 2 arrays are required!"); } function* recurse(depth, current) { if (depth === arrays.length) { yield current; } else { for (let item of arrays[depth]) { yield* recurse(depth + 1, current.concat(item)); } } } yield* recurse(1, [arrays[0]]); }


Una vez más, ChatGPT parecía estar haciendo algunas suposiciones sobre los argumentos.


Copilot devolvió el mismo código que antes. Ignoró por completo mi solicitud de un generador.


Y ahora, ¡la verdadera prueba! A continuación, se muestran los resultados de las pruebas comparativas de velocidad y memoria utilizando el código generado por ChatGPT, Copilot y los productos cartesianos más rápidos y eficientes en memoria de los que tengo conocimiento , CXProduct y Lazy Cartesian Product .


También se incluye una función de una línea que quizás ChatGPT o Copilot deberían haber devuelto (verá por qué incluí esto cuando revise los resultados).


 //https://stackoverflow.com/questions/12303989/cartesian-product-of-multiple-arrays-in-javascript const cartesian = (...a) => a.reduce((a, b) => a.flatMap(d => b.map(e => [d, e].flat())));


La prueba toma el producto cartesiano de 4 matrices con 10 elementos cada una. Por tanto, las funciones deberían producir un producto cartesiano de 10 * 10 * 10 * 10 elementos, es decir, 10.000.


Fuente

# Resultados

Montón usado

Seg. de operaciones

CPU de usuario

CPU del sistema

ChatGPT1

300

N / A

N / A

N / A

N / A

ChatGPT2

50

N / A

N / A

N / A

N / A

ChatGPT3

600

N / A

N / A

N / A

N / A

Memoria ChatGPT eficiente

N / A

N / A

N / A

N / A

N / A

Generador ChatGPT

1000

N / A

N / A

N / A

N / A

Códice

4

N / A

N / A

N / A

N / A

Copiloto

30

N / A

N / A

N / A

N / A

Una línea

10000

0

78.46

18150

0

CXProducto

10000

0

291.50

7330

0

LazyProducto

10000

17824

266.65

11560

150


En resumen:


  1. ChatGPT, Codex y Copilot no pueden generar código que produzca el resultado correcto para un producto cartesiano.


  2. ChatGPT a veces hace suposiciones que pueden no ser válidas, por ejemplo, requiere 2 argumentos.


  3. ChatGPT generó un código que, según decía, era más eficiente con la memoria, pero ni siquiera funcionaba.


  4. ¡Los programadores ganan!


Una vez más, ChatGPT podría mejorarse aplicando su "inteligencia" para hacerle al usuario algunas preguntas aclaratorias, por ejemplo, ¿busca velocidad o eficiencia de la memoria, desea requerir al menos dos argumentos? Sin embargo, antes de esto, ¡debe generar el código correcto!

Memoización

Habiendo aprendido mi lección con la intersección y los productos cartesianos, inmediatamente hice una pregunta refinada para memorizar.


Muéstrame una función de JavaScript eficiente que memorice objetos y argumentos primitivos.

OpenAI ChatGPT

 function memoize(fn) { let cache = new WeakMap(); return function(obj, ...args) { let key; if (obj) { if (!cache.has(obj)) { cache.set(obj, {}); } key = JSON.stringify([obj, ...args]); } else { key = JSON.stringify(args); } if (cache.has(key)) { return cache.get(key); } let result = fn.apply(this, [obj, ...args]); cache.set(key, result); return result; } }


ChatCPT generó un código no válido; un WeakMap debe usar un objeto como clave. El código se modificó de la siguiente manera para realizar pruebas.


 function memoize(fn) { let cache = new Map(); return function(obj, ...args) { let key; if (obj!=null) { if (!cache.has(obj)) { cache.set(obj, {}); } key = JSON.stringify([obj, ...args]); } else { key = JSON.stringify(args); } if (cache.has(key)) { return cache.get(key); } let result = fn.apply(this, [obj, ...args]); cache.set(key, result); return result; } }

Códice abierto de IA

 function memoize(func) { var cache = {}; return function() { var args = Array.prototype.slice.call(arguments); var key = JSON.stringify(args); if (cache[key]) { return cache[key]; } else { var result = func.apply(null, args); cache[key] = result; return result; } } }

copiloto de microsoft

 const memoize = (fn, cache = new Map()) => { return (...args) => { const key = JSON.stringify(args); if (cache.has(key)) { return cache.get(key); } else { const result = fn(...args); cache.set(key, result); return result; } } }


Codex y Copilot generaron la misma función, lo que no debería sorprender dado que Copilot se basa en Codex. Aunque, en los ejemplos anteriores, diferían.


ChatGPT, Codex y Copilot generaron funciones ineficientes, JSON.stringify, seguidas de una comparación de cadenas que es lenta y consume mucha memoria a escala.


También hay valores de JavaScript que no se encadenan, por ejemplo, Infinity y NaN. (Desafortunadamente, la especificación JSON de JavaScript se definió antes de la era de la ciencia de datos y los microservicios, y se asumió que Infinity y NaN implicaban errores en el código que no eran legítimos o que no necesitaban ser transportados de un lugar a otro. .)


Así que ahora, para la prueba de eficiencia al comparar el código ChatGPT y Copilot con nano-memoize y micro-memoize generando el número 12 de Fibonacci usando este código:


 const fibonacci = (number) => { return number < 2 ? number : fibonacci(number - 1) + fibonacci(number - 2); };


Fuente

Montón usado

Seg. de operaciones

CPU de usuario

CPU del sistema

ChatGPT (corregido)

102552

45801

620

0

Códice

17888

52238

320

0

Copiloto

17888

51301

320

0

nano-memoizar

17576

93699

470

160

micro-memorizar

18872

82833

620

0


Nano-memoize es el código más rápido y casi el doble que ChatGPT, Codex y Copilot. También utiliza menos memoria. Micro-memoize le pisa los talones.


Aunque la utilización de la CPU para nano-memoize y micro-memoize es ligeramente superior a la de Code y Copilot, el rendimiento vale la pena, ¡y los programadores ganan una vez más!

Resumen e hipótesis

Aunque sin duda hay valor en el uso de Copilot y ChatGPT para la generación de código, debe hacerse con cuidado. Ninguno producirá un código óptimo y, en algunos casos, simplemente no será válido o, peor aún, será incorrecto. Además, al usar ChatGPT, las consultas deben ser bastante específicas.


ChatGPT y Copilot podrían mejorarse agregando una función que haría preguntas aclaratorias.


ChatGPT, si fuera realmente inteligente, diría a los usuarios que emplearan su hermano Codex para la generación de código o simplemente usaría Codex en segundo plano.


Si está utilizando Codex en segundo plano, entonces no estoy seguro de lo que sucede cuando proporciono la misma descripción de función para ambos y obtengo resultados diferentes.


Aunque no estoy familiarizado con el funcionamiento interno de ninguna de las herramientas, además de saber que están basadas en un modelo de lenguaje, creo que es poco probable que lleguen a un punto en el que puedan generar un código óptimo sin superar esta deficiencia:


Un sistema entrenado en grandes volúmenes de código no examinado públicamente producirá resultados promedio para el código, es decir, código de rendimiento promedio y código con un número promedio de errores.


Para proporcionar resultados consistentemente precisos, el sistema necesitará:


  1. La capacidad de consumir y usar fragmentos de datos de "muestra de contador", por ejemplo, JSON.stringify, puede ser ineficiente. El sistema puede obtener esta capacidad analizando los resultados de las pruebas, así como el código, o recibiendo un código óptimo conocido con algún tipo de ponderación o simplemente mediante la crítica de los resultados por parte de expertos conocidos. Desafortunadamente, el código óptimo a menudo no es el más frecuente o el más utilizado, y simplemente alimentar los modelos con más ejemplos no ayudará. En el caso ideal, un sistema realmente inteligente sería capaz de generar sus propios casos de prueba.


  2. Una "comprensión" más profunda del primer principio de la programación para analizar el código que genera en busca de fallas de eficiencia, por ejemplo, generalmente favorece la iteración sobre la recursividad para la eficiencia del tiempo de ejecución, generalmente favorece la recursividad para el tamaño del código y la legibilidad.


  3. Como mínimo, una IA generadora de código debería intentar analizar el código que ha generado y evaluar su validez sintáctica. Esta debería ser una simple mejora de ChatGPT.


  4. Idealmente, la IA también ejecutaría al menos uno o dos casos de prueba simples para garantizar la validez del tipo. Mientras creaba pruebas unitarias, Copilot propuso una serie de completaciones de código mejoradas útiles que podrían usarse para este propósito, por ejemplo, argumentos para llamadas a funciones y búsquedas de matrices. Supongo que ChatGPT y Codex podrían mejorarse para hacer tal cosa.

Recursos


Espero que hayas disfrutado este artículo. ¡Que tengas un gran día y haz que el ayer tenga celos de lo que aprendiste hoy (aquí o en otro lugar)!


También publicado aquí