有哪些好的做兼职网站有哪些/百度搜索一下

有哪些好的做兼职网站有哪些,百度搜索一下,龙华网站建设多少钱,做企业网站的尺寸是多少最近在恶补计算机基础知识,学到CSAPP第五章的内容,在这里总结并且展开一下C程序性能优化相关的内容。 衡量程序性能的方式 一般而言,程序的性能可以用CPE(Cycles Per Element)来衡量,其指的是处理每个元素…

最近在恶补计算机基础知识,学到CSAPP第五章的内容,在这里总结并且展开一下C++程序性能优化相关的内容。

衡量程序性能的方式

一般而言,程序的性能可以用CPE(Cycles Per Element)来衡量,其指的是处理每个元素所需的CPU时钟周期数,计算公式为:CPE = 总执行周期数/处理的元素数量。

计算方式为:

#include <iostream>
#include <chrono>const int N = 1000000;
int arr[N];void test_function() {for (int i = 0; i < N; i++) {arr[i] = i * 2;}
}int main() {auto start = std::chrono::high_resolution_clock::now();test_function();auto end = std::chrono::high_resolution_clock::now();double elapsed_cycles = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count() * 2.5; // 假设CPU 2.5 GHzdouble cpe = elapsed_cycles / N; // 计算 CPEstd::cout << "CPE: " << cpe << std::endl;return 0;
}

影响编译器优化的因素

用gcc时,gcc -Og可以指定优化方式,但随着优化等级升高,程序规模也可能增加。

gcc优化等级

  • -O1:不会进行激进优化(如函数内联、代码重排序),不会影响可读性,编译时间仍然较短。优化包括死代码消除、常量传播、循环优化等。
  • -O2:在基本优化的基础上增加更高级优化:消除冗余计算、循环展开、指令调度、函数内联、分支预测优化,仍然不会进行极端优化。
  • -O3:实现更激进的循环展开、自动使用SIMD指令,使函数尽可能内联,并消除冗余加载和存储,对复杂的数学运算进行优化。但可能导致代码膨胀,过度优化会导致性能下降,如缓存效率降低。
  • -Os:基于-O2,但会避免增加代码大小的优化,适合嵌入式系统。

以下为一些妨碍编译器优化的因素:

内存别名使用

对于以下看似相同的代码段:

//代码段1
void twiddle1(long *xp, long *yp){*xp += *yp;*xp += *yp;
}//代码段2
void twiddle2(long *xp, long* yp){*xp += 2* *yp;
}

很显然,代码段2的执行所需耗费时间更短,其需要的内存访问次数更少。

然而,编译器无法将代码1优化为代码2,因为当yp=xp时,代码1等效于xp = 4 xp, 而代码2等效于 *xp = 3 * *xp,编译器不知道函数该如何被调用。这种两个指针可能指向同一个内存位置的情况称为内存别名使用,在只执行安全的优化中,编译器必须假设不同的指针可能会指向内存中同一位置。

修改全局程序状态的函数

对于以下看似相同的代码段:

long counter = 0;
long f(){return counter++;
}
//代码段1
long func1(){return f()+f()+f()+f();
}
//代码段2
long func2(){return 4*f();
}

当函数f的返回值涉及到全局变量counter时,可以看出,func1的输出为6,而func2的输出为0。

将函数定义为内联函数,可以直接将函数调用替换为函数体,例如,代码段1在o1优化下可以展开为:

long funclin(){long t = counter++;t += counter++;t += counter++;t += counter++;return t;
}

如果使用-o2及以上优化,可能会展开为:

long funclin() {long tmp = counter;counter += 4;return tmp + (tmp + 1) + (tmp + 2) + (tmp + 3);
}

直接优化方法

为了举例说明优化方法是如何实现的,我们定义向量数据结构如下:

typedef struct{long len;data_t *data;
} vec_rec, *vec_ptr;

data_t代表基本元素的数据类型。

定义初始化该向量、访问向量元素以及获取向量长度的方法如下:

