揭示C语言中CPU对register变量分配的决策过程

news2024/5/20 16:16:50

揭示C语言中CPU对register变量分配的决策过程

  • 博主简介
  • 一、引言
    • 1.1、register变量的定义和用途
    • 1.2、CPU对register变量分配的重要性
  • 二、CPU寄存器分配的概述
    • 2.1、CPU寄存器的作用和程序执行过程中的角色
    • 2.2、不同类型的CPU寄存器(通用寄存器、特殊寄存器等)
    • 2.3、寄存器分配在优化程序性能方面的重要性
  • 三、影响CPU寄存器分配register变量的因素
    • 3.1、C语言中的`register`关键字以及它对编译器优化的影响
    • 3.2、编译器在决定register变量分配时的作用
    • 3.3、编译器使用的启发式算法和决策过程
  • 四、编译器优化和寄存器分配
    • 4.1、常见的编译器优化技术概述(常量传播、循环展开等)
    • 4.2、这些优化技术与寄存器分配决策之间的关系
    • 4.3、示例展示寄存器分配如何影响程序的效率和性能?
  • 五、CPU架构和寄存器可用性
    • 5.1、不同CPU架构及其寄存器配置
    • 5.2、CPU设计如何影响C语言中的寄存器分配决策
    • 5.3、寄存器可用性与其他因素(缓存利用、流水线等)之间的权衡
  • 总结

博主简介


💡一个热爱分享高性能服务器后台开发知识的博主,目标是通过理论与代码实践的结合,让世界上看似难以掌握的技术变得易于理解与掌握。技能涵盖了多个领域,包括C/C++、Linux、Nginx、MySQL、Redis、fastdfs、kafka、Docker、TCP/IP、协程、DPDK等。
👉
🎖️ CSDN实力新星、CSDN博客专家、华为云云享专家
👉
👉我的博客将提供以下内容:
👉
💡1. 高性能服务器后台开发知识深入剖析:深入探讨各种技术的原理和内部工作机制,帮助理解它们的核心概念和使用方法。
👉
💡2. 实践案例与代码分享:分享一些实际项目中的应用案例和代码实现,帮助将理论知识转化为实际应用,并提供实践中的经验和技巧。
👉
💡3. 技术教程和指南:编写简明扼要的教程和指南,帮助初学者入门并逐步掌握这些技术,同时也为有经验的开发者提供深入的技术进阶指导。
👉
💡无论是一个刚入门的初学者,还是一个有经验的开发者,我的博客都将提供有价值的内容和实用的技术指导。


一、引言

1.1、register变量的定义和用途

在C语言中,register是一个存储类别(storage class),用于声明寄存器变量。它的作用是告诉编译器将该变量存储在CPU的寄存器中,以提高访问速度。

register关键字可以用来修饰局部变量,但不能修饰全局变量或函数参数。它的使用方式如下:

register int x;

register变量具有以下特点:

  1. 寄存器变量的访问速度比内存变量快,因为它们存储在CPU的寄存器中,而不是内存中。
  2. register变量的大小通常与寄存器的大小相等,一般为机器字长。
  3. 由于寄存器数量是有限的,所以编译器可能会忽略对register变量的请求,将其视为普通的自动变量。
  4. register变量不能取地址,因为它们没有分配内存地址。

然而,在现代的编译器优化技术中,register关键字的使用已经不常见了。编译器通常能够根据代码的上下文自动进行寄存器分配,并且能够更好地进行优化。因此,大多数情况下,程序员无需手动使用register关键字来声明寄存器变量。

1.2、CPU对register变量分配的重要性

CPU对register变量的分配是非常重要的,这是因为寄存器是CPU内部最快速的存储器。与内存相比,寄存器的访问速度更快,因此将关键的变量保存在寄存器中可以大大提高程序的性能。

通过将变量存储在寄存器中,可以避免内存读写操作,从而减少了访问延迟,提高了程序的执行速度。另外,寄存器还可以在多个指令之间快速传递数据,进一步加快了程序的运行效率。

然而,由于寄存器数量有限,CPU必须进行智能的寄存器分配。编译器使用各种算法和优化策略来确定哪些变量应该放入寄存器中,以及何时应该将其存储在内存中。这个过程被称为寄存器分配或寄存器分配器。

寄存器分配的目标是最大限度地利用寄存器,并确保最频繁访问的变量尽可能保存在寄存器中。当一个变量无法分配到寄存器时,它将被存储在内存中,这会导致额外的内存读写开销。因此,良好的寄存器分配对于提高程序性能至关重要。

