CUDA专项

1、讲讲shared memory bank conflict的发生场景?以及你能想到哪些解决方案?

CUDA中的共享内存(Shared Memory)是GPU上的一种快速内存,通常用于在CUDA线程(Thread)之间共享数据。然而,当多个线程同时访问共享内存的不同位置时,可能会遇到bank conflict(银行冲突)的问题,这会导致性能下降。

Bank Conflict的发生场景

CUDA的共享内存被组织成多个bank,每个bank都可以独立地进行读写操作。然而,当多个线程访问同一个bank的不同地址时,这些访问会被串行化,导致性能下降。具体来说,bank conflict的发生场景如下:

  1. 同一warp中的线程访问同一个bank的不同地址:在CUDA中,线程被组织成warp(线程束),一个warp包含32个线程。如果这32个线程中的某些线程访问同一个bank的不同地址,就会发生bank conflict。
  2. 不规则的访问模式:如果线程访问共享内存的模式是不规则的,即线程访问的地址没有一定的规律,那么bank conflict的发生概率就会增加。

解决方案

  1. 合理的内存分配:通过合理的内存分配策略,将不同数据分配到不同的banks中,从而减少bank conflict的可能性。例如,可以使用CUDA提供的内存对齐工具来确保数据按照bank的大小进行对齐。
  2. 内存访问模式优化:通过优化线程的访存顺序和数据分布,使得不同线程访问的bank地址不重叠,从而避免bank conflict。例如,可以使用循环展开(Loop Unrolling)等技术来减少线程之间的内存访问冲突。
  3. 使用更多的共享内存:如果可能的话,可以增加共享内存的大小,从而减少每个bank的访问压力。但是需要注意的是,共享内存的大小是有限的,因此需要权衡性能和内存使用之间的平衡。
  4. 使用其他内存层次结构:除了共享内存之外,CUDA还提供了其他内存层次结构,如全局内存、常量内存和纹理内存等。在某些情况下,可以使用这些内存层次结构来替代共享内存,从而避免bank conflict的问题。
  5. 使用CUDA提供的内置函数:CUDA提供了一些内置函数来帮助程序员管理共享内存和避免bank conflict。例如,__shfl_sync函数可以用于在warp内部进行数据的交换和广播,从而避免不必要的共享内存访问。
  6. 使用原子操作:如果必须让多个线程同时访问和修改同一个地址的数据,可以使用原子操作(Atomic Operations)来确保数据的一致性。但是需要注意的是,原子操作会引入额外的开销,并且可能会降低性能。

综上所述,避免CUDA共享内存bank conflict的关键在于合理的内存分配、内存访问模式优化以及使用CUDA提供的内置函数和工具。

2、CUDA里面如何分配GPU显存?为什么里面的参数是二级指针?

在CUDA中,GPU显存的分配主要通过使用特定的函数和内存管理策略来实现。以下是关于CUDA中显存分配的基本信息:

  1. 全局内存分配:全局内存是CUDA中最常用的一种显存分配方式。所有线程都可以访问和修改全局内存,它是通过cudaMalloc函数在设备端(即GPU)上进行分配的。由于全局内存的分配是在主机端(即CPU)进行的,因此需要在主机端进行相应的内存管理。

  2. 为什么使用二级指针

    • 在CUDA编程中,当需要向函数(如cudaMalloc)传递一个指针的地址,以便该函数可以修改这个指针的值(即分配的内存地址)时,就需要使用二级指针。这是因为一级指针(即普通的指针)在传递时只能传递指针本身的值(即它所指向的地址),而不能修改这个指针本身。而二级指针(即指向指针的指针)可以传递一个指针的地址,从而允许函数修改这个指针的值。
    • cudaMalloc为例,这个函数的目的是在GPU上分配内存,并将分配的内存地址存储在一个指针中。由于这个操作需要在GPU上进行,而函数的调用是在CPU上进行的,因此需要一个机制来将分配的内存地址从GPU传递回CPU。通过使用二级指针,cudaMalloc可以接收一个指向指针的指针(即二级指针),然后将分配的内存地址存储在这个二级指针所指向的一级指针中。这样,当cudaMalloc返回时,CPU就可以通过这个一级指针访问到在GPU上分配的内存了。
  3. 其他显存分配方式:除了全局内存外,CUDA还支持其他类型的显存分配方式,包括:

    • 共享内存:一种位于GPU上的高速缓存,访问速度比全局内存快。它是在每个线程块(block)中共享的,同一线程块中的线程可以相互通信和共享数据。
    • 常量内存:一种只读的内存,适用于在整个计算过程中不会被修改的数据。
    • 纹理内存:一种特殊的内存,适用于对内存访问具有空间局部性的计算。
    • 局部内存:一种在每个线程中私有的内存,用于存储线程私有的临时变量。

