Generative AI for Beginners

Code Review對軟體開發非常重要。好的Code Review不但可以提升軟體的效能,而且可以保證軟體的品質。Code Review的最大挑戰在於Reviewer必須是資深軟體工程師,才足以擔當。但是在於一個高速成長,又非常動態的軟體產業,尤其是在臺灣,所謂資深根本不存在。現在遊戲規則改變了,ChatGPT不但會寫程式,而且用ChatGPT來做Code Review更是讓人歎爲觀止,比人做的好太多。因爲程式語言的語法定義清楚,相對於自然語言的模糊曖昧,單純很多。ChatGPT把GitHub裏的億萬行程式碼都讀的滾瓜爛熟,然後就成爲天下無敵了。

 
請說明什麼是Code Review?

Code Review(代碼審查)是一種軟件開發過程中的質量保證活動,旨在通過同行評審的方式檢查代碼的正確性、風格一致性、可維護性和其他品質相關因素。在進行Code Review時,開發者提交他們的代碼更改,由一位或多位同事審查這些更改,目的是尋找並修正錯誤,改進代碼質量,共享知識與最佳實踐,並確保代碼符合項目的設計和開發標準。

Code Review可以採用多種形式,包括面對面的審查會議、通過電子郵件的非同步審查,或使用代碼審查工具和平台進行的在線審查。無論採取何種形式,Code Review都有以下幾個關鍵目標:

  1. 發現並修正錯誤:最直接的目的是識別代碼中的錯誤,包括邏輯錯誤、性能問題、安全漏洞等,以減少未來的維護成本和潛在的運行時錯誤。
  2. 提升代碼質量:通過審查,可以確保代碼清晰、結構良好且易於維護,符合項目的編碼標準和風格指南。
  3. 知識共享與學習:Code Review是一種有效的知識共享方式,讓團隊成員能夠學習彼此的代碼寫作風格和解決問題的方法,促進技術交流和團隊合作。
  4. 保障一致性和標準化:確保所有的代碼更改都符合項目的架構設計和編碼規範,有助於維護代碼庫的一致性和整潔性。

進行Code Review時,審查者和作者之間應保持開放和尊重的態度,將重點放在代碼和技術問題上,避免針對個人的批評。Code Review不僅僅是找錯的過程,同時也是一個學習和提升的機會。

Linux kernel是現在最重要、影響最大的軟體。平均每六十天左右推出一新版本,最新版本是 6.7版。有超過兩千八百萬行的程式碼。裏面很多程式碼超過三十年,反覆被Review修正,千錘百煉。也有很多新加入的程式碼,等待全世界去測試使用。下面選出兩段Linux kernel 程式碼,示範請ChatGPT做Code Review。

 
請介紹什麼是Linux kernel?

Linux kernel是Linux作業系統的核心部分,負責管理系統的硬件資源,提供程序運行的基礎服務。它是一種自由和開放原始碼的核心,最初由Linus Torvalds在1991年開發。Linux kernel作為一個操作系統核心,其主要功能包括進程管理、記憶體管理、裝置驅動程序管理、檔案系統控制以及網絡通訊等。

主要功能和特點

  • 進程管理:Linux kernel負責進程的創建、執行、調度和終止。進程管理使得多個程序可以同時運行,並有效分配處理器時間。
  • 記憶體管理:核心管理所有的系統記憶體資源,包括物理記憶體和虛擬記憶體。它確保每個進程都有足夠的記憶體空間運行,同時也負責記憶體的分配和回收。
  • 裝置驅動程序:Linux kernel包含大量的裝置驅動程序,使它能夠支持廣泛的硬件設備。這些驅動程序允許操作系統與硬件設備進行交互。
  • 檔案系統:它支持多種檔案系統,允許用戶和程序存儲和訪問數據。Linux核心透過統一的檔案系統接口管理不同的檔案系統。
  • 網絡功能:提供了廣泛的網絡協議支持,使得Linux系統可以作為伺服器和網絡裝置運行,在互聯網上進行數據傳輸和通訊。

開放原始碼和社群貢獻

Linux kernel是開放原始碼項目的典範,全球數以千計的開發者參與其開發和維護。任何人都可以自由地訪問源代碼,貢獻新的功能,或對現有代碼進行修改和改進。這種開放和協作的開發模式促進了Linux kernel的快速發展和創新。