对于现代的编译器来说,它们已经具备了非常复杂的寄存器分配算法和优化策略,能够自动地进行寄存器分配。因此,在大多数情况下,程序员无需手动使用register关键字来声明寄存器变量。编译器可以更好地判断哪些变量应该存储在寄存器中,并进行相应的优化。

二、CPU寄存器分配的概述

2.1、CPU寄存器的作用和程序执行过程中的角色

CPU寄存器是位于中央处理器内部的一组用于存储和操作数据的高速存储器。它们对于计算机程序的执行起着重要的作用,可以承担多种角色。

首先,寄存器在程序执行过程中承担临时存储数据的作用。当程序需要进行计算或操作时,数据通常会从内存传输到寄存器中进行处理。由于寄存器位于CPU内部,其访问速度非常快,比主存取数据的速度要快得多。这样,通过将数据存储在寄存器中,可以提高程序执行的效率。

其次,寄存器还承担了指令执行的作用。在程序执行过程中,指令会被加载到寄存器中,并根据需要被解码和执行。寄存器中的指令包含了程序运行的各个步骤,如数据操作、条件判断、跳转等,它们按照特定的顺序被CPU执行,以完成程序的功能。

此外,寄存器还用于存储程序的状态信息。例如,程序计数器(PC)寄存器用于存储当前正在执行的指令的地址,以便CPU知道下一条指令应该从哪里读取。另外,标志寄存器(Flag Register)用于存储条件判断的结果,如零标志、进位标志等。

在程序执行过程中,寄存器的角色是将数据从内存加载到寄存器中,并根据指令进行相应的操作。寄存器可以进行加减乘除等算术运算,逻辑运算和移位运算等。通过这些操作,CPU能够完成程序的功能,并将结果存储回内存或其他寄存器中。

2.2、不同类型的CPU寄存器(通用寄存器、特殊寄存器等)

不同类型的CPU寄存器包括通用寄存器、特殊寄存器、控制寄存器和段寄存器等。

通用寄存器:这些寄存器可以被程序员自由使用,并且用于保存临时数据和中间结果。通常有多个通用寄存器,如x86架构中的EAX、EBX、ECX、EDX等。

特殊寄存器:

  • 程序计数器(PC):也称为指令指针,用于存储当前正在执行的指令的地址。
  • 标志寄存器(Flag Register):存储条件判断的结果,如零标志、进位标志等。
  • 堆栈指针寄存器(Stack Pointer Register):存储当前堆栈的顶部地址。
  • 基址寄存器(Base Register):存储相对于段的基地址,用于访问内存中的数据。
  • 指令寄存器(Instruction Register):存储当前要执行的指令。

控制寄存器:这些寄存器用于控制CPU的运行和操作系统的工作状态,例如:

  • 控制寄存器0(CR0):存储与内存保护相关的设置。
  • 控制寄存器3(CR3):存储页表的基地址。

段寄存器:这些寄存器用于管理分段内存模型,包括代码段、数据段、堆栈段等。在x86架构中,有四个段寄存器:

  • 代码段寄存器(CS):用于存储代码段的起始地址。
  • 数据段寄存器(DS):用于存储数据段的起始地址。
  • 堆栈段寄存器(SS):用于存储堆栈段的起始地址。
  • 附加段寄存器(ES):用于存储其他数据段的起始地址。

这些不同类型的寄存器在CPU中起着各自独特的作用,从临时存储数据到控制CPU运行状态都扮演着重要角色。它们通过相互配合和协同工作,完成计算机程序的执行和操作系统的运行。

2.3、寄存器分配在优化程序性能方面的重要性

寄存器分配对性能优化的几个方面:

  1. 提高访问速度:寄存器位于CPU内部,具有非常快的访问速度。相比之下,访问主存储器的速度较慢。通过将频繁使用的数据存储在寄存器中,可以减少对主存的访问次数,从而提高程序的执行速度。

  2. 降低内存访问延迟:内存访问通常需要较长的延迟时间。通过将数据存储在寄存器中,可以避免等待内存访问的延迟,从而加快程序执行的速度。

  3. 减少冲突和依赖:寄存器分配可以减少因为寄存器冲突和依赖导致的指令之间的等待时间。当多个指令需要访问同一个寄存器时,可能会发生冲突。通过合理分配寄存器,可以减少冲突和依赖,从而提高指令的并行执行能力。

  4. 优化循环性能:循环是程序中常见的结构,也是性能优化的重点。通过将循环中的变量和临时计算结果存储在寄存器中,可以减少对内存的访问次数,从而提高循环的执行效率。

  5. 支持向量化指令:现代CPU通常支持向量化指令,可以同时处理多个数据元素。寄存器分配可以优化向量化指令的使用,确保数据可以高效地加载到寄存器中,并充分利用向量化指令的并行性能。

