C#数学相关开发性能优化方法
本文Github地址:CSharp-MathOptimization.md
华为公司的C语言编程规范在开头就强调了:
一般情况下,代码的可阅读性高于性能,只有确定性能是瓶颈时,才应该主动优化。
本文讲述的方法没有经过大项目和大公司的检验,所以,请批判性地阅读本文。本文中的大部分结论有测试代码支持,参见SpeedTest.cs. 虽然C#的编译器会在release版本中执行一些优化,C#的运行时也有一些优化,但偶尔会遇到debug版本正常,而release版本异常的问题,比如我在github上fork了已停止维护的屏幕录像软件Captura,debug模式能启动,release版本无法启动,我短时间内解决不了这个问题,如果要发布,只能发布debug版本。所以手工执行一些虽然编译器(在release版本中)会做但也简单易读的优化,还是有意义的。同时建议把未经优化的代码作为注释附在旁边,提高可读性。
1). const
, readonly
, in
这3个关键词在能用的地方要尽量用。这样可以让编译器执行更激进的优化策略,同时提高代码的安全性、可读性和可维护性。
2). 正整数乘以或除以 2 n 2^n 2n (n也是整数),使用移位来代替。但对乘以非 2 n 2^n 2n 的整数不要使用此方法,比如不要把 i * 12
改写成 (i << 2 + i << 3)
. 对于负整数的乘除运算,也不要用移位,否则代码可读性很差,也容易出错。对于int型整数,除法耗时是乘法的13倍,是移位的17倍。对于long型整数,除法耗时是乘法的1.4倍,是移位的9.8倍。(数据来源)
3). 除以浮点型常数的除法,改写为乘以这个数的倒数。比如把 x / 2.0
改写为 x * 0.5
.对于条件语句if(a/b > c)
,可以改写为if( (b > 0 && a > b * c) || (b < 0 && a < b * c) )
. 对于某些有很多分数嵌套的数学公式,请先进行恒等变形,只保留最长的一条分数线,其它的分数一律去掉分母,除法变成乘法。例如:
( 1 a 2 + k 2 b 2 ) x 2 + 2 k m b 2 x + m 2 b 2 − 1 = 0 \left(\frac{1}{a^{2}}+\frac{k^{2}}{b^{2}}\right)x^2+\frac{2km}{b^{2}}x+\frac{m^{2}}{b^{2}}-1=0 (a21+b2k2)x2+b22kmx+b2m2−1=0
x 1 + x 2 = − 2 k m b 2 1 a 2 + k 2 b 2 = − 2 a 2 k m b 2 + a 2 k 2 x_1+x_2=-\frac{\frac{2km}{b^{2}}}{\frac{1}{a^{2}}+\frac{k^{2}}{b^{2}}}=-\frac{2a^{2}km}{b^{2}+a^{2}k^{2}} x1+x2=−a21+b2k2b22km=−b2+a2k22a2km
x 1 x 2 = m 2 b 2 − 1 1 a 2 + k 2 b 2 = ( m 2 − b 2 ) a 2 b 2 + a 2 k 2 x_1x_2=\frac{\frac{m^2}{b^{2}}-1}{\frac{1}{a^{2}}+\frac{k^{2}}{b^{2}}}=\frac{\left(m^{2}-b^{2}\right)a^{2}}{b^{2}+a^{2}k^{2}} x1x2=a21+b2k2b2m2−1=b2+a2k2(m2−b2)a2
因为浮点除法的耗时是浮点乘法耗时的13倍。(数据来源)
4). 除非测试结果表明使用float型比double型更快,或者因为数据量巨大,float型比double型显著节省空间,否则都应该使用double型浮点数。因为float型的计算速度有时比double更慢,而且float型的精度太差,可能带来一些难以察觉的bug,比如 for(float i = 0.0f; i < 20000000; i++) 就是一个难以察觉的死循环,当 i 达到 2 24 2^{24} 224=16777216
时,会由于float的精度太低,无法区分 16777216 和 16777217,即无法自增1. 另外,使用float型,所有的字面量都要加 f 后缀,所有的Math库函数前面都要加(float)进行强制类型转换(或者使用MathF库中的函数),写起来麻烦,看起来丑陋,所以要尽量避免。在以下情形(但不限于)可考虑使用float型:
* 训练AI模型,数据量巨大但对计算精度要求不高,float型可显著节省存储空间。
* 程序要在32位处理器上运行,或者要在没有硬件浮点单元的处理器上运行。
* 需要使用SIMD技术加速,使用float型可以在一条指令中处理两倍于double型的数据。
5). 如需计算 x n x^n xn ,当 n=2,3 时,不要使用Math.Pow(x,n)
, 而是直接写成 x * x
和 x * x * x
. 当 n = 2 k ( k ∈ N + ) n=2^k(k\in N^+) n=2k(k∈N+) 时,可用y=x, 再执行k次 y*=y
来代替。当 n 取其它值时才可调用Math.Pow.
6). 引入一些额外的变量来存储函数调用的结果,或者复杂运算过程中的子过程的值,避免重复调用和计算。比如计算二维坐标旋转:
x1 = x * cos(a) - y * sin(a) y1 = x * sin(a) + y * cos(a)
同一个角的正弦和余弦值都要使用两次。一元二次方程求根, Δ 2 a \frac{\sqrt{\Delta}}{2a} 2aΔ 会使用两次。二元一次方程求根,系数矩阵的行列式值会使用两次。在循环中如果要以同样的参数调用某个函数,或者有一些不随循环变化的子过程,则应提到循环外部,用变量存储。
7). 对浮点数进行取整操作时,如果确定浮点数的大小不超出int(或long)型的范围,以及不会出现NaN,则可以用强制类型转换结合条件语句替代Floor
、Ceiling
和Round
函数,显著提高速度。使用 (int)(t ± 0.5)
来代替Math.Round(t)则需谨慎,因为当t的小数部分为0.5
时,Round(t)的结果取决于中点值舍入模式的设定,默认是MidpointRounding.ToEven
,即向最近的偶数舍入。其它模式还有ToZero
, AwayFromZero
, ToNegativeInfinity
, ToPositiveInfinity
. 要根据不同的舍入模式选择不同的替代写法。
// 替代 a = (int)Math.Floor(t)a = (t < 0 ? (int)t - 1 : (int)t);// 替代 b = (int)Math.Ceiling(t)b = (t < 0 ? (int)t : (int)t + 1);// 替代 c = (int)Math.Round(t, MidpointRounding.ToZero)c = (t < 0 ? (int)(t - 0.5) : (int)(t + 0.5));
8). 对于Array of Struct(AoS)和Struct of Array(SoA)两种数据结构,
- 内存布局:
AoS:每个结构体实例的所有字段在内存中是连续存储的。
SoA:每个字段的所有值在内存中是连续存储的,但不同字段的值分开存储。- 性能:
AoS:在需要频繁访问单个结构体实例的所有字段时性能较好。
SoA:在需要频繁访问所有实例的单个字段时性能较好,特别是在SIMD(单指令多数据)优化中表现更佳。
对于有限元程序,需要存储大量节点的编号、坐标和位移,需要视情况选择AoS或SoA.
9). 对于较小的结构体,可以考虑用ref struct
代替struct
,强制结构体存储在栈上(注意防范栈溢出),避免装箱操作,同时减少垃圾回收的性能损失。
10). 对于局部变量,使用 Span\<T\>
, ReadOnlySpan\<T\>
和 stackalloc
在栈上分配连续的小段内存(注意防范栈溢出),比使用数组(存储在堆上)速度更快。
11). 对于分支较多的流程,优先使用模式匹配而不是大量的if else,既能提高程序可读性,又能提高运行速度。比如分段函数就应该使用模式匹配。
static double Foo(double x) => x switch{< 0 => -x, // 当 x < 0 时,f(x) = -x>= 0 and <= 1 => x * x, // 当 0 <= x <= 1 时,f(x) = x^2> 1 and <= 2 => 2 * x, // 当 1 < x <= 2 时,f(x) = 2x> 2 => x + 1, // 当 x > 2 时,f(x) = x + 1_ => throw new ArgumentOutOfRangeException(nameof(x), "Invalid input")};
12). 尽量避免编写含递归
调用的函数。比如阶乘函数 n!
,递推数列(斐波那契数列、汉诺塔问题等),二分查找等,均可以用循环替代递归。
13). 对于那些参数的允许范围比较小的函数,优先考虑用查表法
实现。比如阶乘函数 n!
,因为阶乘函数增长太快,在大多数情况下,阶乘函数允许的参数的范围很小,
13 ! = 6227020800 > 2 32 = 4294967296 13! = 6227020800 >2^{32} = 4294967296 13!=6227020800>232=4294967296 = uint.MaxValue
21 ! = 5.109 × 1 0 19 > 2 64 = 1.845 × 1 0 19 21!=5.109\times 10^{19} >2^{64} = 1.845\times 10^{19} 21!=5.109×1019>264=1.845×1019 = ulong.MaxValue
35 ! = 1.033 × 1 0 40 > 2 128 = 3.403 × 1 0 38 35!=1.033\times 10^{40} >2^{128} = 3.403\times 10^{38} 35!=1.033×1040>2128=3.403×1038 = float.MaxValue
171 ! = 1.241 × 1 0 309 > 2 1024 = 1.798 × 1 0 308 171!=1.241\times 10^{309} >2^{1024} = 1.798\times 10^{308} 171!=1.241×10309>21024=1.798×10308 = double.MaxValue
至多占用171*8 = 1368
Byte的存储空间,就能满足double型计算的需求。不仅速度快,而且没有多次浮点乘法带来的累积误差。
特殊情况下,指数函数的自变量如果只能取正整数,那么自变量的范围一般也不会很大,比如 e 709 < 2 1024 < e 710 e^{709} < 2^{1024} < e^{710} e709<21024<e710 ,那么可以考虑对不超过某一阈值的整数采用查表法,超过该阈值则调用标准库。或者为自变量取等差数列时的函数值建立数表,然后用少量运算就能得到0~709内任意整数的函数值(参见 https://zhuanlan.zhihu.com/p/5221342896 )。
二项式系数(组合数)
和阶乘的自然对数ln(n!)
可以采用部分查表法。
CRC(循环冗余校验)也经常使用查表法加速。
14). 小于255的素数(质数)一共有54
个,如下:
static readonly byte[] PrimesLessThan255 = [2, 3, 5, 7, 11, 13, 17, 19, 23,29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251];
对正整数n进行素性测试时,可以先用上表的素数进行试除(比使用255以内的奇数试除要快),若都不能整除,就可以继续用从257到 n \sqrt{n} n 之间的奇数进行试除,从257开始是因为253(=11*23)和255都是合数。试除法是最简单但并不高效的素性测试方法,比较高效的方法是 Miller-Rabin测试法。但因为绝大多数正整数都有一个不大的素因子,比如:88%的正整数有一个小于100的素因子,92%的正整数有一个小于1000的素因子(数据来源: 大数因子分解算法综述.刘新星)。大于255但不超过1024的素数有118个。同时,根据素数定理
,不超过n的正整数中,素数占的比例大致是1/ln(n)
. 因此,构建一张比较大的素数表,采用先除素数再除奇数
的试除法,对于不太大的整数,是一种勉强能用的素性测试方法,同时也是寻找素因子的方法。
15). 以e为底的指数函数有一种快速近似算法:
public static double FastExp(double x) { long tmp = (long) (1512775 * x + 1072632447); return BitConverter.Int64BitsToDouble(tmp << 32); }
该方法的速度大致是Math.Exp
的5倍,原理参见《 A Fast, Compact Approximation of the Exponential Function》. 对于神经网络中的Sigmoid
函数中的指数函数,就可以采用这种近似算法。
16). 以e为底的对数函数有一种快速近似算法:
public static double FastLn(double x) // 抛弃对x<=0的检查。{long longx = BitConverter.DoubleToInt64Bits(x);double k = (longx >> 52) - 1022.5; return k * 0.693147180559945309; }
该方法实际上就是Math.Log
的算法的前半部分,用位运算提取了IEEE 754
浮点数的阶码,而抛弃了尾数的对数,速度大致是Math.Log的4倍,其中,-1022.5 = - 1023 + 0.5,0.693147……就是ln(2)
,该算法可以保证绝对误差不超过ln(2)/2=0.346573… 但该算法有一个不可忽视的弊端:设 n 为正整数,则对于区间 [ 2 n − 1 , 2 n ) [2^{n-1},2^n) [2n−1,2n) 内的任意实数,该算法会返回完全一样的结果。以2为底或以10为底的对数函数也可以使用该方法,把最后一行与k相乘的常数换掉即可,以2为底就是return k,以10为底就是return k*0.301029995663981196.
17). 免费的数学库推荐ALGLIB免费版,收费的数学库推荐ALGLIB
、ILNumerics
和Dew.Math
. 不推荐 MathNET Numerics
,其代码质量低下,原因参见点评10多个C#的数学库.
18). 避免在循环中做以下事情:
- 创建对象。
- 使用try catch.
- 打开和关闭同一个文件、数据库等。
- 创建和断开对同一个URI的链接。
19). 避免不加测试地用Parallel.For
代替for循环,因为前者需要创建和管理多个线程,会带来额外的开销。当循环次数太少或者单次循环所做的运算太简单时,使用Parallel.For反而会降低性能,而且很可能出现计算结果不正确的问题。比如函数f(x)在某个区间上做数值积分,有sum += f(xi)*dx
这样的累加运算,需要测试Parallel.For的耗时是否更短以及结果是否正确。又比如一些写不出通项公式的递推数列, a n + 1 = a n + a n + 1 a_{n+1}=a_n+\sqrt{a_n}+1 an+1=an+an+1 ,每次循环都依赖上一次的结果,注定无法并行,此时禁止使用Parallel.For.
20). 考虑使用[SkipLocalsInit]属性,省略CLR将方法中声明的所有局部变量初始化为其默认值的操作,提高速度。注意:此属性需要 AllowUnsafeBlocks
编译器选项,同时要重点检查代码中是否存在访问未初始化的变量的行为。
21). 绝大多数时候,矩阵求逆都是非必须的(而且计算代价很大的),除非就是要得到逆矩阵本身。比如对于线性方程组 A x = b , x = A − 1 b Ax=b,\ x=A^{-1}b Ax=b, x=A−1b ,使用逆矩阵表达方程组的解只具有形式意义,不可直接用于计算。请使用高斯消去法
、LU分解法
、Jacobi迭代法
、Gauss-Seidel迭代法
、Cholesky分解法
等。矩阵的逆几乎不会单独出现,几乎总是会和其它矩阵做乘法,总有不求逆的替代方案。
22). 多项式求值优先使用秦九韶算法
,
a n x n + a n − 1 x n − 1 + ⋯ + a 1 x + a 0 = ( ⋯ ( ( a n x + a n − 1 ) x + a n − 2 ) x + ⋯ + a 1 ) x + a 0 a_nx^n +a_{n-1}x^{n-1}+\cdots+a_1x+a_0 \\= (\cdots ((a_nx+a_{n-1})x+a_{n-2})x+\cdots+a_1)x+a_0 anxn+an−1xn−1+⋯+a1x+a0=(⋯((anx+an−1)x+an−2)x+⋯+a1)x+a0
对于阶数不太高的多项式(比如小于10阶),不要使用循环语句来实现这个算法,而应该手工进行循环展开
。还可以使用融合乘加指令Fma.MultiplyAdd进行进一步加速。秦九韶算法是一个串行的算法,无法并行。如果某个n次多项式的全部根均为实数(设为 x 1 x_1 x1 , x 2 x_2 x2 , ⋯ \cdots ⋯ , x n x_n xn ,需要提前计算出来),那就可以使用SIMD
指令进行并行计算:
a n ( x − x 1 ) ( x − x 2 ) ⋯ ( x − x n ) a_n(x-x_1)(x-x_2)\cdots (x-x_n) an(x−x1)(x−x2)⋯(x−xn)
23). 利用泰勒级数计算double型函数值时,多项式阶数通常不应该超过17阶,太高的阶数没有意义(因为浮点运算的累积误差)。泰勒级数具有局部性
,离展开点越远,精度越差。所以如果要提高计算精度,首先应考虑更换展开点
,而不是提高多项式的阶数。
参考文章:
- Writing Faster Managed Code: Know What Things Cost
- 新版C#高效率编程指南
- C#中那些举手之劳的性能优化
相关文章:
C#数学相关开发性能优化方法
本文Github地址:CSharp-MathOptimization.md 华为公司的C语言编程规范在开头就强调了: 一般情况下,代码的可阅读性高于性能,只有确定性能是瓶颈时,才应该主动优化。 本文讲述的方法没有经过大项目和大公司的检验&…...
【前沿 热点 顶会】AAAI 2025中与目标检测有关的论文
CP-DETR: Concept Prompt Guide DETR Toward Stronger Universal Object Detection(AAAI 2025) 最近关于通用物体检测的研究旨在将语言引入最先进的闭集检测器,然后通过构建大规模(文本区域)数据集进行训练࿰…...