應用範圍

由於其強大的功能和靈活性,Linux kernel被廣泛應用於各種系統中,從個人電腦、伺服器到嵌入式系統和超級計算機。它是許多流行Linux發行版的基礎,包括Ubuntu、Fedora和Debian等。

總之,Linux kernel是Linux操作系統的心臟,其開放原始碼和高度模塊化的設計使其成為當今世界上最流行和最可靠的操作系統核心之一。

Please review the following linux kernel iomem.c code:
				
					/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/device.h>
#include <linux/types.h>
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/ioremap.h>

#ifndef arch_memremap_wb
static void *arch_memremap_wb(resource_size_t offset, unsigned long size)
{
#ifdef ioremap_cache
	return (__force void *)ioremap_cache(offset, size);
#else
	return (__force void *)ioremap(offset, size);
#endif
}
#endif

#ifndef arch_memremap_can_ram_remap
static bool arch_memremap_can_ram_remap(resource_size_t offset, size_t size,
					unsigned long flags)
{
	return true;
}
#endif

static void *try_ram_remap(resource_size_t offset, size_t size,
			   unsigned long flags)
{
	unsigned long pfn = PHYS_PFN(offset);

	/* In the simple case just return the existing linear address */
	if (pfn_valid(pfn) && !PageHighMem(pfn_to_page(pfn)) &&
	    arch_memremap_can_ram_remap(offset, size, flags))
		return __va(offset);

	return NULL; /* fallback to arch_memremap_wb */
}

/**
 * memremap() - remap an iomem_resource as cacheable memory
 * @offset: iomem resource start address
 * @size: size of remap
 * @flags: any of MEMREMAP_WB, MEMREMAP_WT, MEMREMAP_WC,
 *		  MEMREMAP_ENC, MEMREMAP_DEC
 *
 * memremap() is "ioremap" for cases where it is known that the resource
 * being mapped does not have i/o side effects and the __iomem
 * annotation is not applicable. In the case of multiple flags, the different
 * mapping types will be attempted in the order listed below until one of
 * them succeeds.
 *
 * MEMREMAP_WB - matches the default mapping for System RAM on
 * the architecture.  This is usually a read-allocate write-back cache.
 * Moreover, if MEMREMAP_WB is specified and the requested remap region is RAM
 * memremap() will bypass establishing a new mapping and instead return
 * a pointer into the direct map.
 *
 * MEMREMAP_WT - establish a mapping whereby writes either bypass the
 * cache or are written through to memory and never exist in a
 * cache-dirty state with respect to program visibility.  Attempts to
 * map System RAM with this mapping type will fail.
 *
 * MEMREMAP_WC - establish a writecombine mapping, whereby writes may
 * be coalesced together (e.g. in the CPU's write buffers), but is otherwise
 * uncached. Attempts to map System RAM with this mapping type will fail.
 */
void *memremap(resource_size_t offset, size_t size, unsigned long flags)
{
	int is_ram = region_intersects(offset, size,
				       IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE);
	void *addr = NULL;

	if (!flags)
		return NULL;

	if (is_ram == REGION_MIXED) {
		WARN_ONCE(1, "memremap attempted on mixed range %pa size: %#lx\n",
				&offset, (unsigned long) size);
		return NULL;
	}

	/* Try all mapping types requested until one returns non-NULL */
	if (flags & MEMREMAP_WB) {
		/*
		 * MEMREMAP_WB is special in that it can be satisfied
		 * from the direct map.  Some archs depend on the
		 * capability of memremap() to autodetect cases where
		 * the requested range is potentially in System RAM.
		 */
		if (is_ram == REGION_INTERSECTS)
			addr = try_ram_remap(offset, size, flags);
		if (!addr)
			addr = arch_memremap_wb(offset, size);
	}

	/*
	 * If we don't have a mapping yet and other request flags are
	 * present then we will be attempting to establish a new virtual
	 * address mapping.  Enforce that this mapping is not aliasing
	 * System RAM.
	 */
	if (!addr && is_ram == REGION_INTERSECTS && flags != MEMREMAP_WB) {
		WARN_ONCE(1, "memremap attempted on ram %pa size: %#lx\n",
				&offset, (unsigned long) size);
		return NULL;
	}

	if (!addr && (flags & MEMREMAP_WT))
		addr = ioremap_wt(offset, size);

	if (!addr && (flags & MEMREMAP_WC))
		addr = ioremap_wc(offset, size);

	return addr;
}
EXPORT_SYMBOL(memremap);