三、影响CPU寄存器分配register变量的因素

3.1、C语言中的register关键字以及它对编译器优化的影响

在C语言中,register是一个关键字,用于告诉编译器将变量存储在寄存器中而不是内存中。这样的声明仅仅是一种建议,而不是强制性要求。实际上,现代编译器通常会自动决定哪些变量应该存储在寄存器中。

register关键字对编译器优化有以下几个方面的影响:

  • 提高访问速度:由于寄存器位于CPU内部,因此访问寄存器比访问内存要快得多。通过将某些变量存储在寄存器中,可以提高程序的执行效率。

  • 减少内存访问:当变量存储在内存中时,每次使用它们时都需要进行内存读取操作。而如果将变量存储在寄存器中,可以避免频繁的内存访问,从而减少了对内存带宽的需求。

  • 限制变量的地址获取:由于寄存器是CPU内部资源,无法通过地址来引用它们。因此,使用register关键字声明的变量无法获取它们的地址。这可能会对某些需要显式地址的操作造成限制。

register关键字只是一种建议,并不能保证变量一定会被存储在寄存器中。实际上,现代编译器往往会根据具体情况自动选择将哪些变量存储在寄存器中,因此使用register关键字并不一定会产生明显的优化效果。在大多数情况下,编译器能够更好地了解代码的执行情况,并作出更合理的优化决策。因此,对于现代编译器来说,手动使用register关键字的必要性已经大大降低。

3.2、编译器在决定register变量分配时的作用

编译器通常会考虑以下几个因素来确定register变量的分配:

  • 变量的使用频率:编译器分析代码时会检查变量的使用频率。如果一个变量经常被使用,那么将其存储在寄存器中可以加快访问速度。相反,如果一个变量很少被使用,可能不值得将其存储在寄存器中。

  • 变量的生命周期:编译器还会考虑变量的作用域和生命周期。如果一个变量在一个较小的作用域内使用,并且其生命周期短暂,那么将其存储在寄存器中可能更加合适。

  • 寄存器的数量限制:每个CPU都有一定数量的寄存器可供使用。编译器需要考虑寄存器的数量限制,并尽可能高效地利用这些寄存器。如果register变量的数量超过了可用的寄存器数量,编译器可能会选择将某些变量存储在内存中。

  • 冲突与竞争:当多个变量都希望存储在寄存器中时,可能会发生冲突或竞争。编译器需要解决这些冲突,并做出适当的分配决策。

3.3、编译器使用的启发式算法和决策过程

编译器在进行register变量分配时使用的算法和决策过程通常是基于启发式算法和优化技术。

  • 静态单赋值(Static Single Assignment, SSA)形式:编译器经常会将代码转换为SSA形式,其中每个变量只能被定义一次。这样做可以简化数据流分析,并帮助编译器更好地了解变量的生命周期和使用情况。

  • 数据流分析:编译器通过数据流分析技术来获取关于变量使用和定义的信息。这种分析可以帮助编译器确定变量的活跃范围、定位循环中频繁使用的变量等。

  • 寄存器分配图着色:编译器可能会构建一个寄存器分配图,其中节点表示变量,边表示变量之间的相互依赖关系。然后,编译器使用着色算法来为图中的节点分配寄存器。常用的着色算法有图染色算法和线性扫描算法。

  • 基于权重的决策:编译器可能会为变量赋予权重或成本,并根据这些权重或成本作出决策。例如,使用频率高的变量可能会被分配到寄存器中,而使用频率较低的变量可能会被分配到内存中。

  • 目标平台特性:编译器还会考虑目标平台的特性和限制。例如,某些CPU可能具有特定的寄存器用途或寄存器数量限制,编译器会根据这些特性来进行优化决策。

四、编译器优化和寄存器分配