/* Create vector of specified length */
vec_ptr new_vec(long len)
{/* Allocate header structure */vec_ptr result = (vec_ptr) malloc(sizeof(vec_rec));data_t *data = NULL;if (!result)return NULL;  /* Couldn't allocate storage */result->len = len;/* Allocate array */if (len > 0) {data = (data_t *)calloc(len, sizeof(data_t));if (!data) {free((void *) result);return NULL; /* Couldn't allocate storage */}}/* Data will either be NULL or allocated array */result->data = data;return result;
}/** Retrieve vector element and store at dest.* Return 0 (out of bounds) or 1 (successful)*/
int get_vec_element(vec_ptr v, long index, data_t *dest)
{if (index < 0 || index >= v->len)return 0;*dest = v->data[index];return 1;
}/* Return length of vector */
long vec_length(vec_ptr v)
{return v->len;
}

采用计算向量元素乘积的初始代码如下:

#define IDENT 1
#define OP *
/* Implementation with maximum use of data abstraction */
void combine1(vec_ptr v, data_t *dest)
{long i;*dest = IDENT;for (i = 0; i < vec_length(v); i++) {data_t val;get_vec_element(v, i, &val);*dest = *dest OP val;}
}

对于这段初始代码,有一些方向可以进行优化改进。

提高循环效率

代码移动

代码移动指的是将在循环里需要执行多次但计算结果不会改变的计算移动到循环外:

#define IDENT 1
#define OP *
/* Implementation with maximum use of data abstraction */
void combine2(vec_ptr v, data_t *dest)
{long i;long length = vec_length(v);*dest = IDENT;for (i = 0; i < length; i++) {data_t val;get_vec_element(v, i, &val);*dest = *dest OP val;}
}

减少过程调用

上述函数可以继续简化为:

data_t *get_vec_start(vec_ptr v)
{return v->data;
}/* Direct access to vector data */
void combine3(vec_ptr v, data_t *dest)
{long i;long length = vec_length(v);data_t *data = get_vec_start(v);*dest = IDENT;for (i = 0; i < length; i++) {*dest = *dest OP data[i];}
}

这种写法和combine2相比,减少了索引与数组边界的比较,但优化效果并不明显。

消除不必要的内存引用

对于combine3的赋值过程:

*dest = *dest OP data[i];

需要访问*dest指针的值,再根据这个地址从内存中取dest数组的值,并在计算完成后赋值到对应的内存上,在每次迭代过程中都要完成这样一个从内存读写数据的过程,将函数继续简化,减少对内存的读写:

void combine4(vec_ptr v, data_t *dest)
{long i;long length = vec_length(v);data_t *data = get_vec_start(v);data_t cur = IDENT;for (i = 0; i < length; i++) {cur = cur OP data[i];}*data = cur;
}

考虑机器特性的优化方法

上述优化方法都没有依赖目标机器的任何特性,如果要进一步提升性能,则需要考虑利用处理器微体系结构进行优化。

现代处理器结构

现代微处理器的简化示意图如下图所示,其可以分为指令控制单元ICU和执行单元EU两部分。

在这里插入图片描述

  • 取指控制:ICU从指令高速缓存中读取指令,并在译码后将对应的操作发送到EU。一般来说,会在当前执行的指令很早之前就进行取指。然而当程序遇到分支时,处理器采用分支预测技术,会猜测是否选择该分支并预测其目标地址。使用投机执行技术,处理器会在确定分支预测是否正确前就跳到分支对应的指令,甚至开始执行这些对应的操作。如果分支预测错误,则将状态重新设为分支点的状态。
  • 指令译码:接收实际的程序指令并将其转换为一组基本操作。
  • 加载和存储单元:内置加法器,用于读写内存。
  • 分支单元:向ICU返回分支预测是否正确的结果。
  • 算术运算单元:执行整数和浮点数操作的不同组合。
  • 退役单元:记录正在进行的处理,并确保其遵守机器级程序的语义。退役单元包含了多种寄存器,并控制这些寄存器的更新。指令译码时,其信息被放置在一个队列中,直到分支点预测结果出现,若预测正确,则程序寄存器的更新将被实际执行。任何对程序寄存器的更新都只会在指令退役的时候才会发生。

功能单元的性能

对于功能单元进行运算的性能,有以下几个指标可以用来衡量:

延迟L:表示完成运算所需要的总时间

发射时间I:表示两个连续的同类型运算之间需要的最小周期数

容量C:表示能够执行该运算的功能单元的数量

操作的吞吐量=C/I