void memunmap(void *addr)
{
	if (is_ioremap_addr(addr))
		iounmap((void __iomem *) addr);
}
EXPORT_SYMBOL(memunmap);

static void devm_memremap_release(struct device *dev, void *res)
{
	memunmap(*(void **)res);
}

static int devm_memremap_match(struct device *dev, void *res, void *match_data)
{
	return *(void **)res == match_data;
}

void *devm_memremap(struct device *dev, resource_size_t offset,
		size_t size, unsigned long flags)
{
	void **ptr, *addr;

	ptr = devres_alloc_node(devm_memremap_release, sizeof(*ptr), GFP_KERNEL,
			dev_to_node(dev));
	if (!ptr)
		return ERR_PTR(-ENOMEM);

	addr = memremap(offset, size, flags);
	if (addr) {
		*ptr = addr;
		devres_add(dev, ptr);
	} else {
		devres_free(ptr);
		return ERR_PTR(-ENXIO);
	}

	return addr;
}
EXPORT_SYMBOL(devm_memremap);

void devm_memunmap(struct device *dev, void *addr)
{
	WARN_ON(devres_release(dev, devm_memremap_release,
				devm_memremap_match, addr));
}
EXPORT_SYMBOL(devm_memunmap);

				
			

This Linux kernel code from iomem.c provides functionality for memory remapping, specifically targeting the remapping of I/O memory resources as cacheable memory, managing such remappings in device-managed (devm) contexts, and their corresponding cleanup. Here’s an overview and some insights on key aspects of the code:

Key Functions and Their Purpose

  • memremap: Remaps an I/O memory resource as cacheable memory, handling different types of memory caching strategies based on the flags provided. This function is critical for situations where access to I/O memory needs to be optimized for performance by using caching techniques that best match the hardware requirements and access patterns.
  • memunmap: Unmaps a previously remapped memory, ensuring that resources are cleaned up properly and no memory leaks occur.
  • devm_memremap: A device-managed version of memremap that automatically releases remapped memory when the device is detached or the driver is unloaded. This helps prevent memory leaks in drivers.
  • devm_memunmap: Complements devm_memremap by providing a device-managed way to unmap previously mapped memory.

Observations and Recommendations

  • Code Organization: The code is well-organized, separating the concerns of basic remapping, device-managed resource management, and cleanup functionalities. This makes the code easier to understand and maintain.
  • Use of Preprocessor Directives: The use of #ifndef for defining arch_memremap_wb and arch_memremap_can_ram_remap ensures that architecture-specific versions of these functions can override the defaults. This is a flexible design choice that accommodates different hardware architectures.
  • Error Handling: The functions provide basic error handling, returning NULL or error pointers (ERR_PTR) in case of failures. It would be beneficial to ensure that all error paths log or otherwise communicate the error condition to the caller or system log for easier debugging.
  • Memory Mapping Flags: The handling of different flags (MEMREMAP_WB, MEMREMAP_WT, MEMREMAP_WC) demonstrates a good understanding of memory caching strategies and the need for flexible memory access configurations. It’s important that the documentation for these flags is kept up-to-date and clear for developers.
  • Potential for Improvement: One area for potential improvement could be the explicit handling of new memory mapping flags or technologies as they become available. Ensuring the code is extensible and can accommodate future developments without significant refactoring is key to long-term maintenance.
  • Architecture-Specific Considerations: The reliance on architecture-specific functionality (arch_memremap_wb, arch_memremap_can_ram_remap) is necessary but requires that architecture maintainers keep these functions updated. A clear contract or documentation about what these functions should provide would be helpful.

Conclusion

Overall, the code is robust, reflecting well-thought-out approaches to handling I/O memory remapping in the Linux kernel. It demonstrates good practices in terms of flexibility, error handling, and device management. Like with any complex system, continuous review, testing, and updates are essential, especially as new hardware architectures and technologies emerge.

這支程式碼開始出現於2018年5月的4.18版,基本上已經有一段時間,已經相對成熟了。

Please review the following linux kernel code drm_accel.c:
				
					// SPDX-License-Identifier: GPL-2.0