4.1、常见的编译器优化技术概述(常量传播、循环展开等)

  1. 常量传播(Constant Propagation):将变量赋值为常量的语句替换为对应的常量值,以减少不必要的运行时计算。

  2. 循环展开(Loop Unrolling):将循环体的代码复制多次,减少循环迭代的次数,从而减少循环控制开销和提高代码的并行度。

  3. 公共子表达式消除(Common Subexpression Elimination):在同一代码块中多次出现的相同子表达式只计算一次,并将结果存储在一个临时变量中,以避免重复计算。

  4. 死代码消除(Dead Code Elimination):移除不会被执行的代码,以减少程序的运行时间和内存占用。

  5. 数据流分析(Data Flow Analysis):通过分析程序中的数据依赖关系,找出不会被使用或改变的变量,在适当的地方进行优化,如删除无用变量、缩小变量的作用域等。

  6. 寄存器分配(Register Allocation):将变量存储在寄存器中而不是内存中,以减少对内存的访问次数,提高程序的执行速度。

  7. 内联(Inlining):将函数调用处的代码替换为被调用函数的实际代码,减少函数调用的开销,提高程序的执行效率。

  8. 部分求值(Partial Evaluation):对具有已知输入的表达式进行计算,以在编译时预先计算出结果,减少运行时的计算量。

  9. 循环优化(Loop Optimization):通过改变循环结构、循环变量的处理方式、循环不变量提取等手段优化循环,以提高循环的执行效率。

  10. 内存优化(Memory Optimization):通过数据结构布局调整、内存访问模式改进等技术,减少内存访问次数和缓存未命中,以提高程序的性能。

4.2、这些优化技术与寄存器分配决策之间的关系

寄存器分配是编译器优化中的一个重要环节,它决定了哪些变量应该存储在寄存器中而不是内存中。

  1. 公共子表达式消除和常量传播:这两种优化技术可以减少代码中的重复计算。当编译器进行寄存器分配时,可以考虑将这些计算结果存储在寄存器中,以便在需要时直接使用,避免了重复计算的开销。

  2. 循环展开和循环优化:循环展开通过复制循环体的代码来减少循环迭代的次数。在进行寄存器分配时,编译器可能会根据展开后的循环体大小和寄存器数量来决定是否将循环中的变量存储在寄存器中,以减少对内存的访问次数并提高循环的执行效率。

  3. 内联和函数调用优化:内联技术将函数调用处的代码替换为被调用函数的实际代码,减少了函数调用的开销。在进行寄存器分配时,编译器可以优先将内联函数中的变量存储在寄存器中,以进一步减少函数调用和返回的开销。

  4. 数据流分析和死代码消除:数据流分析可以帮助编译器找出不会被使用或改变的变量。在进行寄存器分配时,编译器可以根据这些分析结果来决定哪些变量可以从寄存器中释放,并将其存储在内存中,以降低寄存器压力并优化寄存器分配方案。

4.3、示例展示寄存器分配如何影响程序的效率和性能?

让通过一个简单的示例来展示寄存器分配是如何影响程序的效率和性能的。

int foo(int a, int b) {
    int c = a + b;
    int d = c * 2;
    return d;
}

int main() {
    int x = 10;
    int y = 20;
    int result = foo(x, y);
    return result;
}

在上述代码中,函数foo计算输入参数a和b的和,并将结果乘以2返回。main函数调用foo函数,并将返回值存储在result变量中。

如果编译器优化级别较低,没有进行寄存器分配,则变量a、b、c和d都会被存储在内存中。每次使用这些变量时,都需要进行内存访问操作。

然而,如果编译器进行了寄存器分配优化,将这些变量存储在寄存器中,那么执行过程中就可以直接在寄存器中进行计算操作,无需频繁地访问内存。

这种寄存器分配优化可以显著提高程序的效率和性能。因为减少了对内存的访问次数,从而减少了内存读写的延迟和带宽消耗。此外,寄存器分配也可以减少内存访问的竞争,提高程序的并行度。

通过对比编译器进行和不进行寄存器分配优化的执行速度和性能,你会发现使用寄存器分配的版本更快,并且具有更好的整体性能。这是因为寄存器分配减少了内存访问的开销,从而提高了代码的执行效率和性能。

五、CPU架构和寄存器可用性

5.1、不同CPU架构及其寄存器配置

不同的CPU架构在设计中都有其特定的寄存器配置。

