写点什么

软件开发者是硬件开发者的两倍!CUDA“元老级”架构师亲述:最初 12 人的团队如何成就了英伟达万亿市值

  • 2025-03-12
    北京
  • 本文字数:8068 字

    阅读完需:约 26 分钟

大小:3.98M时长:23:11
软件开发者是硬件开发者的两倍!CUDA“元老级”架构师亲述:最初12人的团队如何成就了英伟达万亿市值

编者按:

 

最近,在 NVIDIA 的一次内部对话中,英伟达的三位员工 Nader、Stephen 和 Carter 三位员工分享了他们对 CUDA 技术的发展历程及其在计算科学和 AI 领域的应用的见解。Stephen,作为 CUDA 架构师之一,回顾了 CUDA 的早期发展,包括团队如何在初期面对图形团队的质疑和挑战。他讲述了 CUDA 如何从一个内部初创项目发展成为推动图形和计算领域创新的关键技术。



Stephen 还解释了在 CUDA 之前,开发者如何通过 OpenGL 和 DirectX 等技术与 GPU 交互,以及 CUDA 的出现如何改变了游戏开发和视觉效果的实现方式。

 

此外,Stephen 还提到了英伟达在并行计算方面的前瞻性,早在 90 年代,英伟达 CEO 黄仁勋(Jensen)就预见到了并行化的潜力。他解释了当时图形架构的竞争,以及英伟达如何通过专用芯片提高性能,最终形成了如今的多线程机器。

 

Carter,物理学背景出身,分享了 CUDA 如何帮助物理学家突破计算极限,特别是在天气预报和计算生物学等领域。他提到,尽管最初物理学家对 CUDA 的多线程设计持保留态度,但随着计算需求的增长,他们逐渐接受了这一技术。

 

在这场谈话中,Nader、Carter 和 Stephen 深入探讨了英伟达的技术发展、CUDA 的演进以及公司在计算科学和 AI 芯片领域的主导地位。但在他们的谈话,InfoQ 读到了他们透露出来的另一个讯号,也是黄仁勋曾在公开场合反复提到的:英伟达现在是一家以软件为核心的公司。

 

事实上,英伟达的加速计算组(Accelerated Computing Group)负责人 Dave Salvator 去年曾在公开场合提到过,英伟达的代码工程师人数是其硬件工程师人数的两倍。

 

以下内容为 InfoQ 根据视频采访实录翻译整理,经编辑:

 

Nader:我们是来自英伟达公司的 Nader、Stephen 和 Carter。在英伟达工作的最大收获,就是有机会结识 Stephen 这样出色的人物。这个系列节目的标题是“英伟达是家软件公司”,希望强调我们软硬件工程结合的身份。

 

Stephen:是的,我们现在有最多的软件工程师和硬件工程师。我记得几年之前 CEO 黄仁勋曾经说过,“英伟达不再是一家硬件公司,而是一家软件公司”。这话一点没错。

 

Nader:相信大家都听说过 CUDA,还有它对开发人员的重要意义。Stephen,你是 CUDA 团队的一员吧?

 

Stephen:没错,我是 CUDA 团队的架构师,而且从 2008 年开始就参与这个项目了。最初团队里只有 12 个人,那段经历非常有趣。

 

记得我当初就是随机加入了英伟达内部的团队,当时大家的工作就是用显卡 GPU 做计算。结果刚一入职,我就发现大家都气呼呼的。“咋的了这是?”我完全搞不清状况。后来才知道,当时所有人都会用 3D Mark 基准测试来给硬件跑分,而 CUDA 会占用掉 10%的 GPU 资源。所有图形开发同事都说“它让 3D Mark 得分低了 10%”,而且为此恼怒不已。我真的没想到自己也会被卷进来。

 

但讽刺的是,如今的 GPU 保持着 90%可编程、10%固定功能的设计比例,相当于跟当初的情况完全颠倒了。我们在 CUDA 身上倾注的心血,最终在游戏等各类用例下都在发挥作用。