对于一个执行n个乘法的函数,若其需要L*n+K个周期,其中K为调用函数和初始化等开销,此时CPE=L,对于单个按照顺序执行的功能单元组成的函数,延迟L表明了CPE的最小值,而对于多个功能单元组成的函数,还需要考虑其吞吐量。

处理器操作的抽象模型

将函数combine4的循环部分转换为汇编代码:

Inner loop of combine44. data_t = double, OP = *
acc in %xmm0, data+i in %rdx, data+length in %rax
1 .L25:
2 vmulsd (%rdx), %xmm0, %xmm0    loop: Multiply acc by data[i]
3 addq $8, %rdx                  Increment data+i
4 cmpq %rax, %rdx                Compare to data+length
5 jne .L25                       If !=, goto loop

将其抽象为数据流图,并去除不影响数据流的指令:

在这里插入图片描述

可以看出,乘法和加法运算是制约循环性能的两个因素,而浮点乘法的延迟约为整数加法的5倍,其成为了最关键的制约原因,程序的CPE为5。循环中的其他操作与乘法器并行地执行。

循环展开

循环展开是一种程序变换,通过增加每次迭代计算元素的数量来减少循环的迭代次数。

其优点为,可以提高缓存命中率,增加循环体内语句并发执行的可能性,同时减少分支预测失败的可能性。

用循环展开继续改进上述代码为:

/* 2 x 1 loop unrolling */
void combine5(vec_ptr v, data_t *dest)
{long i;long length = vec_length(v);long limit = length - 1;data_t *data = get_vec_start(v);data_t cur= IDENT;/* Combine 2 elements at a time */for (i = 0; i < limit; i += 2) {cur= (cur OP data[i]) OP data[i + 1];}/* Finish any remaining elements */for (; i < length; i++) {cur =  cur OP data[i];}*dest = cur;
}

编译器可以很轻松地执行循环展开,用GCC的优化等级大于等于3时就会执行循环展开。

提高并行性

我们知道,乘法操作和加法操作是可以并行化的,也就是说,不需要等待对方完成即可进行下一次操作,可以在每个时钟周期就开始一次新的操作。但目前的代码还并不能更高速率地执行乘法和加法,这是因为我们将累积值放在一个单独的变量cur中,在前面计算完成之前都不能计算cur的新值。

为了提高并行性,我们可以用多个累积变量分别计算:

void combine6(vec_ptr v, data_t *dest){long i;long length = vec_length(v);long limit = length - 1;data_t cur0 = IDNET;data_t cur1 = IDNET;for(i = 0; i <limit; i+=2){cur0 = cur0 OP data[i];cur1 = cur1 OP data[i+1];}for(; i < length; i++)cur0 = cur0 OP data[i];*dest = cur0 OP cur1;
}

我们可以将多个累积变量变换归纳为将循环展开k次,以及并行累积k个值,得到k×k的循环展开,当k足够大时,程序在所有情况下几乎都能达到吞吐量界限。通常,只有保持能执行该操作的所有功能单元的流水线都是满的,程序才能达到这个操作的吞吐量界限,对延迟为L,容量为C的操作而言,要求循环展开因子k ≥ L*C即可达到最大吞吐量。

除了以上并行累计的方式以外,还可以通过重新结合变换的方式对combine5进行继续优化:

void combine7(vec_ptr v, data_t *dest){long i;long length = vec_length(v);long limit = length-1;data_t *data = get_vec_start(v);data_t cur = IDENT;for(i = 0; i < limit; i+=2){cur = cur OP (data[i] OP data{i+1]);}for(; i < length; i++)cur = cur OP data[i];*dest = cur;
}

combine7和combine5的区别在于**data[i] OP data[i+1]**计算的先后顺序不同,而(data[i] OP data[i+1])时可以被并行计算的,因为它不依赖于cur的计算结果,可以提前计算。(现代CPU的超标量架构,可以在一个时钟周期内执行多个独立的指令,如果两个指令没有数据依赖,CPU可以同时执行它们。)

书写适用于条件传送的代码

条件传送(Conditional Move, CMOV) 是一种 CPU 指令优化技术,它允许根据条件决定是否执行数据传送而不使用传统的条件跳转(branching)
在 x86 架构中,CMOV 指令集(如 CMOVZ, CMOVNZ, CMOVL 等)可以在满足某些条件时,将值从一个寄存器传送到另一个寄存器,而不会触发分支预测失败的问题。