x86架构(例如Intel和AMD处理器):

  • 通用寄存器:EAX、EBX、ECX、EDX、ESI、EDI、EBP、ESP
  • 累加器:EAX
  • 基址寄存器:EBX
  • 计数器:ECX
  • 数据指针:EDI
  • 堆栈指针:ESP
  • 指令指针:EIP

ARM架构:

  • 通用寄存器:R0-R15
  • 累加器:R0-R3
  • 基址寄存器:R4
  • 栈指针:R13(SP)
  • 链接寄存器:R14(LR)
  • 程序计数器:R15(PC)

MIPS架构:

  • 通用寄存器:$0-$31
  • 累加器:$ v0、$v1
  • 参数寄存器:$ a0-$a3
  • 返回值寄存器:$ v0、$v1
  • 堆 栈指针:$ sp
  • 指令指针:$ ra

注意:不同的CPU架构可能具有不同的寄存器数量、名称和用途。此外,一些架构还可以具有特定用途的专用寄存器,如条件码寄存器、向量寄存器等。

编译器在针对不同的CPU架构进行寄存器分配时,会根据目标平台的寄存器配置和规范,将变量分配到适当的寄存器中,并在必要时进行寄存器间传递或内存溢出。这样可以最大限度地提高程序的执行效率和性能。

5.2、CPU设计如何影响C语言中的寄存器分配决策

寄存器数量和类型:不同的CPU架构具有不同数量和类型的寄存器。例如,某些架构可能有更多的通用寄存器可供使用,而其他架构可能具有特殊用途的寄存器,如条件码寄存器或向量寄存器。编译器在进行寄存器分配时会考虑目标平台的寄存器数量和类型,以确定可以用于变量分配的合适寄存器。

寄存器大小:寄存器的大小也会影响寄存器分配决策。某些CPU架构可能具有不同大小的寄存器,如8位、16位、32位或64位。编译器需要根据寄存器的大小来选择合适的变量进行分配,以充分利用寄存器的容量。

寄存器之间的传递开销:在一些CPU架构中,将数据从一个寄存器传递到另一个寄存器可能存在一定的开销。这可能涉及到数据的临时存储或加载,从而增加了额外的指令和延迟。编译器在进行寄存器分配时会尽量减少这种寄存器间传递的开销,以优化代码的执行效率。

跨寄存器操作:某些CPU架构可能有一些限制,不允许直接在两个特定寄存器之间进行某些操作。这可能导致编译器在寄存器分配决策时需要考虑这些限制,并选择合适的寄存器组合,以确保正确的指令序列生成。

5.3、寄存器可用性与其他因素(缓存利用、流水线等)之间的权衡

缓存利用:寄存器和缓存都是存储器层次结构中的高速存储器。寄存器分配可以减少对内存的访问次数,从而减少缓存未命中的可能性,并提高程序的执行效率。然而,如果寄存器分配过度,导致寄存器不足,就可能会增加对内存的访问次数,从而增加缓存未命中的风险。编译器在进行寄存器分配决策时需要权衡寄存器数量和缓存利用之间的关系,以寻找适当的平衡点。

流水线效率:流水线是一种指令并行执行的技术,可以提高CPU的吞吐量。寄存器分配可以减少对内存的访问次数,从而避免了潜在的数据相关(例如读后写、写后读)引起的流水线停顿。然而,如果寄存器分配过多,导致寄存器溢出或频繁的寄存器间传递,也可能导致流水线停顿。编译器在进行寄存器分配决策时需要综合考虑流水线效率和寄存器数量之间的平衡,以最大限度地提高指令的并行度。

可扩展性:一些CPU架构具有可扩展的寄存器文件,可以支持更多的寄存器。在这种情况下,编译器可以更自由地进行寄存器分配,从而减少对内存的访问次数,并提高程序的效率。然而,如果目标CPU不支持可扩展的寄存器文件或受到其他限制,如指令宽度等,则可能需要更谨慎地进行寄存器分配。

总结

程序员了解CPU寄存器分配的重要性是非常关键的。CPU寄存器是计算机中最快的存储设备之一,用于临时存储和操作数据。合理地利用和分配寄存器可以显著提高程序的执行效率和性能。

首先,程序员需要了解不同类型的寄存器以及它们的功能。例如,通用寄存器用于存储临时数据和变量,而特定功能寄存器用于处理特定的任务,如存储指令指针或堆栈指针。了解这些寄存器的用途和限制可以帮助程序员更好地优化代码。