CUDA 曾因占用 10%内存被嫌弃


Nader:那在 CUDA 出现之前,GPU 是什么样的?人们怎样与之交互,游戏开发者又在其中扮演什么角色?

 

Stephen:当时有 OpenGL 和 DirectX,它们诞生于 2000 年左右。虽然我不是图形专家,但大致上开发者会构建容纳 3D 对象的场景并进行沉浸。OpenGL 就是构造这些场景、实现矩阵旋转的工具。之后 OpenGL 开始提供一些可编程的小脚本,这样开发者就能实现几何体变形之类的效果。这种可编程性当时被称为可编程着色器,是 GPU 上最早的无固定功能硬件——也可以说是后来可编程设计的起源。

 

CUDA 的发明者是斯坦福大学的 Ian Buck,他想出了如何将普通编译器映射为可编程形式。他在英伟达实习时说服了 Jensen,强调这只会占用芯片 10%的资源。但正因为如此,同事们才特别不爽。但现在回想起来,这不就是图形开发人员强调的“动态可编程着色、几何图形能创造出更好的游戏画面,更好的视觉效果”吗?总之我们开始像设计计算系统那样设计图形系统,包含函数、循环、变量和 C 编译器。

 

一切的开始源于固定函数渲染。固定函数渲染代表我们可以将网格传递到硬件,由硬件中的机制处理这些三角形并显示在屏幕上。但在传递给硬件之前,这些函数大半都存放在软件当中。正因为如此,OpenGL 和 DirectX 的驱动程序才特别复杂。各种东西中都需要内置编译器,确保硬件本身始终以最高速度显示出最多的三角形。

 

如今,我们已经可以使用着色器和内核来管理并控制几何图形的变形和移动了。所以,固定函数渲染的作用就是说明“这是个三角形,这是一张纹理图,把它们显示在屏幕上”。这是一份巨大的清单,能够实现很多效果。但随着技术发展,开发者们希望增强控制能力或者添加一些光源之类的效果,而最终成果就是可编程设计。

 

几乎所有这些都可以在 GPU 中以高度轻量化的线程形式存在,它们可以执行最基本的代码。过去我们没办法使用 for 循环,一切内容都需要注册,而且不能执行函数。那时候一切真的非常基础,但至少可以获取一些变量,把它们相加以得到另一个变量,再开平方等等。总之,这一点点计算量成就了更强的控制能力。

 

于是我们下定决心,哪怕图形显示还不需要 CUDA,我们也要借此实现可编程性。事实证明我们是对的。但现在的情况又不一样了,我们在图形方面看到的不少重大进步,其实是来自基于 AI 的着色器、神经网络着色器等,比如机器学习 RTX。光线追踪的技术原理虽然特殊,但 RTX 使其能够嵌入微小的机器学习算法,再以程序化方式生成结果,且速度比普通软件算法要快得多。

 

CUDA 的“护城河”是并行化能力

 

Nader:那这一切是发生在 CUDA 之上的单独层,还是就在 CUDA 当中?

 

Stephen:CUDA 其实是一套集合。很多朋友在说起 CUDA 时,首先想到的是 CUDA C++语言。但不止于此,它包含一系列类似于 C++的库,可以通过特定方式编写并更好地发挥对应硬件的性能。它可以调用 cuDNN 的 PyTorch 框架,比如调用 cuBLAS、调用 cuTENSOR 乃至编译器。PyTorch 下面的这一切都是 CUDA甚至 CUDA 代码本身也代表着英伟达为 PyTorch 代码库做出的贡献。

 

所以说,CUDA 就是一套完整的技术栈。毕竟没人愿意直接编写汇编代码,也几乎不想编写 C++这类低级代码,这就需要一套库或者封装的 SDK 帮大家解决需求。CUDA 有 10 个层,最底层是编译器,负责获取最终代码并将其映射至 GPU 线程。

 