/*
 * Copyright 2022 HabanaLabs, Ltd.
 * All Rights Reserved.
 *
 */

#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/idr.h>

#include <drm/drm_accel.h>
#include <drm/drm_debugfs.h>
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_print.h>

static DEFINE_SPINLOCK(accel_minor_lock);
static struct idr accel_minors_idr;

static struct dentry *accel_debugfs_root;

static struct device_type accel_sysfs_device_minor = {
	.name = "accel_minor"
};

static char *accel_devnode(const struct device *dev, umode_t *mode)
{
	return kasprintf(GFP_KERNEL, "accel/%s", dev_name(dev));
}

static const struct class accel_class = {
	.name = "accel",
	.devnode = accel_devnode,
};

static int accel_sysfs_init(void)
{
	return class_register(&accel_class);
}

static void accel_sysfs_destroy(void)
{
	class_unregister(&accel_class);
}

static int accel_name_info(struct seq_file *m, void *data)
{
	struct drm_info_node *node = (struct drm_info_node *) m->private;
	struct drm_minor *minor = node->minor;
	struct drm_device *dev = minor->dev;
	struct drm_master *master;

	mutex_lock(&dev->master_mutex);
	master = dev->master;
	seq_printf(m, "%s", dev->driver->name);
	if (dev->dev)
		seq_printf(m, " dev=%s", dev_name(dev->dev));
	if (master && master->unique)
		seq_printf(m, " master=%s", master->unique);
	if (dev->unique)
		seq_printf(m, " unique=%s", dev->unique);
	seq_puts(m, "\n");
	mutex_unlock(&dev->master_mutex);

	return 0;
}

static const struct drm_info_list accel_debugfs_list[] = {
	{"name", accel_name_info, 0}
};
#define ACCEL_DEBUGFS_ENTRIES ARRAY_SIZE(accel_debugfs_list)

/**
 * accel_debugfs_init() - Initialize debugfs for device
 * @dev: Pointer to the device instance.
 *
 * This function creates a root directory for the device in debugfs.
 */
void accel_debugfs_init(struct drm_device *dev)
{
	drm_debugfs_dev_init(dev, accel_debugfs_root);
}

/**
 * accel_debugfs_register() - Register debugfs for device
 * @dev: Pointer to the device instance.
 *
 * Creates common files for accelerators.
 */
void accel_debugfs_register(struct drm_device *dev)
{
	struct drm_minor *minor = dev->accel;

	minor->debugfs_root = dev->debugfs_root;

	drm_debugfs_create_files(accel_debugfs_list, ACCEL_DEBUGFS_ENTRIES,
				 dev->debugfs_root, minor);
}

/**
 * accel_set_device_instance_params() - Set some device parameters for accel device
 * @kdev: Pointer to the device instance.
 * @index: The minor's index
 *
 * This function creates the dev_t of the device using the accel major and
 * the device's minor number. In addition, it sets the class and type of the
 * device instance to the accel sysfs class and device type, respectively.
 */
void accel_set_device_instance_params(struct device *kdev, int index)
{
	kdev->devt = MKDEV(ACCEL_MAJOR, index);
	kdev->class = &accel_class;
	kdev->type = &accel_sysfs_device_minor;
}

/**
 * accel_minor_alloc() - Allocates a new accel minor
 *
 * This function access the accel minors idr and allocates from it
 * a new id to represent a new accel minor
 *
 * Return: A new id on success or error code in case idr_alloc failed
 */
int accel_minor_alloc(void)
{
	unsigned long flags;
	int r;

	spin_lock_irqsave(&accel_minor_lock, flags);
	r = idr_alloc(&accel_minors_idr, NULL, 0, ACCEL_MAX_MINORS, GFP_NOWAIT);
	spin_unlock_irqrestore(&accel_minor_lock, flags);

	return r;
}

/**
 * accel_minor_remove() - Remove an accel minor
 * @index: The minor id to remove.
 *
 * This function access the accel minors idr and removes from
 * it the member with the id that is passed to this function.
 */
void accel_minor_remove(int index)
{
	unsigned long flags;

	spin_lock_irqsave(&accel_minor_lock, flags);
	idr_remove(&accel_minors_idr, index);
	spin_unlock_irqrestore(&accel_minor_lock, flags);
}