其次,程序员需要知道如何将变量和数据放置在正确的寄存器中。通过将频繁使用的变量存储在寄存器中,可以避免从内存中读取数据的开销,提高程序的执行速度。此外,合理的寄存器分配还可以减少寄存器间的数据传输,降低了内存访问的需求,进一步提高了性能。

另外,了解寄存器的数量和大小也有助于程序员设计出更有效的算法和数据结构。例如,在某些架构上,寄存器数量有限,因此程序员需要谨慎选择需要保存在寄存器中的变量。此外,某些架构上寄存器的大小可能不同,因此程序员需要注意数据类型的选择和对齐,以确保最佳性能。
在这里插入图片描述

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

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

相关文章

redis基础总结(数据类型)

Redis十大数据类型 String String 是redis最基本数据类型,一个key对应一个value. String类型是二进制安全的,意思是Redis的string类型可以包含任何数据,比如jpg图片或者序列化的对象; String类型是最基本的数据类型,一个redis中字符串value最多是512M; String类型在redis底层…

【已解决】span的宽度与高度如何设置

本博文源于笔者基础不扎实的情况下遇到的一个问题&#xff0c;问题是我有三个span&#xff0c;想让它们宽度与高度再大点&#xff0c;结果发现怎样设置都设置不了。最后不经意间解决问题 文章目录 1、问题再现2、解决方案3、解决效果 1、问题再现 <span>1</span>…

邪恶版ChatGPT来了!

「邪恶版」ChatGPT 出现&#xff1a;每月 60 欧元&#xff0c;毫无道德限制&#xff0c;专为“网络罪犯”而生。 WormGPT 并不是一个人工智能聊天机器人&#xff0c;它的开发目的不是为了有趣地提供无脊椎动物的人工智能帮助&#xff0c;就像专注于猫科动物的CatGPT一样。相反&…

一份热乎乎的字节面试真题

一份热乎乎的字节面试真题 说说Redis为什么快 基于内存存储实现 内存读写是比在磁盘快很多的&#xff0c;Redis基于内存存储实现的数据库&#xff0c;相对于数据存在磁盘的MySQL数据库&#xff0c;省去磁盘I/O的消耗。 高效的数据结构 Mysql索引为了提高效率&#xff0c;选…

【unity】Pico VR 开发笔记(基础篇)

Pico VR 开发笔记(基础篇) XR Interaction Tooikit 版本 2.3.2 一、环境搭建 其实官方文档已经写的很详细了&#xff0c;这里只是不废话快速搭建&#xff0c;另外有一项官方说明有误的&#xff0c;补充说明一下&#xff0c;在开发工具部分说明 插件安装——安装pico的sdk和XR…

为什么定时器,串口这些东西被称之为外设

前言 &#xff08;1&#xff09;我们常常说定时器&#xff0c;串口是外设&#xff0c;但是很多人肯定有疑惑。定时器&#xff0c;串口不明明是存储在芯片里面的吗&#xff1f; &#xff08;2&#xff09;为了弄明白这个&#xff0c;就需要追溯到上个世纪了。 上个世纪的CPU与串…

【玩转Python系列【小白必看】Python多线程爬虫:下载表情包网站的图片

文章目录 前言1. 导入模块和库2. 定义函数 download_image(url, filepath)3. 定义函数 get_page()4. 主程序入口 完整代码运行效果 结束语 前言 本文主要介绍了使用Python编写的多线程爬虫程序&#xff0c;用于下载表情包网站上的图片。通过解析网页内容和使用XPath定位&#x…

Spring优雅的在事务提交/回滚前后插入业务逻辑

业务背景 业务那边想要统计下我们这边每天注册商户成功和失败的数量&#xff0c;你看看怎么给他弄下这个功能 功能实现 TransactionSynchronizationManager.registerSynchronization&#xff0c;发现这是spring事务提供的注册回调接口的方法。 在事务注解方法中&#xff0c…

Java 版 spring cloud + spring boot 工程系统管理 工程项目管理系统源码 工程项目各模块及其功能点清单

工程项目各模块及其功能点清单 一、系统管理 1、数据字典&#xff1a;实现对数据字典标签的增删改查操作 2、编码管理&#xff1a;实现对系统编码的增删改查操作 3、用户管理&#xff1a;管理和查看用户角色 4、菜单管理&#xff1a;实现对系统菜单的增删改查操…

python中有哪些比较运算符