这些不同类型的内存具有不同的访问权限、生命周期和用途,可以根据具体的应用场景和需求来选择合适的内存类型。

3、优化CUDA程序的访存效率,你可以想到哪些?

化CUDA程序的访存效率是一个复杂而重要的任务,以下是一些建议的策略和方法:

  1. 优化内存访问

    • 重新组织数据布局:使数据访问更符合GPU的内存访问机制,减少内存访问延迟。
    • 合并内存访问:通过合并多个内存访问请求,减少访问次数,提高内存访问效率。
    • 利用缓存:通过合理的数据访问模式,尽可能利用GPU的L1和L2缓存,减少全局内存的访问。
  2. 减少线程同步开销

    • 优化算法设计,减少线程同步的次数,以提高GPU的并行计算效率。
    • 使用原子操作(atomic operations)时,要谨慎,因为它们可能会引入额外的同步开销。
  3. 合理使用寄存器

    • 合理使用GPU的寄存器来存储临时数据,以减少数据传输延迟和内存访问开销。
    • 避免过多的寄存器溢出,这会导致额外的内存访问和性能下降。
  4. 使用Pinned Memory

    • Pinned Memory(页锁定存储器)可以更快地在主机和设备之间传输数据。通过cudaHostAlloc函数分配Pinned Memory,并使用cudaHostRegister函数将已分配的变量转换为Pinned Memory。Pinned Memory允许实现主机和设备之间数据的异步传输,从而提高程序的整体性能。
  5. 全局内存访存优化

    • 分析数据流路径,确定是否使用了L1缓存,并据此确定当前内存访问的最小粒度(如32 Bytes或128 Bytes)。
    • 分析原始数据存储的结构,结合访存粒度,确保数据访问的内存对齐和合并访问。
    • 使用Nvprof或Nsight等工具来分析和优化全局内存的访问效率。
  6. 选择合适的CUDA版本和编译器选项

    • 根据GPU的型号和CUDA版本,选择最适合的编译器选项和内存访问模式。
    • 关注CUDA的更新和改进,以利用新的功能和优化。
  7. 算法和代码优化

    • 优化算法和数据结构,减少不必要的计算和内存访问。
    • 使用循环展开(loop unrolling)和向量化(vectorization)等技术来提高代码的执行效率。
    • 避免在内核函数中使用复杂的条件语句和循环,以减少分支预测错误和同步开销。
  8. 内存管理优化

    • 使用内存池(memory pooling)技术来管理GPU内存,减少内存分配和释放的开销。
    • 在必要时,使用零拷贝(zero-copy)技术来避免不必要的数据传输。
  9. 性能分析和调试

    • 使用CUDA的性能分析工具(如Nsight和Visual Profiler)来分析和识别性能瓶颈。
    • 根据性能分析结果,针对瓶颈进行针对性的优化。
  10. 注意GPU的硬件特性

    • 了解GPU的硬件特性,如缓存大小、内存带宽和延迟等,以编写更高效的CUDA代码。
    • 根据硬件特性选择合适的优化策略和方法。

通过综合应用以上策略和方法,可以有效地提高CUDA程序的访存效率,从而提升程序的整体性能。

4、优化CUDA程序的计算效率,你又可以想到哪些?