APP投放的归因框架设计
一、归因相关概念回顾 在广告归因简介中我们介绍常见的归因模型和归因方法,我们先来回顾一下: 1. 背景 2. 设备标识 3. 归因模型 归因模型的多样性意味着每种模型都有其独特的优势和局限。关键在于选择一个与您的业务场景相匹配的模型,并且…...
职业生涯记录-1
机缘 普通人改命,要学会向上社交,能自然融入的圈子,多数是往下社交,没有多少价值,想要获取更多资源,但是有钱人只跟有钱人交朋友,不会带我们普通人玩,又错了,有钱人身边最不缺的就是有钱人,他们缺的是对他们有利用价值的朋友,往上社交的关键,你必须是个对他有利用价…...

江苏捷科云:可视化平台助力制造企业智能化管理
公司简介 江苏捷科云信息科技有限公司(以下简称“捷科”)是一家专注于云平台、云储存、云管理等产品领域的创新型企业,集研发、生产和销售于一体,致力于在网络技术领域打造尖端品牌。在推动制造业企业数字化转型的进程中…...
【ES6复习笔记】Promise对象详解(12)
1. 什么是 Promise? Promise 是 JavaScript 中处理异步操作的一种机制,它可以让异步操作更加容易管理和控制。Promise 对象代表一个异步操作的最终完成或失败,并提供了一种方式来处理操作的结果。 2. Promise 的基本语法 Promise 对象有三…...