/**
 * accel_minor_replace() - Replace minor pointer in accel minors idr.
 * @minor: Pointer to the new minor.
 * @index: The minor id to replace.
 *
 * This function access the accel minors idr structure and replaces the pointer
 * that is associated with an existing id. Because the minor pointer can be
 * NULL, we need to explicitly pass the index.
 *
 * Return: 0 for success, negative value for error
 */
void accel_minor_replace(struct drm_minor *minor, int index)
{
	unsigned long flags;

	spin_lock_irqsave(&accel_minor_lock, flags);
	idr_replace(&accel_minors_idr, minor, index);
	spin_unlock_irqrestore(&accel_minor_lock, flags);
}

/*
 * Looks up the given minor-ID and returns the respective DRM-minor object. The
 * refence-count of the underlying device is increased so you must release this
 * object with accel_minor_release().
 *
 * The object can be only a drm_minor that represents an accel device.
 *
 * As long as you hold this minor, it is guaranteed that the object and the
 * minor->dev pointer will stay valid! However, the device may get unplugged and
 * unregistered while you hold the minor.
 */
static struct drm_minor *accel_minor_acquire(unsigned int minor_id)
{
	struct drm_minor *minor;
	unsigned long flags;

	spin_lock_irqsave(&accel_minor_lock, flags);
	minor = idr_find(&accel_minors_idr, minor_id);
	if (minor)
		drm_dev_get(minor->dev);
	spin_unlock_irqrestore(&accel_minor_lock, flags);

	if (!minor) {
		return ERR_PTR(-ENODEV);
	} else if (drm_dev_is_unplugged(minor->dev)) {
		drm_dev_put(minor->dev);
		return ERR_PTR(-ENODEV);
	}

	return minor;
}

static void accel_minor_release(struct drm_minor *minor)
{
	drm_dev_put(minor->dev);
}

/**
 * accel_open - open method for ACCEL file
 * @inode: device inode
 * @filp: file pointer.
 *
 * This function must be used by drivers as their &file_operations.open method.
 * It looks up the correct ACCEL device and instantiates all the per-file
 * resources for it. It also calls the &drm_driver.open driver callback.
 *
 * Return: 0 on success or negative errno value on failure.
 */
int accel_open(struct inode *inode, struct file *filp)
{
	struct drm_device *dev;
	struct drm_minor *minor;
	int retcode;

	minor = accel_minor_acquire(iminor(inode));
	if (IS_ERR(minor))
		return PTR_ERR(minor);

	dev = minor->dev;

	atomic_fetch_inc(&dev->open_count);

	/* share address_space across all char-devs of a single device */
	filp->f_mapping = dev->anon_inode->i_mapping;

	retcode = drm_open_helper(filp, minor);
	if (retcode)
		goto err_undo;

	return 0;

err_undo:
	atomic_dec(&dev->open_count);
	accel_minor_release(minor);
	return retcode;
}
EXPORT_SYMBOL_GPL(accel_open);

static int accel_stub_open(struct inode *inode, struct file *filp)
{
	const struct file_operations *new_fops;
	struct drm_minor *minor;
	int err;

	minor = accel_minor_acquire(iminor(inode));
	if (IS_ERR(minor))
		return PTR_ERR(minor);

	new_fops = fops_get(minor->dev->driver->fops);
	if (!new_fops) {
		err = -ENODEV;
		goto out;
	}

	replace_fops(filp, new_fops);
	if (filp->f_op->open)
		err = filp->f_op->open(inode, filp);
	else
		err = 0;

out:
	accel_minor_release(minor);

	return err;
}

static const struct file_operations accel_stub_fops = {
	.owner = THIS_MODULE,
	.open = accel_stub_open,
	.llseek = noop_llseek,
};

void accel_core_exit(void)
{
	unregister_chrdev(ACCEL_MAJOR, "accel");
	debugfs_remove(accel_debugfs_root);
	accel_sysfs_destroy();
	idr_destroy(&accel_minors_idr);
}

int __init accel_core_init(void)
{
	int ret;

	idr_init(&accel_minors_idr);

	ret = accel_sysfs_init();
	if (ret < 0) {
		DRM_ERROR("Cannot create ACCEL class: %d\n", ret);
		goto error;
	}

	accel_debugfs_root = debugfs_create_dir("accel", NULL);

	ret = register_chrdev(ACCEL_MAJOR, "accel", &accel_stub_fops);
	if (ret < 0)
		DRM_ERROR("Cannot register ACCEL major: %d\n", ret);

error:
	/*
	 * Any cleanup due to errors will be done in drm_core_exit() that
	 * will call accel_core_exit()
	 */
	return ret;
}
				
			