DirectX 就是一个 SDK,是微软打造的 API。其中是一组定义如何设置图形的函数。大家在 Windows 上玩的任何游戏几乎都要依赖 DirectX。OpenGL 也差不多,只是有点跟不上时代了。Vulkan 取代了它的地位。如果大家在 Linux 上玩游戏,那就要用到 Vulkan。这些都是标准图形 SDK。虽然不一定能完全互换,但从功能上讲,它们有很多共性。

 

Nader:那你为什么会决定对 DirectX 进行分叉?

 

Stephen:DirectX 是微软的 API 规范,定义了图形的设置方式。也就是说,英伟达 GPU 或者其他显卡只要实现了 DirectX API 和 Vulkan API,就能让游戏跟它们通信。有些程序在英伟达 GPU 上运行效果更好,但 DirectX 永远存在。Vulkan 则可以添加扩展及其他厂商特定的驱动程序,借此支持某些核心硬件。

 

在 CUDA 项目期间,我们一直在努力达成的一个目标,就是让同样使用汇编语言的图形管道和 CUDA 通用软件开发管道保持互操作性。这也是 AI 在图形领域的意义所在。机器学习模型及其执行、推理都是由 CUDA C++编写而成。换言之,AI 内核、图形内核和着色器可以共存并相互调用。

 

另外一个重点在于功率。我的笔记本电脑只有 150 瓦,台式机则有 1.5 千瓦。而哪怕是功率达到 20 兆瓦的数据中心,它的容纳上限也是明确的。因此除非摩尔定律持续起效、晶体管数量不断增加,否则缺少能效提升空间的晶体管总会耗尽我们的功率配额。因此,降低运行功率以维持相同或者更多芯片的运行,就成了一大核心目标。

 

能效的意义也得到了历史的证明。随着晶体管的缩小,启动它们所需要的电量也在降低。直到 2006 年英特尔酷睿 2 发布之前,这种思路都非常有效。但奔腾 4 第一次出现了功率问题,漏电子流效应意味着电子会逃离晶体管。这跟电阻无关,单纯是量子力学所决定。这些电子会传送到其他东西上,进而导致能量损失。

 

芯片开关的速度越快,能量损失的速度也会越快,进而拉低主频。正因为如此,2006 年到 2007 年左右出现了一系列主频很低的 CPU。它们的出现,打破了“主频就是性能”的固有认知。

 

后来我们又遇到了单核功率瓶颈。我们发现如果继续缩小晶体管,那么其功耗将会翻倍,导致电压不足以正常实现状态切换。结果就是当时的芯片主频从 3、4 GHz 下降至 1.5 GHz,但双核、四核和八核芯片逐渐诞生。如今某些 AMD 芯片甚至有 128 个核心。

 

核心数量的增加其实是种妥协性质的障眼法,是在掩饰半导体厂商已经无法继续提高主频的问题。这样设计出来的芯片虽然理论上拥有双倍性能,但也只有两个核心全部满负荷运转时才成立,而当时绝大多数软件根本不支持多线程。

 

所以如果观察摩尔定律曲线,就会发现 CPU 的晶体管密度已经提高了 20 倍,但单线程、单核心性能却仅仅提高了几倍。所以除非采用并行处理,否则自 2006 年以来,后续发布的 CPU 根本就没什么颠覆性的性能提升。

 

总之,我们已经在单核设计上穷尽了一切手段。继续缩小晶体管的唯一好处,就是实现并行化。并行化是软件领域的必要能力,GPU 就完全是在执行并行计算。当前的 GPU 大约可提供 25 万个线程,这在过去简直无法想象

 

Nader:那这要怎样编程?你又如何看待这样的设计?

 

Stephen:CUDA 所关注的就不是单线程编程场景。我们在 CUDA 立项早期就确定了并行性的规划方针。比如是把这种机制藏起来、公开出去,还是允许用户思考并参与进来。

 