优化CUDA程序的计算效率是一个复杂但重要的任务,以下是一些可以考虑的优化策略:

  1. 优化内存访问
    • 重新组织数据布局,使得数据访问更符合GPU的内存访问机制,以减少内存访问延迟。
    • 合并多个内存访问请求,以减少访问次数,提高内存访问效率。
    • 尽量减少Host(CPU)和Device(GPU)之间的数据拷贝,通过优化算法和数据结构来减少数据传输的开销。
  2. 合理使用GPU资源
    • 在配置kernel时,分配合理的thread(线程)个数和block(线程块)个数,以最大化device的使用效率,充分利用硬件资源。
    • 尽可能使用shared memory(共享内存)来存储需要频繁访问的数据,以减少对global memory(全局内存)的访问次数。
  3. 减少线程同步开销
    • 优化算法设计,减少线程同步的次数,以提高GPU的并行计算效率。
    • 在同一个warp(线程束)中,尽量减少分支,以减少线程之间的分歧和同步开销。
  4. 优化算法和数据结构
    • 将串行代码并行化,特别是针对可以并行化的循环结构,如for循环。
    • 使用更高效的数据结构和算法来减少计算量和内存使用。
  5. 注意数据类型和精度
    • 在可能的情况下,使用更小的数据类型来减少内存使用和传输开销。
    • 注意浮点数的精度问题,避免不必要的精度损失和计算开销。
  6. 编译器优化
    • 使用合适的编译器选项和设置来优化代码生成和性能。
    • 了解并使用CUDA编译器提供的性能分析工具来找出性能瓶颈和优化点。
  7. 硬件和驱动优化
    • 确保GPU驱动和硬件是最新的,以获得最佳的性能和兼容性。
    • 根据具体的应用场景和硬件特性,调整CUDA程序的配置和参数设置。
  8. 使用CUDA提供的内置函数和库
    • CUDA提供了许多内置函数和库,如数学函数库、内存管理库等,这些函数和库经过优化,可以提供更高的性能。
  9. 代码审查和重构
    • 定期进行代码审查,找出潜在的性能问题和改进点。
    • 对代码进行重构和优化,以提高代码的可读性、可维护性和性能。
  10. 测试和验证
    • 在不同的硬件和配置下测试CUDA程序的性能,确保优化策略的有效性。
    • 使用验证数据集来验证CUDA程序的正确性和准确性。

请注意,优化CUDA程序的计算效率是一个持续的过程,需要不断地进行实验和调整。同时,不同的应用场景和硬件环境可能需要不同的优化策略。

1、GPU是如何与CPU协调工作的?
 

GPU与CPU的协调工作主要通过它们之间的接口和通信机制实现。具体而言,以下是它们协同工作的一般流程:

  1. 任务分配:CPU作为计算机系统的主要控制单元,负责将需要处理的任务分配给不同的处理器。当任务涉及大量的图形、图像处理或视频编码等计算密集型任务时,CPU会将这些任务分配给GPU处理。
  2. 数据传输:在任务分配后,CPU需要将相关的数据传输给GPU。这通常通过内存总线或专用接口(如PCIe总线)进行。数据传输的速度和带宽对于整个系统的性能至关重要,因为它们决定了GPU能够多快地获取所需的数据。
  3. 并行处理:GPU接收到数据后,会利用其大量的核心进行并行处理。GPU的核心数量远多于CPU,因此能够同时处理更多的任务和数据。这使得GPU在处理计算密集型任务时具有显著的优势。
  4. 结果回传:当GPU完成处理后,会将结果回传给CPU。CPU会对这些结果进行处理和整合,以便后续使用或输出。

在硬件层面,GPU和CPU的协调工作主要通过以下方式实现:

  1. 架构差异:CPU的架构通常是基于冯·诺依曼体系结构的,采用串行的方式执行指令,每个时钟周期只能执行一条指令。而GPU的架构通常是基于SIMD(单指令多数据流)的,采用并行的方式执行指令,每个时钟周期可以执行多条指令。这种架构差异使得GPU更适合处理计算密集型任务。
  2. 内存和缓存:GPU拥有独立的显存和缓存机制,用于存储和处理图形和图像数据。这些内存和缓存与CPU的内存和缓存是分开的,但可以通过内存总线或专用接口进行通信。在数据处理过程中,CPU和GPU会根据需要相互协作,将数据从主内存传输到显存或缓存中。
  3. 通信接口:CPU和GPU之间的通信主要通过PCIe总线等接口进行。这些接口提供了高速的数据传输通道,使得CPU和GPU能够快速地交换数据和指令。

在编程和开发层面,程序员可以通过特定的编程语言和库(如CUDA、OpenCL等)来利用GPU进行加速计算。这些库提供了丰富的API和工具,使得程序员能够轻松地编写和调试GPU程序,并将其与CPU程序进行集成和协同工作。

