Angesichts der zunehmenden Verfügbarkeit von No-Code-/Low-Code-Umgebungen und dem Aufkommen der Anwendung von KI in der Programmierung wurde in einer Reihe von Artikeln berichtet, dass das Ende für Programmierer nahe sei, entweder weil Endbenutzer ihre eigenen Apps erstellen oder die KI dies tun werde Schreiben Sie Apps aus Beschreibungen. Vielleicht … aber noch nicht. In diesem Artikel untersuche ich den aktuellen Stand der KI-gestützten Codierung, indem ich die Vorschläge von OpenAI und to Microsoft mit handgeschriebenem Code vergleiche. Die KIs produzieren entweder durchschnittlichen oder fehlerhaften Code. ChatGPT Codex Copilot Da heutzutage so viel Wert auf die Verarbeitung großer Datenmengen gelegt wird (einschließlich der großen Sprachmodelle, die für ChatGPT und Copilot verwendet werden), konzentriere ich mich auf Algorithmen, die für eine effiziente Datenverarbeitung von entscheidender Bedeutung sind. Hoffentlich „verstehen“ die KIs ihre eigene Natur besser als Code im Allgemeinen! Diese Algorithmen sind: Überschneidung Kreuzprodukt auswendig lernen Ich habe ChatGPT Anweisungen für das Formular bereitgestellt: Zeigen Sie mir <einige Funktionsbeschreibung> Ich habe Codex mit Eingaben versorgt: <einige Funktionsbeschreibung> Ich habe Copilot Kommentare in einer WebStorm-IDE bereitgestellt // <einige Funktionsbeschreibung> Die Funktionsbeschreibungen waren jeweils gleich. Testmethodik Ich habe Jasmine als Unit-Test-Harnisch verwendet, ergänzt durch Code, der dem in ähnelt, um Geschwindigkeit sowie Speicher- und CPU-Auslastung zu erzielen. Benchtest Überschneidung Zeigen Sie mir eine JavaScript-Funktion, die eine variable Anzahl von Arrays akzeptiert und deren Schnittmenge zurückgibt. 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]; } OpenAI-Codex 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); } Microsoft Copilot const intersection = (...arrays) => { const [first, ...rest] = arrays; return first.filter(x => rest.every(y => y.includes(x))); } ChatGPT ging davon aus, dass die Bereitstellung von weniger als einem Array ungültig ist. Ich kann mir Situationen vorstellen, in denen die Angabe eines Arguments gültig sein könnte. Durch die Verwendung eines stellt ChatGPT sicher, dass in den Ergebnissen keine Duplikate vorhanden sind. Der Schnittpunkt soll eine festgelegte Operation sein und Duplikate sollten entfernt werden. Der Copilot-Code gibt ein Array zurück, das möglicherweise Duplikate enthält. Set Weder ChatGPT noch Copilot sortieren die Originalargumente aufsteigend nach Länge. Dies ist eine triviale Optimierung, die einen großen Unterschied machen könnte! Wenn ein Argument die Länge Null hat, gibt es keinen Schnittpunkt; Unabhängig davon werden die Schleifen verkürzt, da der maximale Schnittpunkt mit dem kürzesten Array-Argument übereinstimmt. Codex stellte komplexen Code bereit, der meines Wissens nach recht effizient aussah. Die Verwendung von Argumenten ist jedoch unklar und weist einen etwas archaischen Programmierstil auf. Beachten Sie, dass als letztes Argument ein Rückruf angegeben werden muss, um die Schnittmenge an den aufrufenden Code zurückzugeben, das Argument wird jedoch nicht angezeigt. Ich habe dann ChatGPT und Copilot gebeten, mir eine effiziente Funktion zur Verfügung zu stellen. Zeigen Sie mir eine effiziente JavaScript-Funktion, die eine variable Anzahl von Arrays verwendet und deren Schnittmenge zurückgibt. 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 hat denselben Code wie bei der ersten Anfrage generiert. ChatGPT fügte außerdem einen Kommentar hinzu, der besagte, dass die Funktion für Objekte nicht wie erwartet funktionieren würde, obwohl dies wohl der Fall sein würde. Also habe ich nach einer Funktion gefragt, die Objekte unterstützen würde: Zeigen Sie mir eine effiziente JavaScript-Funktion, die eine variable Anzahl von Arrays mit Objekten oder Grundelementen verwendet und deren Schnittmenge zurückgibt. 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 sortiert Objekte in Strings, um sie zu vergleichen. Dabei gibt es eine Reihe von Problemen: Zwei Objekte, die dieselbe Zeichenfolgendarstellung haben, können legitimerweise separate Objekte sein. Selbst wenn die Zeichenfolgenbildung gültig ist, führen unterschiedliche Eigenschaftsreihenfolgen zu unterschiedlichen Zeichenfolgen, und die Objekte repräsentieren möglicherweise dieselbe Entität. gefolgt von einem String-Vergleich ist ein langsamer Vorgang. JSON.stringify Und jetzt der eigentliche Test! Nachfolgend finden Sie Benchmark-Ergebnisse für Geschwindigkeit und Speicher unter Verwendung von Code, der von ChatGPT, Codex, Copilot und den beiden schnellsten über NPM verfügbaren Schnittpunktbibliotheken, und , generiert wurde. fastArrayIntersect intersector Der Benchmark schnitt drei identische Arrays mit jeweils 10.000 Ganzzahlen und ein abschließendes Array mit 5.000 Ganzzahlen mit einer Stichprobengröße von 100. Außerdem wurde ein Test durchgeführt, um zu bestätigen, dass die Funktionen Sätze zurückgaben, wenn in den Quellarrays doppelte Einträge vorhanden waren. Quelle Duplikate Heap verwendet Ops Sec Benutzer-CPU System-CPU ChatGPT NEIN 39768 6,65 152170 780 Kodex NEIN 5475888 16.00 69070 160 Kopilot Ja 30768 4.16 345190 940 Schnittmenge NEIN 37304 73.02 21020 160 Schnelle Array-Kreuzung NEIN 42064 20.33 18990 630 Copilot nutzt den geringsten Heap, ist aber auch am langsamsten und rechenintensivsten. ChatGPT ist am wenigsten CPU-intensiv, aber auch langsam. Fast Array Intersect ist drei- bis fünfmal so schnell wie Copilot und Chat GPT und 20 % schneller als Codex und gleichzeitig weniger CPU-intensiv. Intersector ist 15-mal schneller als ChatGPT und Copilot, 4-mal schneller als Codex und mehr als dreimal schneller als Fast Array Intersect und weist gleichzeitig die niedrigste CPU-Auslastung auf. In Summe: Weder ChatGPT noch Copilot generieren effizienten Code. ChatGPT geht von Annahmen aus, die möglicherweise nicht gültig sind. Copilot generiert Code, der keine Menge erzeugt, wenn seine Argumente doppelte Werte enthalten. Codex erzeugt einigermaßen leistungsfähigen, wenn auch undurchsichtigen Code. Die Programmierer gewinnen. Sowohl ChatGPT als auch Copilot könnten verbessert werden, indem sie ihre „Intelligenz“ einsetzen, um dem Benutzer ein paar klärende Fragen zu stellen, z. B. suchen Sie nach Geschwindigkeit oder Speichereffizienz, möchten Sie mindestens zwei Argumente benötigen? Kreuzprodukt Um das Ziel dieses Abschnitts klarzustellen: Ich habe nach allen möglichen Kombinationen von Elementen in Arrays gesucht, nicht nach einem arithmetischen Kreuzprodukt von Vektoren. Zeigen Sie mir eine JavaScript-Funktion für kartesisches Kreuzprodukt. 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}; } OpenAI-Codex 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); } Microsoft Copilot const crossProduct = (...arrays) => { const [first, ...rest] = arrays; return first.flatMap(x => rest.map(y => [x, y])); } Leider bietet ChatGPT eine Funktion zur arithmetischen Kreuzproduktberechnung. Meiner Meinung nach hätte es beide Arten von Funktionen bereitstellen oder um Klarstellung bitten sollen. Nach mehreren Versuchen mit unterschiedlichen Abfragen konnte ich ChatGPT dazu bringen, alle drei der folgenden Punkte zu generieren: 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; } Kopilot const crossProduct = (...arrays) => { const [first, ...rest] = arrays; return first.flatMap(x => rest.map(y => [x, y])); } Wie bei der Schnittmenge stellte Codex eine Funktion bereit, die einen alten Programmierstil mit unklarer Argumentbehandlung nutzte. Wer mit kartesischen Produkten vertraut ist, weiß, dass diese sowohl im Hinblick auf die Speicherauslastung als auch auf die Leistung explosiv sein können. Naive Implementierungen verbrauchen ausreichend RAM, um alle Permutationen zu speichern, und geben kein Ergebnis zurück, bis alle Permutationen generiert wurden (was lange dauern kann). Die Funktionen ChatGPT und Copilot leiden beide unter diesen Mängeln. Daher habe ich anschließend nach gefragt. einer speichereffizienten JavaScript-Funktion für das kartesische Produkt 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 lieferte die gleichen Ergebnisse wie zuvor. Ich ging davon aus, dass die „speichereffiziente“ Version von ChatGPT weniger RAM verbrauchte als die erste. Ich wusste jedoch, dass es nicht optimal war. Nach mehreren Versuchen habe ich entwickelt. mithilfe eines Generators eine speichereffiziente JavaScript-Funktion für kartesische Produkte 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); } Ehrlich gesagt war ich von dieser Antwort von ChatGPT angenehm überrascht. Versuche, diesen Code auszuführen, schlugen jedoch fehl, da sich nicht in einem Generator, sondern in einer verschachtelten Funktion namens befindet. yield [item,...result] recursion Ich habe auch eine leichte Variation erhalten, bei der ich die Abfrage verloren habe: 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]]); } Wieder einmal schien ChatGPT einige Annahmen über Argumente zu treffen. Copilot hat den gleichen Code zurückgegeben wie zuvor. Meine Bitte um einen Generator wurde völlig ignoriert. Und jetzt der eigentliche Test! Nachfolgend finden Sie Benchmark-Ergebnisse für Geschwindigkeit und Speicher unter Verwendung von Code, der von ChatGPT, Copilot und den schnellsten und speichereffizientesten kartesischen Produkten generiert wurde, die mir bekannt sind und . : CXProduct Lazy Cartesian Product Außerdem ist eine einzeilige Funktion enthalten, die möglicherweise entweder ChatGPT oder Copilot hätte zurückgeben sollen (Sie werden sehen, warum ich diese eingefügt habe, wenn Sie die Ergebnisse überprüfen). //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()))); Der Test verwendet das kartesische Produkt von 4 Arrays mit jeweils 10 Elementen. Daher sollten die Funktionen ein kartesisches Produkt von 10 * 10 * 10 * 10 Elementen erzeugen, also 10.000. Quelle # Ergebnisse Heap verwendet Ops Sec Benutzer-CPU System-CPU 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 ChatGPT speichereffizient N / A N / A N / A N / A N / A ChatGPT-Generator 1000 N / A N / A N / A N / A Kodex 4 N / A N / A N / A N / A Kopilot 30 N / A N / A N / A N / A Eine Linie 10000 0 78,46 18150 0 CXProdukt 10000 0 291,50 7330 0 LazyProduct 10000 17824 266,65 11560 150 In Summe: ChatGPT, Codex und Copilot sind nicht in der Lage, Code zu generieren, der das richtige Ergebnis für ein kartesisches Produkt liefert. ChatGPT geht manchmal von Annahmen aus, die möglicherweise nicht gültig sind, z. B. erfordert es zwei Argumente. ChatGPT generierte Code, der angeblich speichereffizienter war, aber nicht einmal ausgeführt werden konnte. Die Programmierer gewinnen! Auch hier könnte ChatGPT verbessert werden, indem seine „Intelligenz“ darauf angewendet wird, dem Benutzer ein paar klärende Fragen zu stellen, z. B.: Suchen Sie nach Geschwindigkeit oder Speichereffizienz, möchten Sie mindestens zwei Argumente benötigen? Zuvor muss jedoch der richtige Code generiert werden! Auswendiglernen Nachdem ich meine Lektion über Schnittmengen und kartesische Produkte gelernt hatte, stellte ich sofort eine verfeinerte Frage zum Auswendiglernen. Zeigen Sie mir eine effiziente JavaScript-Funktion, die Objekt- und Grundargumente speichert. 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 hat ungültigen Code generiert; Eine muss ein Objekt als Schlüssel verwenden. Der Code wurde zum Testen wie folgt geändert. WeakMap 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; } } Öffnen Sie den KI-Codex 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; } } } Microsoft Copilot 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 und Copilot haben die gleiche Funktion generiert, was angesichts der Tatsache, dass Copilot auf Codex basiert, nicht überraschend sein sollte. Obwohl sie in den früheren Beispielen unterschiedlich waren. Die von ChatGPT, Codex und Copilot generierten ineffizienten Funktionen gefolgt von einem String-Vergleich, sind langsam und verbrauchen im großen Maßstab Speicher. JSON.stringify, Es gibt auch JavaScript-Werte, die nicht stringifiziert sind, z. B. Infinity und NaN. (Leider wurde die JavaScript-JSON-Spezifikation vor dem Zeitalter der Datenwissenschaft und Mikrodienste definiert und man ging davon aus, dass Infinity und NaN Fehler im Code implizierten, die entweder nicht legitim waren oder nicht von einem Ort zum anderen transportiert werden mussten .) Nun zum Beweis der Effizienz durch den Vergleich von ChatGPT- und Copilot-Code mit und durch Generierung der 12. Fibonacci-Zahl mit diesem Code: Nano-Memoize Micro-Memoize const fibonacci = (number) => { return number < 2 ? number : fibonacci(number - 1) + fibonacci(number - 2); }; Quelle Heap verwendet Ops Sec Benutzer-CPU System-CPU ChatGPT (korrigiert) 102552 45801 620 0 Kodex 17888 52238 320 0 Kopilot 17888 51301 320 0 Nano-Memoize 17576 93699 470 160 Mikro-Auswendiglernen 18872 82833 620 0 Nano-Memoize ist am schnellsten und fast doppelt so schnell wie ChatGPT-, Codex- und Copilot-Code. Es verbraucht auch weniger Speicher. Micro-Memoize ist dicht auf den Fersen. Obwohl die CPU-Auslastung für und etwas höher ist als bei Code und Copilot, lohnt sich die Leistung und die Programmierer gewinnen wieder einmal! nano-memoize micro-memoize Zusammenfassung & Hypothesen Obwohl die Verwendung von Copilot und ChatGPT für die Codegenerierung sicherlich von Nutzen ist, sollte dies mit Vorsicht erfolgen. Beides führt nicht zu optimalem Code und in manchen Fällen ist er einfach ungültig oder, schlimmer noch, falsch. Darüber hinaus sollten bei der Verwendung von ChatGPT die Abfragen recht spezifisch sein. Sowohl ChatGPT als auch Copilot könnten durch die Hinzufügung einer Funktion verbessert werden, die klärende Fragen stellt. Wenn ChatGPT wirklich intelligent wäre, würde es die Benutzer entweder auffordern, seinen Schwestercodex für die Codegenerierung zu verwenden, oder Codex einfach im Hintergrund verwenden. Wenn Codex im Hintergrund verwendet wird, bin ich mir nicht sicher, was passiert, wenn ich beiden dieselbe Funktionsbeschreibung gebe und unterschiedliche Ergebnisse erhalte. Obwohl ich mit der internen Funktionsweise beider Tools nicht vertraut bin, abgesehen von dem Wissen, dass sie auf Sprachmodellen basieren, gehe ich davon aus, dass es unwahrscheinlich ist, dass sie an einen Punkt gelangen, an dem sie optimalen Code generieren können, ohne diesen Mangel zu beheben: Ein System, das auf große Mengen öffentlich ungeprüften Codes trainiert wird, wird durchschnittliche Ergebnisse für Code liefern, dh Code mit durchschnittlicher Leistung und Code mit einer durchschnittlichen Anzahl von Fehlern. Um stets genaue Ergebnisse zu liefern, benötigt das System Folgendes: Die Möglichkeit, Fragmente von „Gegenbeispiel“-Daten, z. B. zu konsumieren und zu verwenden, kann ineffizient sein. Das System könnte diese Fähigkeit erlangen, indem es Testergebnisse und Code analysiert oder mit bekanntem optimalem Code mit einer Gewichtung gefüttert wird oder einfach durch die Kritik der Ergebnisse durch bekannte Experten. Leider ist optimaler Code oft nicht der am weitesten verbreitete oder am häufigsten verwendete Code, und es hilft nicht, den Modellen einfach weitere Beispiele zuzuführen. Im Idealfall wäre ein wirklich intelligentes System in der Lage, eigene Testfälle zu generieren. JSON.stringify, Ein tieferes, grundlegenderes „Verständnis“ der Programmierung, um den von ihm generierten Code auf Effizienzmängel zu analysieren, z. B. bevorzugen normalerweise Iteration gegenüber Rekursion für Laufzeiteffizienz, bevorzugen normalerweise Rekursion für Codegröße und Lesbarkeit. Zumindest sollte eine codegenerierende KI versuchen, den von ihr generierten Code zu analysieren und seine syntaktische Gültigkeit zu bewerten. Dies sollte eine einfache Erweiterung von ChatGPT sein. Idealerweise würde die KI auch mindestens einen oder zwei einfache Testfälle ausführen, um die Typgültigkeit sicherzustellen. Während ich Unit-Tests erstellte, schlug Copilot eine Reihe nützlicher erweiterter Code-Vervollständigungen vor, die für diesen Zweck verwendet werden könnten, z. B. Argumente für Funktionsaufrufe und Array-Suchen. Ich gehe davon aus, dass ChatGPT und Codex für so etwas erweitert werden könnten. Ressourcen OpenAI ChatGPT: https://chat.openai.com/chat OpenAI-Codex: https://beta.openai.com/codex-javascript-sandbox Microsoft Copilot: https://github.com/features/copilot Intersektor: https://github.com/anywhichway/intersector Fast Array Intersect: https://github.com/lovasoa/fast_array_intersect Kartesisches Produkt One Liner: https://stackoverflow.com/questions/12303989/cartesian-product-of-multiple-arrays-in-javascript CXProduct: https://github.com/anywhichway/cxproduct LazyProduct: http://phrogz.net/lazy-cartesian-product Nano Memoize: https://github.com/anywhichway/nano-memoize Mikro-Memoize: https://github.com/planttheidea/micro-memoize Ich hoffe, Ihnen hat dieser Artikel gefallen. Ich wünsche Ihnen einen schönen Tag und machen Sie den gestrigen Tag neidisch auf das, was Sie heute (hier oder anderswo) gelernt haben! Auch veröffentlicht hier