我们实际是把它分成了两个部分,即粗粒度与细粒度。首先要明确一点,没人能同时运行这 25 万个线程。这世界上有两种并行类型,其一是数据并行,即用户拥有大量数据,比如需要渲染屏幕上的每一个像素。这时候可以把任务拆分并展开,也就是分而治之的处理思路。

 

另外一种是任务并行,即场景中有多个对象,游戏就是典型案例。你可能需要为多个对象改变几何形状、执行程序纹理、设置光源等等。这时候要做的就不是拿 25 万个线程渲染同一个对象,而是要同时渲染 100 个对象,把这些对象分配给多个线程。

 

在 CUDA 之前,很多编程模型都只支持一种并行类型,即数据并行或者任务并行。但有了几十万个线程,我们觉得划分应该更明确一些。比如根据实际需求把数据或者任务拆分成多个独立部分,再各自分配 1000 个线程来处理。

 

我们让这种拆分过程透明可见,这也更贴合 GPU 的实际运行状态。CUDA 的一大原则,就是尽可能贴近硬件以实现最佳性能。GPU 内部的硬件拥有我们称为流式多处理器(SM)的核心。当前的 GPU 拥有约 150 个这样的核心,它使用这些核心进行程序分配,且每核心可运行 2000 个线程。

 

这就让硬件拥有了自然拆分任务和数据的能力。比如先将 GPU 资源拆分成 150 个独立的部分,接下来既可以把它们组合起来解决同一个问题,也可以指挥特定部分所对应的线程分别处理问题中的图形或者算法等具体环节。

 

这就是 CUDA 得以迅速发展的原因。随着芯片越做越大并迎来流式多处理器,这种任务拆分自然会让计算性能得到提升。这跟 CPU 完全不同——CPU 始终强调单线程,因此增加核心数量对于实际性能的贡献往往比较有限。

CUDA 的挑战在于让程序员能同时控制两种计算类型

 

Nader:人类的思维是有连续性的。那在展示这种全新范式时,有没有遇到过抵触情绪和沟通障碍?对于这样一种不同于以往的编程和问题解决方式,市场的采用曲线又是怎样的?

 

Carter:我是学物理学出身,编程设计方面的事我不太有发言权,但这种范式变化确实给了我很大帮助。比如在 AI 爆发之前,CUDA 的第一个目标就是计算科学,希望优化天气预报和计算生物学等的处理性能。

 

物理学家跟程序员不同,他们可以穷尽一生去探究物理世界的秘密。正是因为我发现自己缺少那样极致的天赋,最终才决定投身计算机。我曾在大学物理实验室工作过,那里会把角色分得很开——有科学家、也有程序员,大家各司其职共同协作。物理学家不知道如何最大限度运用计算机,而程序员就出面解决这类需求。这种强强联手的方式非常成功。

 

我们专门去找过这些物理学家,向他们介绍 CUDA 和多线程设计。他们刚开始确实会拒绝,因为这不是他们惯常的计算实现思路,没想过怎样把任务映射到大量线程上。但这种习惯思维意味着更大的问题必须由更大的计算机来解决,总有一天计算机会不够大,这时工作就卡住了。

 

2000 年初那段时间,人们总觉得算力的提升是无上限的。但物理学家的态度更灵活,毕竟只要算力能再强哪怕一点点,结果都可能完全不同。所以他们对任何能帮助自己跨越新边界的技术,都比较愿意接受。

 

就比如说 2D 图形到 3D 图像的转换,后者对于供电和内存的需求百倍、千倍于前者。但大家都希望能尽早实现,而不是坐等摩尔定律带来足够强大的单台计算机。

 

所以哪怕在 CUDA 之前,就有很多博士生在编写新算法,努力在更小的机器上解决更大的难题。我对这方面探索很感兴趣,因此经常会出席超级计算会议,跟其他与会者交流心得。

 