目录 python中有哪些比较运算符 使用比较运算符需要注意什么 总结 python中有哪些比较运算符 在Python中&#xff0c;有以下比较运算符可以用于比较两个值之间的关系&#xff1a; 1. 等于 ()&#xff1a;检查两个值是否相等。 x y 2. 不等于 (!)&#xff1a;检查两个…

2024考研408-计算机网络 第一章-计算机网络体系结构学习笔记

文章目录 前言一、计算机网络概述1.1、概念及功能1.1.1、计算机网络的概念1.1.2、计算机网络的功能功能1、数据通信功能2、资源共享功能3、分布式处理功能4、提高可靠性&#xff08;分布式处理引申功能&#xff09;功能5、负载均衡&#xff08;也是分布式处理引申功能&#xff…

23 张图详解路由协议

路由的概念 在 TCP/IP 通信中&#xff0c;网络层的作用是实现终端的点对点通信。IP 协议通过 IP 地址将数据包发送给目的主机&#xff0c;能够让互联网上任何两台主机进行通信。IP 地址可以识别主机和路由器&#xff0c;路由器可以把全世界的网络连接起来。 什么是路由器 路由…

使用Flutter的image_picker插件实现设备的相册的访问和拍照

文章目录 需求描述Flutter插件image_picker的介绍使用步骤1、添加依赖2、导入 例子完整的代码效果 总结 需求描述 在应用开发时&#xff0c;我们有很多场景要使用到更换图片的功能&#xff0c;即将原本的图像替换设置成其他的图像&#xff0c;从设备的相册或相机中选择图片或拍…

【LeetCode 75】第十五题(1456)定长子串中元音的最大数目

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码运行结果&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 就难度而言&#xff0c;我觉得算不上中等&#xff0c;因为和上一题基本一致&#xff0c;只不过上一题是求最大平均数&#xff0c…

基于Django美食分享交流网站-计算机毕设 附源码10913

美食分享交流网站 摘 要 大数据时代下&#xff0c;数据呈爆炸式地增长。为了迎合信息化时代的潮流和信息化安全的要求&#xff0c;利用互联网服务于其他行业&#xff0c;促进生产&#xff0c;已经是成为一种势不可挡的趋势。在美食分享的要求下&#xff0c;开发一款整体式结构的…

为什么计算机对浮点型数字计算存在误差

我们输入的十进制小数在计算机中都是以二进制进行存储。比如&#xff1a; 我们把0.25转换为二进制 0.25 * 2 0.5 取0 0.50 * 2 1.0 取1 所以十进制0.25的二进制应当为0.01但是我们把0.3转换为二进制存储 0.3 * 2 0.6 取0 0.6 * 2 1.2 取1 0.2 * 2 0.4 取0 0.4 * …

在腾讯云服务器OpenCLoudOS系统中安装mysql(有图详解)

1. 创建MySQL安装目录 mkdir -p app/soft//mysql 2. 进入MySQL安装目录&#xff0c;下载&#xff0c;安装 cd /app/soft/mysql/ wget http://dev.mysql.com/get/mysql-5.7.26-1.el7.x86_64.rpm-bundle.tar 得到安装包&#xff1a; 解压安装包&#xff1a; 查看系统是否自带…

使用Python机器学习预测外卖送餐时间!

大家好&#xff0c;我是小F&#xff5e; 现在的天气是一天比一天热&#xff0c;好多人周末休息在家的时候&#xff0c;就会选择点外卖&#xff0c;毕竟出去一趟又晒又热。 如果你太饿了&#xff0c;点餐太晚了&#xff0c;就可能去关注外卖员送餐到哪了&#xff0c;还有多少时间…

Kotlin泛型的协变与逆变

以下内容摘自郭霖《第一行代码》第三版 泛型的协变 一个泛型类或者泛型接口中的方法&#xff0c;它的参数列表是接收数据的地方&#xff0c;因此可以称它为in位置&#xff0c;而它的返回值是输出数据的地方&#xff0c;因此可以称它为out位置。 先定义三个类&#xff1a; op…

《golang设计模式》第一部分·创建型模式-03-建造者模式(Builder)

文章目录 1. 概念1.1 角色1.2 类图 2. 代码示例2.1 设计2.2 代码2.3 类图 1. 概念 1.1 角色 Builder&#xff08;抽象建造者&#xff09;&#xff1a;给出一个抽象接口&#xff0c;以规范产品对象的各个组成成分的建造。ConcreteBuilder&#xff08;具体建造者&#xff09;&a…