01 Oracle 基本操作
Oracle 基本操作 初使用步骤 1.创建表空间 2.创建用户、设置密码、指定表空间 3.给用户授权 4.切换用户登录 5.创建表 注意点:oracle中管理表的基本单位是用户 文章目录 了解Oracle体系结构 1.创建表空间**2.删除表空间**3.创建用户4.给用户授权5.切换用户登录6.表操…...

C语言基础:指针(数组指针与指针数组)
数组指针与指针数组 数组指针 概念:数组指针是指向数组的指针,本质上还是指针 特点: 先有数组,后有指针 它指向的是一个完整的数组 一维数组指针: 语法: 数据类型 (*指针变量名)[行容量][列容量]; 案…...

本地部署 LLaMA-Factory
本地部署 LLaMA-Factory 1. 本地部署 LLaMA-Factory2. 下载模型3. 微调模型3-1. 下载数据集3-2. 配置参数3-3. 启动微调3-4. 模型评估3-5. 模型对话 1. 本地部署 LLaMA-Factory 下载代码, git clone https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Facto…...

Web前端基础知识(三)
表单的应用非常丰富,可以说,每个网站都会用到表单。下面首先介绍表单中的form标签。 --------------------------------------------------------------------------------------------------------------------------------- <form></form&g…...
数据库设计问题记录
唯一性约束和逻辑删除的冲突 问题描述 如果一张表中,存在唯一性约束,比如一些数据中的code,且数据表使用逻辑删除。当删除某行数据的时候,以后再次插入相同code的数据,数据库会报错。 问题分析 在逻辑删除中&#…...