让我印象深刻的是,2019 年时大家都对 AI 嗤之以鼻,觉得这东西根本就不存在。但短短几年的疫情之后,如今每个人都在聊 AI,比如“我把 AI 加入了自己的生物算法”。

 

机器学习的强大之处,在于我们可以训练它来生成一条复杂的函数,从而简单快速地解决问题——哪怕我们并不完全理解它的基本原理。仍然以天气预报为例,由于蝴蝶效应,地球上某个区域的微小变化会极大影响其他区域的天气。

 

照传统方法,我们需要对整个星球进行预测,采集国家气象局的行星模拟数据。然而,世界上最大的计算机也只能以约 10 公里的分辨率模拟行星。虽然从星球的角度来看,10 公里这个单位并不算大,但常规云团的直径也就是 1 公里,且足以决定特定区域的天气。换句话说,老办法根本不可能准确生成整个星球的天气预报。

 

几年之前,人们发现可以使用机器学习模型来填补空白,而且效果比传统云团模型好得多。海洋模型和云团模型都是极其复杂的系统,但通过填充这些边长 10 公里的正方形块,它们能够有效实现 1 公里的分辨率。所以过去几年间,天气预报的预测范围已经从 5 天延长到了 7 天甚至是 10 天。

 

在 AI 的帮助下,天气预报突然就“支楞”起来了,这是因为机器学习算法让我们得到了更好的结果。

 

之所以能把天气预报的范围延长到 10 天,是因为机器学习算法能够实现原本难以做到的事情,比如处理近似非线性议程。简单来讲,AI 可以将上百万个维度上的无数个点拟合成一条曲线。在训练过程中,它会逐渐把握住这条与数据最贴近的曲线,并在之后的推理期间用曲线核查真实采集到的点。也正因为如此,推理的速度才会远远快于训练。

 

这样的 AI 填充技术不仅适用于天气预报,也适用于其他场景。AI 算法对极其复杂的蛋白质模型进行推理,彻底改变了计算生物学。但这里又要说回功率的问题。晶体管越小,电子损失越严重。因此随着我们不断增加晶体管数量,能效并不能随之倍增。毕竟我们常用的笔记本电脑就只有 150 瓦,台式机是 1.5 千瓦,它们能容纳的晶体管总量是有限的。

 

但 AI 算法能把评估成本降低至百分之一甚至千分之一。比如在图形渲染中,AI 可以生成几乎看不出区别的近似帧,同时保证不超过笔记本电脑的功率上限。这虽然不是真正的精确渲染,但却可以让每像素的生成成本都满足预算范围,这在以往根本不可能实现。

 

因此我们才特别需要新的 AI 算法,作为替代方案,它们能够适应功率预算且继续提供良好的计算结果。

 

虽然在图形渲染中,AI 有时候可能生成伪影,比如当游戏中的对象快速移动时,AI 仍在根据上一帧生成下一帧,因此显示效果和正确效果间会有轻微差异。但总体来讲,AI 还是能比传统方法更好地呈现运动曲线。

 

计算的历史一直由冯·诺依曼计算架构所主导,其强调的是可重复、精确且合乎逻辑。但如今我们正进入神经计算的新时代,其中的计算并不精确。我们输入同样的数据,有可能会得到略有不同的输出,这是因为问题中的变量太多。神经计算具有容错性,不会像冯·诺依曼计算那样因错误而崩溃。

 

神经算法为高度复杂的问题找到了非常简单的解决办法,唯一的代价就是得到的是近似结果、而非精确结果。但这对人类来说已经够好了。当然,我们在某些场景下不想用 AI,比如说报税——报税过程绝对不能有幻觉。但 AI 在数学问题上的表现令人震惊,所以我相信很快我就能把所有日常事务都交给它了。

 

未来,冯·诺依曼计算和 AI 计算将携手共存,可能还要再加上量子计算等其他计算类型。CUDA 的挑战,在于找到一种方法让程序员同时控制这两种计算类型。作为异构系统,GPU 中包含多种不同用途的不同硬件,而 CUDA 需要将它们统一起来,提供一套能够与之交互的通用平台。

 