总之,GPU和CPU的协调工作是通过它们之间的接口、通信机制以及编程和开发层面的支持来实现的。这种协同工作使得计算机能够更高效地处理各种任务和数据,提高了整个系统的性能和效率。

2、GPU也有缓存机制吗?有几层?它们的速度差异多少?

GPU也有缓存机制,但通常主流GPU芯片上的缓存层数比CPU少。主流CPU芯片上有四级缓存,而主流GPU芯片最多有两层缓存。

关于GPU缓存的速度差异,一般来说,离处理器越近的缓存级别速度越快,但容量也越小。例如,L1缓存(一级缓存)的速度最快,但容量最小;L2缓存(二级缓存)的速度稍慢,但容量较大。这种设计是为了在速度和容量之间找到一个平衡,以便在处理数据时能够快速访问到所需的数据。

然而,具体的速度差异取决于具体的处理器和缓存设计。不同的GPU型号和制造商可能会使用不同的缓存设计和架构,因此它们之间的速度差异也会有所不同。

总的来说,GPU的缓存机制对于提高处理器的性能和效率非常重要,但具体的缓存层数和速度差异取决于处理器的设计和制造商的选择。

3、GPU的渲染流程有哪些阶段?它们的功能分别是什么?

  1. 顶点处理(Vertex Processing)
    • 功能:此阶段主要负责处理输入的顶点数据,包括三维坐标(x, y, z)和其他顶点属性(如颜色、法线等)。通过顶点着色器(Vertex Shader)对这些顶点进行变换,将三维顶点坐标映射到二维屏幕坐标上,并计算各顶点的亮度值等。
    • 特点:这个阶段是可编程的,允许开发者定义自己的顶点处理逻辑。输入与输出一一对应,即一个顶点被处理后仍然是一个顶点,各顶点间的处理相互独立,可以并行完成。
  2. 图元生成(Primitive Generation)
    • 功能:根据应用程序定义的顶点拓扑逻辑(如三角形、线段等),将上阶段输出的顶点组织起来形成有序的图元流。这些图元记录了由哪些顶点组成,以及它们在输出流中的顺序。
  3. 图元处理(Primitive Processing)
    • 功能:此阶段进一步处理图元,通常通过几何着色器(Geometry Shader)完成。几何着色器可以创建新的图元(例如,将点转换为线,或将线转换为三角形),也可以丢弃图元。这个阶段也是可编程的,允许开发者定义自己的图元处理逻辑。
  4. 光栅化(Rasterization)
    • 功能:光栅化阶段将图元转换为像素(片段),并为每个像素生成一个片元记录。这些片元记录包含了像素在屏幕空间中的位置、与视点之间的距离以及通过插值获得的顶点属性等信息。
    • 特点:这一阶段会对每一个图元在屏幕空间进行采样,每个采样点对应一个片元记录。
  5. 片元处理(Fragment Processing)
    • 功能:在片元着色器(Fragment Shader)中,对每个片元进行颜色计算和纹理映射等操作。片元着色器会考虑各种因素(如光照、材质等),为每个片元计算出最终的颜色值。
  6. 屏幕映射(Screen Mapping)
    • 功能:此阶段将处理后的片元信息映射到屏幕坐标系中,以便最终显示在屏幕上。
  7. 输出合并(Output Merging)
    • 功能:在这一阶段,将片元着色器输出的颜色与屏幕上已有的颜色进行合并。这通常包括深度测试(确保物体按正确的顺序渲染)、模板测试(用于实现特殊效果,如阴影)和混合(用于实现透明效果)等操作。

需要注意的是,不同的GPU架构和渲染引擎可能会有一些细微的差别,但上述阶段和功能是GPU渲染流程中比较通用的部分。

4、Early-Z技术是什么?发生在哪个阶段?这个阶段还会发生什么?会产生什么问题?如何解决?

5、SIMD和SIMT是什么?它们的好处是什么?co-issue呢?

6、GPU是并行处理的么?若是,硬件层是如何设计和实现的?

7、GPC、TPC、SM是什么?Warp又是什么?它们和Core、Thread之间的关系如何?