在 C++ 中,我们可以使用 条件运算符(?:内联汇编(asm标准库函数(std::max 以及 SIMD 指令 来实现 条件传送。

在现代C++编译器中,使用三元运算符可能被编译器优化为CMOV指令:

#include <iostream>//传统条件分支的代码
int branching(int x, int y){if (x > y)return x;elsereturn y;}
//使用条件传送的代码
int conditional_move(int x, int y) {return (x > y) ? x : y;  // 编译器可能优化为 CMOV
}int main() {int a = 5, b = 10;std::cout << "Max: " << conditional_move(a, b) << std::endl;return 0;
}

除此之外,gcc在 -O2 或更高级别优化下,std::max(a, b) 可能会被优化为 CMOV 指令

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

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

相关文章

transformer模型介绍——大语言模型 LLMBook 学习(二)

1. transformer模型 1.1 注意力机制 **注意力机制&#xff08;Attention Mechanism&#xff09;**在人工智能中的应用&#xff0c;实际上是对人类认知系统中的注意力机制的一种模拟。它主要模仿了人类在处理信息时的选择性注意&#xff08;Selective Attention&#xff09;&a…

Dify 本地部署教程

目录 一、下载安装包 二、修改配置 三、启动容器 四、访问 Dify 五、总结 本篇文章主要记录 Dify 本地部署过程,有问题欢迎交流~ 一、下载安装包 从 Github 仓库下载最新稳定版软件包,点击下载~,当然也可以克隆仓库或者从仓库里直接下载zip源码包。 目前最新版本是V…

2.1 掌握XML基础知识

本文介绍了结构化、半结构化和非结构化数据的概念与特点。结构化数据以固定格式存储于数据库&#xff0c;便于查询与管理&#xff0c;常用于金融等领域。半结构化数据如XML、JSON&#xff0c;具有一定的组织形式但模式不固定&#xff0c;适用于Web内容和日志文件。非结构化数据…

Android Studio 配置国内镜像源

Android Studio版本号&#xff1a;2022.1.1 Patch 2 1、配置gradle国内镜像&#xff0c;用腾讯云 镜像源地址&#xff1a;https\://mirrors.cloud.tencent.com/gradle 2、配置Android SDK国内镜像 地址&#xff1a;Index of /AndroidSDK/

超过 37000 台 VMwareESXi 服务器可能受到持续攻击威胁

近日&#xff0c;威胁监测平台影子服务器基金会&#xff08;The Shadowserver Foundation&#xff09;发布报告&#xff0c;指出超 3.7 万个互联网暴露的威睿&#xff08;VMware&#xff09;ESXi 实例存在严重安全隐患&#xff0c;极易受到 CVE-2025-22224 漏洞的攻击。该漏洞属…

npm终端执行时报错

终端npm执行时报错下错误&#xff1a; 报错了&#xff0c;就来百度&#xff0c;报错的原因是 1、这个错误是因为 PowerShell 的执行策略&#xff08;Execution Policy&#xff09;限制了脚本的运行 2、默认情况下&#xff0c;Windows 系统可能会禁止运行未签名的脚本&#x…

JVM类加载器面试题及原理

JVM只会运行二进制文件&#xff0c;类加载器的作用就是将字节码文件加载到JVM中&#xff0c;从而让Java程序能够启动起来。 1. 类加载器的种类 启动类加载器&#xff08;BootStrap ClassLoader&#xff09;&#xff1a;加载JAVA_HOME/jre/lib目录下的库扩展类加载器&#xff…

C语言每日一练——day_3(快速上手C语言)

引言 针对初学者&#xff0c;每日练习几个题&#xff0c;快速上手C语言。第三天。&#xff08;会连续更新&#xff09; 采用在线OJ的形式 什么是在线OJ&#xff1f; 在线判题系统&#xff08;英语&#xff1a;Online Judge&#xff0c;缩写OJ&#xff09;是一种在编程竞赛中用…

用Qt手搓AI助手,挑战24小时开发DeepSeek Assistant!

一、项目需求分析与技术选型 DeepSeekAssistant是一款基于深度求索&#xff08;DeepSeek&#xff09;API的智能对话助手&#xff0c;核心需求包括&#xff1a; 用户界面友好&#xff1a;支持多轮对话展示数据持久化&#xff1a;历史记录存储与检索异步网络通信&#xff1a;AP…

Go本地缓存设计与实现

本地缓存是一个项目中很常见的组件。在很多人的眼中就是一个简单的key-value的map存储即可实现&#xff0c;但实际上&#xff0c;设计一个本地缓存需要考虑的问题远比你想象的多&#xff0c;比如说&#xff0c;本地缓存是将数据存储在内存&#xff0c;若数据量激增突破了内存限…

AWS云编排详解-Cloud Formation

作者:私语茶馆 1.关键概念 名词 说明 软件: CloudFormation 描述AWS 资源、配置值和互连关系。借助集成设施即代码加快云部署 CloudFormation Designer 拖拽式图形化模板编辑界面。 Amazon Simple Notification Service (SNS) SNS可通过电子邮件跟踪堆栈的创建和删除进度,…

《PyQt5》——设计Python GUI(图形用户界面)实例

PyQt5 PyQt5的配置和基础使用可以参考这篇文章&#xff1a;《 PyQt5》—— 创建 Python GUI&#xff08;图形用户界面&#xff09; Python GUI&#xff08;图形用户界面&#xff09;实例 本实例是设计一个通过玉米和豆粕的价格来预测生猪的价格&#xff0c;并显示预测价格与实…

SpringBoot最简单方式实现自定义异常页面(404)

最简单的方式实现所有自定义异常页面&#xff08;如 404、500 等&#xff09;是通过 静态资源文件 或 模板引擎 来实现。 方法 1&#xff1a;使用静态资源文件&#xff08;最简单&#xff09; Spring Boot 默认会在 src/main/resources/static 或 src/main/resources/public …

django下防御race condition漏洞(竞争型漏洞)

目录 竞争型漏洞 概念 常见类型及示例 环境搭建 ​编辑漏洞复现 ucenter/1/ ucenter/2/ ucenter/3/ ucenter/4/ 总结 悲观锁 乐观锁 竞争型漏洞 概念 竞争型漏洞&#xff0c;也称为竞态条件漏洞&#xff08;Race Condition Vulnerability&#xff09;&#xff0c;…

用Python写一个算24点的小程序

一、运行界面 二、显示答案——递归介绍 工作流程&#xff1a; 1. 基本情况&#xff1a;函数首先检查输入的数字列表 nums 的长度。如果列表中只剩下一个数字&#xff0c;它会判断这个数字是否接近 24&#xff08;使用 abs(nums[0] - 24) < 1e-10 来处理浮点数精度问题&…

GitHub上传项目

总结&#xff08;有基础的话直接执行这几步&#xff0c;就不需要再往下看了&#xff09;&#xff1a; git init 修改git的config文件&#xff1a;添加:[user]:name你的github用户名 email你注册github的用户名 git branch -m master main git remote add origin 你的URL gi…

常见排序算法深度评测:从原理到10万级数据实战

常见排序算法深度评测&#xff1a;从原理到10万级数据实战 摘要 本文系统解析冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序和基数排序8种经典算法&#xff0c;通过C语言实现10万随机数排序并统计耗时。测试显示&#xff1a;快速排序综合性能最优&…

【C】链式二叉树算法题2

目录 1 另一棵树的子树 1&#xff09; 题目描述 示例1&#xff1a; 示例2&#xff1a; 2&#xff09; 算法解析 3&#xff09; 代码 2 二叉树的遍历 1&#xff09; 问题描述 2&#xff09; 算法解析 3&#xff09; 代码 3 总结 1 另一棵树的子树 leetcode链接…

配置Hadoop集群

Hadoop的运行模式 本地运行&#xff1a;在一台单机上运行&#xff0c;没有分布式文件系统&#xff0c;直接读写本地操作系统的文件系统。特点&#xff1a;不对配置文件进行修改&#xff0c;Hadoop 不会启动 伪分布式&#xff1a;也是在一台单机上运行&#xff0c;但用不同的 …

python办公自动化--数据可视化(pandas+matplotlib)--生成条形图和饼状图

前言 前几天我们学习了pandas读取数据&#xff0c;还学习了如何用patplotlib绘制柱状图和折线图。 今天我们继续学习&#xff0c;如何绘制条形图和饼状图。 一、课程回顾-pandas读取数据 1.示例数据文件 这里我们用到的依旧是d盘底下的这个excel工作簿&#xff0c;这个工作簿…