随着市面上的硬件越来越多,我们也越来越难以为开发者提供一套简洁、无缝的编程模型。时至今日,我们仍不清楚该如何把神经算法、冯·诺依曼算法和量子计算融合起来。我们在英伟达的一大工作重点,就是让 Python 成为 CUDA 平台中的重要部分,确保平台中的各个部分,包括库、SDK 和编译器都能跟 Python 顺畅交互。

 

我刚加入英伟达时,当时的芯片大约有 3 万个线程。硬件在运行时会将这些线程组合成块,再由 GPU 使用 SIMT(单指令、多线程)实现并行计算。其中大多数线程会保持独立运行,允许每个线程在必要时执行不同的操作。

 

另外,将数据映射至线程对于机器学习算法的效率和性能同样至关重要。机器学习面临着诸多挑战,例如软件栈太深以及如何将数据有效映射至硬件等。

 

现在 AI 领域中即时编译正逐渐成为主流。只要获得了问题的参数,就能针对其做代码优化。CUDA 则始终提供编译器层,确保开发者能够插入自己的语言和内容。现在通过在线运行即时编译,我们希望人们能够在各类场景下使用编译器,借此缩短编译时间并优化代码,让用户更快获取大模型输出的 token。

 

这种依靠极端复杂软件实现最快推理速度的目标不易达成,必须从中寻求平衡。比如说某些算法虽然生成速度更快、首次推理时间更短,但算法本身却是错的。总之这类场景下没有完美答案,真正的关键在于映射结果,同时辅以延迟和效率。

 

Nader:早在上世纪 90 年代时,Jensen 就预见到了并行化的巨大威力,而当时大多数 CPU 还是单核的。这样的前瞻性令人印象深刻。那 90 年代初的并行化是什么样子?

 

Stephen:当时,不同的图形架构之间存在着广泛竞争,各家厂商都在尝试自己的思路。英伟达也有自己的方案,而且被证明效果相当出色。

 

而且这些并不能算 GPU,只是其他类型的图形芯片。英伟达于 1993 年开始制造图形芯片,刚开始只是用来加速某些渲染工作。当时 GL 已经出现,大家都想给这个问题找到答案。其中一种方式,就是放弃提升单一线程性能、加快其逐像素处理能力,而是让多个线程并行运行。但注意,当时的线程跟我们现在说的计算线程并不是一码事。

 

比如说,光栅化器会获取屏幕上某个位置的斜三角形,并确定哪些像素有光照、它们在边界上的亮度参数。光栅化器随后将三角形转换成像素,而这是个非常简单的操作。只要能够复制多个逻辑门,我们就能一次性渲染多个像素,保证光栅化器飞快运行。从本质上讲,这并不属于可编程设计,毕竟只是在按晶体管和逻辑门的序列来决定像素显示还是不显示。但效果仍然不错,我们确实能在每个时钟周期内渲染出更多像素。

 

而这也现在现代并行性设计的起点,人们开始尝试通过专用芯片尽量提高性能。克隆所有逻辑门以便一次处理更多像素的想法,逐渐演变成了可编程性理论,最终形成了如今规模极大的多线程机器。

 

另外需要关注的是,尽管从 CUDA 的角度来看,GPU 本身就是一台大规模并行机器,但它却并不擅长运行 Linux 这类串行问题。这类任务还是要交给 CPU 才行。

 

而我认为理想的异构机器应该是 CPU 与 GPU 的结合——也正因为如此,我才特别赞赏英伟达的 Grace Hopper 硬件。其中采用了 Hopper GPU 并配合基于 ARM 架构的 Grace CPU(服务器级 CPU),以调整内存将二者连接起来以实现数据共享。它的独特之处,不止于通过总线连接起两台计算设备,而更多体现为一种共享缓存系统的协同关系。在这样的混合设备上,我们能够以不同于以往纯 CPU 或纯 GPU 的方式对其进行编程。在这样的设备上,我们可以让 CPU 专门运行 Linux,让 GPU 专门运行并行编程。

 