8、顶点着色器(VS)和像素着色器(PS)可以是同一处理单元吗?为什么?

顶点着色器(VS)和像素着色器(PS)不是同一处理单元。尽管它们都是GPU中的可编程着色阶段,但它们各自执行不同的任务和具有不同的功能。

  1. 顶点着色器(VS)阶段处理输入汇编程序的顶点,执行每个顶点运算,例如转换、外观、变形和每顶点照明。顶点着色器始终在单个输入顶点上运行并生成单个输出顶点。它的主要任务是处理图形的顶点数据,进行坐标变换、光照计算等操作。
  2. 像素着色器(PS)阶段则支持丰富的着色技术,如每像素照明和后处理。像素着色器是一个程序,它将常变量、纹理数据、内插的每顶点值和其他数据组合起来以生成每像素输出。它的主要任务是对每个像素进行颜色计算和渲染,以实现更丰富的视觉效果。

由于顶点着色器和像素着色器在图形渲染过程中执行的任务不同,因此它们需要不同的处理单元来分别处理。在GPU中,通常会有多个顶点着色器和像素着色器的处理单元,以便能够并行处理多个顶点和像素的数据,提高渲染效率。

因此,顶点着色器和像素着色器不是同一处理单元,它们在图形渲染过程中各自扮演着不同的角色,协同工作以实现高效的图形渲染。

9、像素着色器(PS)的最小处理单位是1像素吗?为什么?会带来什么影响?

10、Shader中的if、for等语句会降低渲染效率吗?为什么?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/10464.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BUUCTF[PWN]

BUUCTF[PWN] 题目:warmup_csaw_2016 地址:warmup_csaw_2016ida打开,进main函数:gets函数的栈溢出:给出了sub_40060D函数的地址直接,溢出到sub_40060D的地址即可: from pwn import *p remote…

[Cmake Qt]找不到文件ui_xx.h的问题?有关Qt工程的问题,看这篇文章就行了。

前言 最近在开发一个组件,但是这个东西是以dll的形式发布的界面库,所以在开发的时候就需要上层调用。 如果你是很懂CMake的话,ui_xx.h的文件目录在 ${CMAKE_CURRENT_BINARY_DIR} 下 然后除了有关这个ui_xx.h,还有一些别的可以简…

Verlog-流水灯-FPGA

Verlog-流水灯-FPGA 引言: ​ 随着电子技术的飞速发展,现场可编程门阵列(FPGA)已成为电子设计自动化(EDA)领域中不可或缺的组件。FPGA以其高度的灵活性和可定制性,广泛应用于通信、图像处理、工…

go-zero整合asynq实现分布式定时任务

本教程基于go-zero微服务入门教程,项目工程结构同上一个教程。 go-zero微服务入门教程(点击进入) 本教程主要实现go-zero整合asynq实现分布式定时任务。 本文源码:https://gitee.com/songfayuan/go-zero-demo (教程源…

外卖点餐单店+多店自由切换小程序源码系统全功能版 带完整的安装代码包以及搭建部署教程

近年来,外卖市场持续火爆,但许多餐饮商家在接入外卖平台时面临着诸多困扰。高昂的平台费用、复杂的操作流程以及数据安全隐患等问题,让商家们倍感压力。为了解决这些问题,小编给大家分享一款集单店与多店管理于一体的外卖点餐系统…

ACM实训冲刺第四天

【碎碎念】最近的任务有点繁重,所以考虑到实际情况,视频学习决定放置一段时间,重点是学校的实训练习题,对于我而言,目标不是优秀/良好,综合考虑我的实际情况,保佑我及格、顺利通过就可&#xff…

通过自建镜像方式搭建RabbitMQ集群

通过自建镜像方式搭建RabbitMQ集群 1. 应用准备1.1 应用目录结构1.2 配置文件1.2.1 .erlang.cookie1.2.2 hosts1.2.3 rabbitmq.conf1.2.4 rabbitmq-env.conf 2. 编写DockerFile2.1 将所有本地文件拷贝到工作目录2.2 拷贝文件到源目录&增加执行权限2.3 安装Erlang & rab…

Leedcode题目:移除链表元素