mac_录屏
参考: mac m1上系统内录方法BlackHole代替soundflower录音(附安装包) https://blog.csdn.net/boildoctor/article/details/122765119录屏后没声音?这应该是 Mac(苹果电脑) 内录声音最优雅的解决方案了 https://www.bilibili.com/…...

【Java-tesseract】OCR图片文本识别
文章目录 一、需求二、概述三、部署安装四、技术细节五、总结 一、需求 场景需求:是对识别常见的PNG,JPEG,TIFF,GIF图片识别,环境为离线内网。组件要求开源免费,并且可以集成Java生成接口服务。 二、概述 我不做选型对比了,我筛选测试了下Tesseract(v…...

redis cluster集群
华子目录 什么是redis集群redis cluster的体系架构什么是数据sharding?什么是hash tag集群中删除或新增节点,数据如何迁移?redis集群如何使用gossip通信?定义meet信息ping消息pong消息fail消息(不是用gossip协议实现的࿰…...

解锁高效密码:适当休息,让学习状态满格
一、“肝帝” 的困境 在当今竞争激烈的职场中,“肝帝” 现象屡见不鲜。超长工时仿佛成为了许多行业的 “标配”,从互联网企业的 “996”“007”,到传统制造业的轮班倒、无休无止的加班,员工们的工作时间被不断拉长。清晨ÿ…...
代码随想录算法训练营第十一天-150.逆波兰表达式求值
队列栈 #include <iostream> #include <vector> #include <stack>class Solution { public:int evalRPN(std::vector<std::string>& tokens) {std::stack<long long> stack_number;for (auto it tokens.begin(); it ! tokens.end(); it) {…...
C++ 泛编程 —— 嵌套使用模板类
嵌套使用模板类 嵌套使用模板类最常见的场景数组容器中有栈容器栈容器中有数组容器递归使用模板类 嵌套使用模板类最常见的场景 容器中有容器 数组的元素可以是栈,栈中的元素可以是数组。先来看一下Stack和Vector的基本代码,定长数组Array的代码也给出来…...
【WebGIS】Cesium:GLTF数据加载
在3D Web GIS开发中,使用GLTF格式的模型可以提高应用的加载速度并提升用户体验。Cesium.js是一个强大的3D地理空间引擎,支持GLTF格式的3D模型,并且提供丰富的API来处理和优化模型的加载和渲染。本文将系统地介绍如何加载GLTF模型,…...
【面经】25届 双非本科 字节跳动 北京 四年的总结
点击“硬核王同学”,选择“关注” 福利干货第一时间送达 大家好,我是硬核王同学,最近在做免费的嵌入式知识分享,帮助对嵌入式感兴趣的同学学习嵌入式、做项目、找工作! 给大家分享一个25届本科大佬的面经,…...

抖去推碰一碰系统技术源码/open SDK转发技术开发
抖去推碰一碰系统技术源码/open SDK转发技术开发 碰一碰智能系统#碰碰卡系统#碰一碰系统#碰一碰系统技术源头开发 碰碰卡智能营销系统开发是一种集成了人工智能和NFC技术的工具,碰碰卡智能营销系统通过整合数据分析、客户关系管理、自动化营销活动、多渠道整合和个…...

大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...