layout:post title: 优化C/C++代码的建议 tags:[C++, Linux, Performan]
优化C/C++代码性能的27条建议——<Tips for Optimizing C/C++ Code>译注 本文来自people.cs.clemson.edu的计算机图形学课程,编号405。关于C++语言,其广泛的应用便是桌面UI和计算机图形学领域。
1.记住Ahmdal法则: $$ Speedup=\frac {time_{old}}{time_{new}}=\frac {1}{(1-func_{cost})+func_{cost}/func_{speedup}} $$
$func_{cost}$是函数func被使用时的运行占总程序时间比,$func_{speedup}$是该函数相比原来提升倍率。
假入要优化函数TriangleIntersect(), 有40%的运行时,那么将其运行速率提升一倍后,程序总的运行速率变为原来的1.25倍。 $$ \frac 1{(1-0.4)+0.4/2}=1.25 $$
这意味着不经常使用的代码(infrequently used code)应该被很少去优化。
2.第一次就编码正确,然后才优化它。 这并不意味着要用8周写一个功能完备的光线跟踪器,然后再用8周去优化它。
基于正确编写的代码,然后再清楚被经常使用的函数是哪些,再去优化它。
发现瓶颈,去除瓶颈,通过优化或者提升算法。经常提升算法可以明显消除瓶颈,可能效果出乎意料。对于你经常使用的函数,不断进行优化是个好主意。
3.我知道的写的非常高效的代码的人,都说他们花费了至少两倍的时间在优化代码上。 写作也如此,想写出好的文章,必须要多多修改。
4.跳转/分支代价高,尽可能不使用它们。 函数调用需要两次跳转,额外操纵内存栈。
枚举胜过递归(依然涉及到内存栈多次操作)。
对于短小函数使用内联函数模式(inline Functions)来降低函数开销。
在函数调用内部,改变循环的写法:
把 for(i=0;i<100;i++) do sth 改成 do sth { for(i=0,1<100;1++) {……} } # 以上意思是,执行开销大的动作非必要不要向循环里放,循环中放判断语句。 大量的if...else if...else if..else if... 语句在结束整个判断链条前需要大量跳转到这些case(因为要尝试每种情况)。如果可能的话,把它们换成switch语句,使得编译器可以将之优化为单一跳转的表循环(a table lookup)。如果无法使用switch语句,就把最容易执行的判断语句放到判断链条之前。
5.思考数组参数的顺序。 二维或者更高维的数组仍然存储在一维的内存中。这意味着(对于C/C++数组)Array[i][j]和Array[i][j+1]相邻(are adjacent to each other),但是对于Array[i][j]和Array[i+1][j]来说它们可能相距很远(may be arbitrary far apart)。
前言 今天是五一劳动节,祝各位无产阶级劳动者节日快乐!
然后来整活分享一些有趣的东西~
这个小工具是我大学时做着玩的,对于各位接班人来说,12个词的核心价值观这东西,大家都非常熟悉了,这工具可以实现将一段话编码为核心价值观实现加密,同时也能将密文解密出来。
为啥要做这个呢,隔太久不太记得了,好像是因为游戏里喷队友老是被屏蔽,灵机一动,要是换成和谐的文字不就不会被屏蔽了吗(误
实现原理 简单说下原理,很简单,我们的文字在计算机中表示为ASCII码,对计算机来说所有数据都是0和1,即二进制的,而我们所熟悉的“核心价值观”有12个词,每个词可以表示一位,所以我们可以定义一种新的编码,以“价值观”组成的12进制编码~
只需要把二进制的ASCII字符转换成12进制的“价值观”编码就可以了。
开始代码 搞清楚原理就可以开始写代码了,本文使用Python语言来实现(接下来可能会尝试一下其他语言的实现)
这里用到了Python的标准库binascii,用来处理ASCII编码,先import进来
ASCII转2进制可以用binascii标准库实现,转12进制就得我们自己来实现了。
(当时)考虑到二进制直接转12进制比较麻烦,我折中一下,先2进制转10进制,然后再转12进制
10进制转12进制的代码如下
def from_num(input_num: int, b: int) -> int: 将数字转换为指定进制 :param input_num: 输入的数字 :param b: 进制 :return: 输出结果 return ((input_num == 0) and 0) or \ (from_num(input_num // b, b).lstrip(0) + 0123456789abcdefghijklmnopqrstuvwxyz[input_num % b]) 前置条件满足了,我们开始来写加密的代码
def encode(raw: str) -> str: 编码 :param raw: 输入的原始字符串 :return: 返回编码结果 result = list() for char in raw: # 把字符转换为16进制字符串 str_16 = binascii.
Linux
安装完redis单独用命令:
redis-server报错:
-bash: redis-server: command not found说明redis-server不是全局命令,那么假如到全局即可:
假如我的redis安装路径是:/home/prod/redis/redis-4.0.8
ln -s /home/prod/redis/redis-4.0.8/src/redis-server /usr/bin/redis-server将redis-server放到/usr/bin下即可(相当于创建一个全局快捷方式)
一台master主机可以拥有多台slave从机,而一台slave从机又可以拥有多个slave从机,如此下去,形成强大的多级服务器集群架构(高扩展)。
主从复制的作用 主从复制,读写分离,容灾恢复。一台主机负责写入数据,多台从机负责备份数据。在高并发的场景下,即便是主机挂了,可以用从机代替主机继续工作,避免单点故障导致系统性能问题(高可用)。读写分离的架构,满足读多写少的并发应用场景。
主从复制的架构 一个命令: slaveof 主机ip 主机port ,就可以确定主从关系;一个命令:./redis-sentinel sentinel.conf ,就可以开启哨兵监控。搭建是简单的,维护是痛苦的。在高并发场景下,会有很多想不到的问题出现。我们只有清楚复制的原理,熟悉主机,从机宕机后的变化。才能很好的跨过这些坑。
架构图:一主二仆一兵(也可以多主多仆多兵)
搭建前的准备工作 笔者选择用一台服务器模拟三台主机,和生产环境的区别仅仅是ip地址和port端口不同。第一步:将redis.conf 拷贝三份,名字分别是,redis6379.conf,redis6380.conf,redis6381.conf第二步:修改三个文件的port端口,pid文件名,日志文件名,rdb文件名第三步:分别打开三个窗口模拟三台服务器,开启redis服务。
[root@itdragon bin]# cp redis.conf redis6379.conf [root@itdragon bin]# cp redis.conf redis6380.conf [root@itdragon bin]# cp redis.conf redis6381.conf [root@itdragon bin]# vim redis6379.conf logfile 6379.log dbfilename dump_6379.rdb [root@itdragon bin]# vim redis6380.conf pidfile /var/run/redis_6380.pid port 6380 logfile 6380.log dbfilename dump_6380.rdb [root@itdragon bin]# vim redis6381.conf port 6381 pidfile /var/run/redis_6381.pid logfile 6381.log dbfilename dump_6381.rdb [root@itdragon bin]# ./redis-server redis6379.conf [root@itdragon bin]# ./redis-cli -h 127.
之前学习C语言的时候都是用IDE类似CodeBlocks的工具写完直接编译运行的,今天突然心血来潮,自己下一个编译器,在命令行下,编译运行C++程序,了解一下编译过程。 一.安装编译器
首先你需要下载一个编译器,我选择的是GNU的mingw,附上下载地址https://sourceforge.net/projects/mingw/
最开始选择的是在线安装,但是因为网速太慢,安装速度感人,所以最后下载了压缩包。将压缩包解压,然后在系统环境变量中配置一下bin目录的路径即可。在命令行中使用命令gcc -v来测试是否配置成功。
二.编辑代码
首先通过命令行进入你准备要创建文件的目录下,然后通过记事本notepad命令创建并打开test.cpp,如果当前目录下有test.cpp会直接打开文件,如果没有会先创建文件再打开。
开始写一段C++程序
三.编译过程
编译过程分为四个步骤:预处理、编译、汇编、链接。
1.预处理
预处理主要处理源文件中的“#include”、“#define”等预处理命令
预处理主要完成的工作有:
(1)删除#define,展开宏;
(2)处理条件编译指令,预处理程序先判断条件,在根据条件修改源代码;
(3)删除注释;
(4)添加行号,以及文件名标识,便于调试
(5)删除“#include”,插入相应的头文件;
使用g++ -E test.cpp -o test.i命令,预处理后得到test.i文件
2.编译
生成汇编代码的过程,使用命令g++ -S test.i -o test.s生成汇编文件test.s文件,当然直接从test.cpp文件得到汇编文件也可以。
3.汇编
将汇编代码转化成机器指令,生成目标二进制代码。
使用命令g++ -c test.s -o test.o 生成test.o文件
4.链接
通过链接库文件,将目标文件转化成可执行文件
使用命令g++ test.o -o test.exe -L 所需库文件路径 其中L为link的缩写
当然,一般情况下,可以直接使用g++ test.cpp -o test 就可以生成可执行程序了。
四.运行程序
最后使用命令test.exe,即可运行程序
原文:
https://blog.csdn.net/ichen820/article/details/117741734
1,先用iostat查看磁盘io 是否读写负载很高
用iostat -x 1 101如果 iostat 没有,要 yum install sysstat安装这个包,第一眼看下图红色圈圈的那个如果%util接近100%,表明I/O请求太多,I/O系统已经满负荷,磁盘可能存在瓶颈,一般%util大于70%,I/O压力就比较大,读取速度有较多的wait,然后再看其他的参数,
rrqm/s:每秒进行merge的读操作数目。即delta(rmerge)/swrqm/s:每秒进行merge的写操作数目。即delta(wmerge)/sr/s:每秒完成的读I/O设备次数。即delta(rio)/sw/s:每秒完成的写I/0设备次数。即delta(wio)/srsec/s:每秒读扇区数。即delta(rsect)/swsec/s:每秒写扇区数。即delta(wsect)/srKB/s:每秒读K字节数。是rsec/s的一半,因为每扇区大小为512字节
wKB/s:每秒写K字节数。是wsec/s的一半avgrq-sz:平均每次设备I/O操作的数据大小(扇区)。即delta(rsect+wsect)/delta(rio+wio)avgqu-sz:平均I/O队列长度。即delta(aveq)/s/1000(因为aveq的单位为毫秒)await:平均每次设备I/O操作的等待时间(毫秒)。即delta(ruse+wuse)/delta(rio+wio)svctm:平均每次设备I/O操作的服务时间(毫秒)。即delta(use)/delta(rio+wio)%util:一秒中有百分之多少的时间用于I/O操作,或者说一秒中有多少时间I/O队列是非空的09
2,找出使用io高的进程的工具 iotop
yum install iotop -y1直接执行 iotop 命令,然后看下图的显示,查看那个进程的读写,找出进程
————————————————版权声明:本文为CSDN博主「IChen.」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/ichen820/article/details/117741734
收集的一些sql的题
not in解法 子查询 select customers.name as 'Customers' from customers where customers.id not in ( select customerid from orders );
package dao; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import org.junit.jupiter.api.Test; public class JDBC { @Test public void fun1() throws Exception { // 注册驱动 String driverClassName = com.mysql.jdbc.Driver; Class.forName(driverClassName); // 获取连接,jdbc协议的格式 String url = jdbc:mysql://localhost:3306/db1; String username = root; String password = 747699; Connection con = DriverManager.getConnection(url, username, password); // 定义sql语句 String sql1 = update account set money = 2000 where id = 1; String sql2 = INSERT INTO account VALUES('3','王五',3000); // 获取SQL对象 Statement stmt = con.
前言 简单整理一下反向代理。
正文 为什么要反向代理呢? 其实这个问题也是相对来说比较好理解的。
一个就是解耦,为什么这么说呢,就是将原本应用的一部分剥离出来,比如说限制流量,如果在程序中写那么相比会增加程序的复杂度。
水平扩展,这个没什么好说的吧,有了水平扩展那么整体性能就得到了一个大的提高。
那么就来弄一下吧。
先把上一节的服务改成一个内网服务,也就是假设有另外一个服务。
那么这个现在只有内网可以访问。
然后设置代理:
upstream local{ server 127.0.0.1:8080; } server { server_name www.axm.com; listen 80; location /{ proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://local; } } 这个proxy_set_header 一般都要加上,不然拿到的就是本地地址了。
那么如何设置代理缓存呢?
proxy_cache_path /tmp/nginxcache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off; 然后再缓存的地方这样使用
proxy_cache my_cache; proxy_cache_key $host$uri$is_args$args; proxy_cache_valid 200 304 302 1d; 然后可以去看一下这个缓存的内容哈: 这个就是key:
值得注意的是,这里不是浏览器的缓存哈,而是nginx 的缓存。
原文地址:https://jingyan.baidu.com/article/7c6fb428add0e7c0652c9072.html
如果有某台内网服务器的程序,由于业务需求该服务器不能布置在公网上,但是我们在外面需要公网进行访问,可能有人通过在公网安装路由解决,但是非常麻烦。其实通过几个小命令就可以解决。
工具/原料 windows server 2008 r2 方法/步骤 两台服务器A (内网:192.168.0.101)服务器连接在公网上,外网可以访问,B (内网:192.168.0.80)服务器位于内网中,外网不能访问,且是程序运行的服务器。
我们要配置服务器A的7001端口转发服务器B的8080 端口。
进入192.168.0.101 服务器,点击 开始--运行,在弹出框中输入 cmd 命令,如图所示:
在命令窗体中输入:netsh interface portproxy add v4tov4 listenaddress=192.168.0.101 listenport=7001 connectaddress=192.168.0.80 connectport=8080 如图所示:
回车输入后,用 netsh interface portproxy show all 命令查看端口转发是否成功。
地址 端口 地址 端口
--------------- ---------- --------------- ----------
* 7001 192.168.0.80 8080
出现上面的样式,证明做端口转发成功。
如果要删除端口转发规则,输入:netsh interface portproxy delete v4tov4 listenaddress=192.168.0.101 listenport=7001
删除后,用 netsh interface portproxy show all 命令查看,如果发现转发的端口不在,证明删除成功,如图所示: