RISC和CISC的比较

6 阅读15分钟

大家好,我是良许

在嵌入式开发领域,处理器架构的选择直接影响着系统的性能、功耗和开发成本。

作为一名从事嵌入式开发多年的程序员,我经常需要在不同架构的处理器上进行开发工作。

今天,我想和大家聊聊处理器架构中的两大流派:RISC(精简指令集计算机)和 CISC(复杂指令集计算机)。

这两种架构各有千秋,理解它们的差异对于我们选择合适的硬件平台至关重要。

1. 什么是 RISC 和 CISC

1.1 CISC 架构的诞生背景

CISC 架构诞生于计算机发展的早期阶段,那时候内存非常昂贵,程序员主要使用汇编语言编程。

为了减少程序占用的内存空间,处理器设计者们想出了一个办法:让单条指令能够完成更复杂的操作。

这样一来,原本需要多条指令才能完成的任务,现在只需要一条指令就能搞定,程序的体积自然就小了。

典型的 CISC 处理器包括 Intel 的 x86 系列,这也是我们日常使用的 PC 机所采用的架构。

CISC 指令集包含了大量的指令,有些指令非常复杂,可以直接操作内存,甚至能完成循环、字符串处理等高级操作。

1.2 RISC 架构的设计理念

到了 20 世纪 80 年代,随着编译器技术的进步和内存成本的下降,研究人员发现,程序中实际使用频率高的往往是那些简单的指令,而复杂指令的使用频率很低。

于是,RISC 架构应运而生,它的核心思想是:简化指令集,让每条指令都能在一个时钟周期内完成,通过提高指令执行效率来提升整体性能。

ARM 处理器就是 RISC 架构的典型代表,在嵌入式领域占据着统治地位。

我们常用的 STM32 系列单片机,采用的就是 ARM Cortex-M 内核,这是一种 32 位的 RISC 架构处理器。

此外,MIPS、RISC-V 等也都属于 RISC 架构。

2. 指令集设计的核心差异

2.1 指令数量和复杂度

CISC 架构的指令集非常庞大,x86 架构的指令数量可以达到数百条甚至上千条。

这些指令的长度不固定,有的只有 1 个字节,有的可能长达 15 个字节。

指令的功能也千差万别,从简单的加法运算到复杂的字符串搜索,应有尽有。

相比之下,RISC 架构的指令集要精简得多,通常只有几十到一百多条指令。

以 ARM Cortex-M3 为例,它的指令集大约包含 100 多条指令。

更重要的是,RISC 指令的长度是固定的,ARM 架构大多数指令都是 32 位(4 字节),Thumb 指令集则是 16 位(2 字节)。

这种固定长度的设计大大简化了指令的译码过程。

2.2 寻址模式

CISC 架构支持非常丰富的寻址模式,x86 架构可以支持十几种寻址方式。

这意味着一条指令可以直接访问内存、使用基址加偏移、间接寻址等多种方式来获取操作数。

例如,在 x86 架构中,你可以写出这样的指令:

MOV EAX, [EBX + ECX*4 + 0x1000]

这条指令直接从内存地址(EBX + ECX*4 + 0x1000)读取数据到 EAX 寄存器,一条指令就完成了复杂的地址计算和内存访问。

RISC 架构则大幅简化了寻址模式,通常只支持几种基本的寻址方式。

在 ARM 架构中,内存访问必须通过专门的加载/存储指令(Load/Store)来完成,而算术逻辑运算只能在寄存器之间进行。

这就是著名的"Load-Store 架构"。

例如,要完成上面类似的操作,在 ARM 中需要这样写:

ADD R0, R1, R2, LSL #2  ; R0 = R1 + R2*4
ADD R0, R0, #0x1000     ; R0 = R0 + 0x1000
LDR R3, [R0]            ; R3 = *R0

虽然需要多条指令,但每条指令都很简单,执行速度快。

2.3 指令执行周期

CISC 架构的指令执行周期差异很大。

简单的指令可能只需要 1-2 个时钟周期,而复杂的指令可能需要几十甚至上百个时钟周期。

这种不确定性给流水线设计带来了很大的挑战。

RISC 架构追求的目标是让大部分指令都能在一个时钟周期内完成(在理想的流水线情况下)。

虽然实际上由于流水线冒险、分支预测失败等原因,并不是所有指令都能达到这个目标,但 RISC 的指令执行时间相对更加可预测,这对于实时系统来说是一个重要优势。

3. 硬件实现的差异

3.1 芯片复杂度

CISC 处理器为了支持复杂的指令集,需要更复杂的译码逻辑和微代码控制单元。

一条复杂的 CISC 指令在执行时,实际上会被分解成多个微操作(micro-ops),这需要额外的硬件资源。

因此,CISC 处理器的晶体管数量通常更多,芯片面积更大。

RISC 处理器的设计相对简单,指令译码器的逻辑更加直接。

由于指令格式规整,可以使用更高效的流水线设计。

在相同的制程工艺下,RISC 处理器可以用更少的晶体管实现相同的功能,或者在相同的芯片面积下集成更多的核心。

这也是为什么 ARM 处理器能够在移动设备和嵌入式系统中占据主导地位的重要原因之一。

3.2 功耗特性

功耗是嵌入式系统设计中的关键考量因素。

RISC 架构由于硬件结构相对简单,指令执行效率高,通常具有更好的能效比。

这对于电池供电的设备来说至关重要。

以 STM32 系列单片机为例,它们采用 ARM Cortex-M 内核,在低功耗模式下可以将功耗降到微安级别。

我在做一个电池供电的物联网项目时,使用 STM32L 系列超低功耗单片机,通过合理的功耗管理,让设备在一颗纽扣电池的供电下运行了两年多。

这在 CISC 架构的处理器上是很难实现的。

3.3 寄存器数量

CISC 架构通常提供较少的通用寄存器。

例如,x86 架构在 32 位模式下只有 8 个通用寄存器(EAX、EBX、ECX、EDX、ESI、EDI、EBP、ESP),而且其中一些还有特殊用途。

这意味着编译器在生成代码时,经常需要将变量存储到内存中,导致频繁的内存访问。

RISC 架构则提供了更多的通用寄存器。

ARM 架构有 16 个通用寄存器(R0-R15),RISC-V 架构有 32 个通用寄存器。

更多的寄存器意味着编译器可以将更多的变量保存在寄存器中,减少内存访问,从而提高程序的执行效率。

4. 编程和开发的影响

4.1 汇编语言编程

在汇编语言层面,CISC 架构的指令更接近高级语言的语义,一条指令可以完成更多的工作。

对于手写汇编代码的程序员来说,CISC 指令集可能更加直观。

但是,由于指令复杂,程序员需要记忆大量的指令和寻址模式,学习曲线较陡。

RISC 架构的汇编语言虽然指令简单,但要完成同样的功能可能需要更多的指令。

不过,由于指令规整,学习起来相对容易。

在实际的嵌入式开发中,我们很少直接编写大量的汇编代码,通常只在启动代码、中断处理等关键部分使用汇编。

下面是一个简单的例子,展示在 STM32 上使用内联汇编来实现原子操作:

// 使用ARM汇编实现原子加法
uint32_t atomic_add(volatile uint32_t *ptr, uint32_t value)
{
    uint32_t result;
    uint32_t tmp;
    
    __asm__ volatile(
        "1: LDREX %0, [%2]\n"        // 独占加载
        "   ADD %0, %0, %3\n"         // 加法运算
        "   STREX %1, %0, [%2]\n"     // 独占存储
        "   CMP %1, #0\n"             // 检查是否成功
        "   BNE 1b\n"                 // 失败则重试
        : "=&r" (result), "=&r" (tmp)
        : "r" (ptr), "r" (value)
        : "cc", "memory"
    );
    
    return result;
}

这段代码使用了 ARM 的 LDREX/STREX 指令来实现原子操作,这是 RISC 架构中实现同步原语的典型方式。

4.2 编译器优化

现代编译器技术的发展,使得高级语言程序员不再需要过多关注底层指令集的差异。

编译器会根据目标架构的特点进行优化。

对于 CISC 架构,编译器需要在众多复杂指令中选择最合适的指令组合,这增加了编译器的复杂度。

而且,由于指令执行时间不确定,编译器很难准确估计代码的执行时间。

对于 RISC 架构,编译器的优化工作相对更加直接。

由于指令简单规整,编译器可以更好地进行指令调度、寄存器分配等优化。

在嵌入式开发中,我们使用 GCC 或 Keil 等工具链编译代码时,编译器会针对 ARM 架构进行各种优化,比如循环展开、函数内联、常量传播等。

4.3 代码密度

代码密度是指单位内存空间能存储多少有效代码。

在嵌入式系统中,Flash 存储空间往往是有限的,代码密度就显得尤为重要。

CISC 架构由于指令长度可变,且单条指令功能强大,理论上可以获得更高的代码密度。

但实际情况并非总是如此,因为复杂指令的使用频率可能不高。

RISC 架构虽然指令长度固定,但为了提高代码密度,也发展出了一些变体。

例如,ARM 的 Thumb 指令集使用 16 位编码,可以显著减小代码体积。

在 STM32 开发中,我们通常会启用 Thumb-2 指令集,它混合使用 16 位和 32 位指令,既保证了性能,又提高了代码密度。

// 在STM32项目中,编译器选项通常会包含:
// -mthumb -mthumb-interwork
// 这样编译出来的代码会使用Thumb指令集
void delay_ms(uint32_t ms)
{
    uint32_t i, j;
    for(i = 0; i < ms; i++) {
        for(j = 0; j < 1000; j++) {
            __NOP();  // 空操作,编译为Thumb指令
        }
    }
}

5. 性能和效率对比

5.1 计算性能

在计算性能方面,不能简单地说哪种架构更优秀,这取决于具体的应用场景和实现方式。

CISC 架构的复杂指令可以在某些特定任务上展现优势。

例如,x86 架构的 SSE、AVX 等 SIMD 指令集,可以一次处理多个数据,在多媒体处理、科学计算等领域表现出色。

RISC 架构通过提高时钟频率和优化流水线来提升性能。

在嵌入式领域,ARM Cortex-M 系列处理器虽然主频不高(通常在几十到几百 MHz),但由于指令执行效率高,在实际应用中性能表现良好。

我在使用 STM32F4 系列单片机(主频 168MHz)开发音频处理项目时,通过合理的算法优化和 DMA 配置,可以实时处理 48kHz 采样率的音频信号。

5.2 实时性能

在嵌入式系统中,实时性能往往比平均性能更重要。

RISC 架构的指令执行时间更加可预测,这对于硬实时系统来说是一个重要优势。

例如,在电机控制应用中,我们需要在精确的时间点更新 PWM 占空比。

使用 STM32 单片机时,我可以比较准确地估算中断服务程序的执行时间,因为每条 ARM 指令的执行周期是相对确定的。

这在 CISC 架构上就比较困难,因为指令执行时间的变化范围较大。

5.3 中断响应

中断响应时间是嵌入式系统的关键指标之一。

RISC 架构通常具有更快的中断响应速度,因为中断处理的硬件逻辑相对简单。

在 STM32 中,中断响应包括以下步骤:保存当前程序状态、跳转到中断向量表、执行中断服务程序、恢复程序状态。

由于 ARM Cortex-M 的硬件设计优化,这个过程可以在很短的时间内完成。以下是一个典型的中断服务程序示例:

// STM32的外部中断处理
void EXTI0_IRQHandler(void)
{
    // 检查中断标志
    if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0) != RESET) {
        // 清除中断标志
        __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
        
        // 执行中断处理逻辑
        // 由于ARM指令简单,这里的代码执行时间可预测
        HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
    }
}

6. 实际应用场景

6.1 嵌入式系统

在嵌入式领域,RISC 架构占据绝对主导地位。

ARM 处理器几乎统治了整个嵌入式市场,从简单的 8 位单片机替代品(Cortex-M0),到高性能的应用处理器(Cortex-A 系列),都能看到 ARM 的身影。

我在做嵌入式 Linux 开发时,使用的是基于 ARM Cortex-A 系列的处理器。

这些处理器不仅性能强大,而且功耗控制得很好,非常适合汽车电子等对可靠性和功耗都有严格要求的应用。

6.2 服务器和数据中心

传统上,服务器市场一直是 x86 架构的天下。

但近年来,ARM 架构也开始进军这个领域。

AWS 的 Graviton 处理器就是基于 ARM 架构的服务器芯片,它在能效比方面表现出色,对于云计算这种需要大规模部署的场景来说,能效比的提升意味着巨大的成本节约。

6.3 移动设备

智能手机和平板电脑几乎全部采用 ARM 架构的处理器。

苹果的 A 系列芯片、高通的骁龙系列、华为的麒麟系列,都是基于 ARM 架构。

这些处理器需要在有限的电池容量下提供强大的性能,RISC 架构的高能效比优势在这里得到了充分体现。

7. 架构融合的趋势

7.1 CISC 向 RISC 学习

现代的 x86 处理器已经不是纯粹的 CISC 架构了。

从 Pentium Pro 开始,Intel 就在处理器内部将复杂的 x86 指令转换成类似 RISC 的微操作(micro-ops),然后用 RISC 风格的执行引擎来执行这些微操作。

这种设计既保持了对 x86 指令集的兼容性,又获得了 RISC 架构的性能优势。

7.2 RISC 的扩展

RISC 架构也在不断发展,增加了一些复杂指令来提高特定任务的性能。

例如,ARM 架构增加了 NEON SIMD 指令集,用于加速多媒体处理;增加了 DSP 指令,用于数字信号处理。

这些扩展指令虽然增加了指令集的复杂度,但都是在保持核心设计理念的基础上进行的。

7.3 新兴架构

RISC-V 是近年来备受关注的开源指令集架构。

它继承了 RISC 的设计理念,同时吸取了过去几十年处理器设计的经验教训。

RISC-V 采用模块化设计,基础指令集非常精简,用户可以根据需要添加扩展指令集。

这种灵活性使得 RISC-V 在物联网、嵌入式等领域展现出巨大潜力。

8. 总结

RISC 和 CISC 代表了处理器设计的两种不同哲学。

CISC 追求用复杂的指令来简化编程,RISC 则追求用简单的指令来提高执行效率。

经过几十年的发展,两种架构都在相互借鉴,界限已经不再那么清晰。

在嵌入式开发领域,我们更多地接触到的是 RISC 架构,特别是 ARM 处理器。

理解 RISC 和 CISC 的差异,可以帮助我们更好地理解处理器的工作原理,写出更高效的代码。

无论是在做 STM32 的裸机开发,还是在做嵌入式 Linux 的应用开发,这些底层知识都是非常有价值的。

选择哪种架构,最终还是要根据具体的应用需求来决定。

对于需要高性能计算、兼容性要求高的场景,x86 架构可能是更好的选择;对于功耗敏感、实时性要求高的嵌入式应用,ARM 等 RISC 架构则更有优势。

作为嵌入式开发者,我们需要根据项目的具体需求,选择最合适的硬件平台,并充分发挥其优势。

更多编程学习资源