题目: 这个题目就是要我们将我们的链表中的值是val的节点删除。 我们题目提供的接口是 传入了指向一个链表的第一个节点的指针,和我们要删除的元素的值val,不只要删除第一个, 思路 我们这里可以创建一个新的链表,…

【C++】学习笔记——模板进阶

文章目录 十一、模板进阶1. 非类型模板参数2. 按需实例化3. 模板的特化类模板的特化 4. 模板的分离编译 未完待续 十一、模板进阶 1. 非类型模板参数 模板参数分为类型形参和非类型形参 。类型形参即:出现在模板参数列表中,跟在class或者typename之类的…

掌握SEO优化的关键:提升网站排名的秘籍(如何提高网站seo排名)

你是否曾经在搜索引擎上搜索过一个关键词,然后点击了排在前几位的网站?如果是,那么你已经体会到了SEO(搜索引擎优化)的威力。SEO是一项关键的网络营销策略,它能够让你的网站在搜索引擎中获得更高的排名&…

Apache ECharts

Apache ECharts介绍: Apache ECharts 是一款基于 Javascript 的数据可视化图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表。 官网地址:https://echarts.apache.org/zh/index.html Apache ECh…

Stable Diffusion写真完整教程

前言 最近自己对AI非常痴迷,并且今后也会一直在这个领域深耕,所以就想着先入门,因此花时间研究了一番,还好,出了点小成果,接下来给大家汇报一下。 AI绘画 提到AI绘画,大家可能立马会想到made…

A-loam建图算法

A-LOAM构建3d点云地图并实时转存二维栅格地图 A-loam算法。源代码用的是velodyne雷达话题,但是现在用rslidar来处理。所以也会遇到另外一个包来转换相关的数据。 git clone https://github.com/HKUST-Aerial-Robotics/A-LOAM.githttps://github.com/HViktorTsoi/r…

重庆市工程技术生态环境专业职称申报条件

重庆市工程技术生态环境专业职称申报条件链接重庆市人力资源和社会保障局 重庆市生态环境局关于印发重庆市工程技术生态环境专业职称申报条件的通知_重庆市人力资源和社会保障局类别基本条件业绩成果备注工程师具备博士学位;或具备硕士学位或第二学士学位&#xff0…

cin.ignore()函数和stoll函数

cin.ignore()函数 cin.ignore() 是一个非常实用的函数,主要用于控制输入流 cin 的行为 cin.ignore(int n 1, char delimiter EOF); n:一个整数参数,表示要忽略的字符数量。默认值是1,意味着只忽略下一个字符。delimiter&#x…

Android 屏幕适配全攻略(下)-百变屏幕无压力,这才是Android屏幕适配的终极解决方案

在上一篇文章中,我们介绍了Android屏幕适配的基本方法,比如使用限定符资源、图片适配、矢量图等。 感兴趣的朋友,请前往查阅:Android 屏幕适配全攻略(中)-从九宫格到矢量图,揭秘Android多屏幕适…

模拟集成电路(3)----单级放大器(共源极)

模拟集成电路(3)----单级放大器(共源极) 放大是模拟电路的基本功能 大多数自然模拟信号太小而无法处理需要足够的信噪比 理想的放大器 线性:无限的幅度和频率范围 输入阻抗无限大 输出阻抗无限小 共源放大器 共源放大器就是将源极接A…

01面向类的讲解

指针指向类成员使用 代码&#xff1a; #include<iostream> using namespace std;class Test { public:void func() { cout << "call Test::func" << endl; }static void static_func();int ma;static int mb; //不依赖对象 }; void Test::static…

JavaScript 动态网页实例 —— 事件处理应用

前言 事件处理的应用很广泛。在事件处理的应用中,鼠标事件的应用是最常用到的。本章给出几个鼠标事件处理应用的示例,包括:页面预览、图像切换、点亮文本、鼠标跟随、鼠标感应和禁用鼠标按键。在这些示例中,有的可以直接拿来应用,有的则只提供了一种应用的方法,稍加拓展,…

示例十一、声音传感器

通过以下几个示例来具体展开学习,了解声音传感器原理及特性&#xff0c;学习声音传感器的应用&#xff08;干货版&#xff09;&#xff1a; 示例十一、声音传感器 ino文件源码&#xff1a; //Arduino C demo void setup() {Serial.begin(9600);pinMode(5, OUTPUT); }void loo…