参考链接:

https://www.youtube.com/watch?v=dNUMNifgExs

https://www.networkcomputing.com/data-center-networking/nvidia-ai-updates-blackwell-platform-nim-agent-blueprints-and-mlperf

2025-03-12 16:1423
用户头像
李冬梅 加V:busulishang4668

发布了 1031 篇内容, 共 645.6 次阅读, 收获喜欢 1197 次。

关注

评论

发布
暂无评论

华为云发布【云巢】智慧康养物联网加速器,加入立享多项扶持

华为云开发者联盟

物联网 华为云 应用开发 云巢 智慧康养

使用 Jackson – 将字符串转换为 JsonNode 对象

HoneyMoose

模块七作业:王者荣耀商城异地多活架构设计

Felix

架构学习模块二

George

如何修改 Discourse 的域名

HoneyMoose

Redis集群docker部署

非晓为骁

redis Docker redis集群

在智能运维中如何进行指标异常检测与分类?

云智慧AIOps社区

算法 场景应用落地 异常检测 智能运维 指标

顶会CIKM'21论文解读:基于图神经网络的人类行为轨迹恢复模型

华为云开发者联盟

图神经网络 华为云数据库 轨迹分析 CIKM PeriodicMove

Vue进阶(幺零幺):npm install -g 和 npm install --save-dev 的关系

No Silver Bullet

Vue 9月日更

架构实战营模块七作业

maybe

我在 InfoQ 创作的思路规划

baiyutang

写作技巧 9月日更

架构实战营 1 期模块 7 作业——业务异地多活架构

tt

架构实战营

在 Discourse 中如何使用输入对话框

HoneyMoose

【LeetCode】路径总和Java题解

Albert

算法 LeetCode 9月日更

网络攻防学习笔记 Day129

穿过生命散发芬芳

日志分析 9月日更

【Flutter 专题】53 图解 BackdropFilter 高斯模糊

阿策小和尚

Flutter 小菜 0 基础学习 Flutter Android 小菜鸟 9月日更

测试金字塔,你在哪一层?

华为云开发者联盟

软件测试 测试 软件质量 单元测试 华为云DevCloud

原来搭建淘客项目如此简单,app、web、小程序轻松搞定

Silently9527

Java uniapp 淘宝客开源

Django 框架的神奇之处,几行代码就能自动入库,微型博客第 3 篇

梦想橡皮擦

9月日更

贯穿全产业链做数字孪生产品,给你更好的选择

一只数据鲸鱼

数据可视化 工业4.0 制造业 数字孪生

OkHttp源码解读HTTP

Changing Lin

9月日更

国家发改委:利用区块链等新技术开展绿色电力交易试点

CECBC

基于 Apache APISIX,爱奇艺 API 网关的更新与落地实践

API7.ai 技术团队

Apache APISIX Meetup 爱奇艺 企业案例

多环境

程序员鱼皮

Java c++ Python 大前端 后端

Java 8 及其后续版本的新遍历 forEach

HoneyMoose

手撸二叉树之二叉树的所有路径

HelloWorld杰少

9月日更

如何使用 GeoTrellis 和 React 构建地理处理应用程序

gisbook

GitHub spark Web GIS React

架构实战营模块七-王者荣耀商城异地多活架构设计

hello

架构训练营

去中心化身份务实

CECBC

13. AlphaGO带给人类的启示到底是什么

Databri_AI

人工智能

IntelliJ IDEA 快速插入 for 循环

HoneyMoose

软件开发者是硬件开发者的两倍!CUDA“元老级”架构师亲述:最初12人的团队如何成就了英伟达万亿市值_英伟达_李冬梅_InfoQ精选文章