This Linux kernel code is related to the implementation of a device-specific subsystem for accelerator devices, integrating with the Direct Rendering Manager (DRM) and providing sysfs, debugfs, and device management functionalities. It’s designed to manage accelerator devices, providing an interface for user-space applications to interact with these devices. Let’s review the key components and offer some insights:

Structure and Initialization

  • Sysfs and Debugfs Integration: The code integrates with Linux’s sysfs and debugfs, providing a way to expose device information and debug interfaces to user space. This is crucial for both device management and troubleshooting.
  • Device Management: It uses the IDR (ID Radix Tree) API for managing minor numbers efficiently, which is common for managing a finite number of device resources within the kernel.

Functionality and Design Choices

  • Dynamic Device Node Creation: The function accel_devnode dynamically creates device nodes under /dev/accel/, enabling user-space applications to interact with accelerator devices. This dynamic approach allows for flexibility and scalability in managing multiple accelerator devices.
  • Debugging and Information Exposure: Through accel_debugfs_init and accel_debugfs_register, the code provides mechanisms to register debugfs entries for accelerator devices, facilitating debugging and performance monitoring.
  • Error Handling and Logging: There are several checks and error handling routines throughout the code, especially in initialization and device registration functions. However, ensuring comprehensive error handling and clear logging in all error cases is crucial for diagnosing issues in production environments.

Recommendations for Improvement

  • Error Handling Consistency: Ensure consistent error handling and cleanup paths in functions like accel_core_init. For example, if accel_sysfs_init fails, it should cleanly undo any partially completed steps before returning.
  • Documentation and Comments: While there are some comments explaining the functions, adding more detailed documentation about the purpose and expected behavior of each function, especially the public API functions like accel_open, would be beneficial for maintainers and developers integrating with this subsystem.
  • Security Considerations: Ensure that the device operations, especially those exposing information via sysfs and debugfs or accepting inputs from user space, are securely implemented to prevent unauthorized access or information leakage.

Code Quality and Maintenance

  • Modularity and Readability: The code is modular, with clear separation of concerns, which aids in readability and maintenance. Further refactoring could be done to group related functionalities into separate files or modules if the codebase grows.
  • Use of Kernel APIs: The code makes proper use of existing kernel APIs such as spinlocks, IDR, and debugfs, which is good for consistency and efficiency within the kernel ecosystem.
  • Future-Proofing: Consider future hardware capabilities and software requirements. As accelerator technology evolves, ensuring that the subsystem can be extended or adapted to support new features without significant rewrites is important.

Conclusion

This code snippet is a solid foundation for managing accelerator devices within the Linux kernel. It demonstrates good practices in device management, debuggability, and integration with kernel subsystems. With attention to detailed documentation, comprehensive error handling, and security considerations, it can serve as a robust part of a kernel module for accelerator devices.

這段程式碼開始出現於2022年11月的6.2版。這段程式碼有非常深遠的意義,是Linux kernel導入的第一支AI神經網路處理器程式。還很陽春,估計到成熟至少還要三、五年。最近不是AI CPU,AI PC喊的很響亮嗎?真相是還很幼稚,還需要一段時間去nurture。

那現在ChatGPT,Copilot,Bard 等等的很厲害又是來自哪裏?各雲端資料中心的Nvidia 獨家獨佔封閉式的加速處理器。所以最近Nvidia的生意好的不得了。

其他人,例如Intel、AMD、ARM等,只好全力拱OpenSource,例如Linux kernel、OpenCL,企圖打破Nvidia的壟斷地位。拭目以待。

 

我的看法:ChatGPT可以寫程式,又可以做Code Review,程式師是不是會失業了?正好相反,程式師學會利用ChatGPT寫程式,做Code Review,能力大增,可以開發出更多的產品和應用系統,讓世界更舒適、更安全、更永續。

已經退休的程式師也可以思考,學習Generative AI,重新回到職場。過着週休三日、週休四日的均衡工作、學習和生活的日子。因爲錢和職位升遷不再是目標了。

Leave a Reply

Your email address will not be published. Required fields are marked *