程序优化的那些事

15 篇文章 0 订阅
13 篇文章 0 订阅

系统设计层面

攒包/定时地进行批量处理。提高单包承载率,减少网络io、减少系统调用次数。

数据处理(cpu消耗型代码)、io独立线程处理。

多地部署,就近接入。分set,避免跨城、跨地区流量访问。

消息队列尽量无锁化、一写一读等。

尽量减少无用的代理层,去中间商。

灰度机制。

内核级优化(操作系统多队列网卡特性等)。

数据自动报表化,多维度展现。

CDN静态加速、css和js合并压缩(在HTTP2中不使用,因为其中一个文件变更,导致缓存失效,不值得)

DNS调度,就近接入(测速)

proxy 预建连、多连接、Quic等加速

tcp长链接(Keep-Alive),请求复用连接(但是http1.1中,协议是文本,“发送方”需按序发送,后面的请求必须等前面的请求完成)

http2(头部压缩、二进制编码、免发重复数据、多路复用(通过引入二进制数据帧和流,使得“发送方”可以乱序发送不用等待,服务端可以恢复顺序))

底层网络收发包使用多路复用如epoll、select等(netty框架)

使用proxy、worker、请求队列方式搭建服务,增加服务器抗网络抖动能力

上流使用反向代理服务器,提供负载均衡、CND缓存能力

限流(qps、流量),对超过服务器承载量的请求及时作丢包处理,避免滚雪球,如令牌桶算法和漏桶算法(令牌通强调输入速率,从入口处限制请求;而漏桶算法强调输出,在出口处限制请求)。
Guava RateLimiter

 

C语音层面

register修饰高频访问的变量。register修饰的变量将尽可能放在CPU内部寄存器中,减少了访问内存的时间。

inline修饰高频调用的函数

内存对齐访问

操作char型数据转换成操作long型数据,因为32位系统简单操作一次32位数据的时间几乎和操作一次8位数据的时间相同

for (j=0;j<8;j++)

   src_buf[j]^=iv_crypt[j];

需要存取内存8*2次,每次只操作1字节,它优化之后的语句:

*(uint64_t *)src_buf ^= *(uint64_t *)iv_crypt;

只需存取内存2*2次,每次可以操作4个字节。

 

优化之前的语句:

   while(nInBufLen) {

       if (src_i<8) {

           src_buf[src_i++]=*(pInBuf++);

           nInBufLen--;

       }

       ...

    }

每个字节需要循环一次,优化之后,每8个字节循环一次。优化之后的语句如下:

   while(nInBufLen) {

       if (src_i == 0 && nInBufLen >= 8) {

           *(uint64_t *)src_buf = *(uint64_t *)pInBuf;

           pInBuf += 8;

           nInBufLen -= 8;

           src_i = 8;

           ...

        }

        ...

    }

 

中断优化

1.避免没有必要的缺页中断

用new或者malloc申请内存时,系统并不会立刻分配相应内存,而是在实际使用时才这片内存时才分配。所以为了避免没有必要的缺页中断,可以在申请内存后,立刻初始化一遍内存,避免进程处理业务时产生终端。

使用mlock锁定内存,避免被交换出去。

尽量减少swap或把swap关掉。

http://blog.csdn.net/b2222505/article/details/60324150

http://blog.csdn.net/b2222505/article/details/54144353

 

2.减少页表映射

如果物理内存很大,映射表的条目将会非常多,会产生较多TLB Miss,内存访问过程很耗时。例如程序需要访问4MB的内存,那么就需要1024个页面,TLB需要1024个表项,同时需要1024个页表项,系统至少需要产生1024次TLB中断,才可以把4MB内存映射到物理内存。

通常情况下对于大块内存的频繁访问,可以采用大页表减少页表条目,提高内存查询速度

cat /proc/meminfo|grep "HugePages"可以查看大页表的状态

减少了内存页表项的缓存压力和CPU cache缓存内存地质映射的压力,提高了寻址能力和内存管理效率。大页内存还有其他一些使用时需要注意的地方:

大页内存不能交换(SWAP).

使用不当时可能造成更大的内存泄漏。

 

3.避免伪共享

多核CPU,每个核都拥有独立的L1/L2的缓存,而缓存按照最小单位缓存行进行和内存交互,缓存行一般为64字节。

当多线程修改互相独立的变量(一般出现在全局变量或者一些动态申请的内存)时,如果这些变量共享同一个缓存行,那么当某个核修改缓存行某个变量值时,需要锁定缓存行,同时其他核已经加载该缓存行的cache也跟着失效,需要重新从内存读取。

 

4、内存池,减少频繁申请、释放内存

 

 

编写cpu缓存友好的代码

空间局部性原理
分配连续紧凑的内存,且最好不要超过catch line的大小

时间局部性原理
尽量重复使用同一个变量,使其保持在寄存器中

因为cpu的MESI的存在,要尽量避免伪共享
将共享在多线程间的数据进行隔离
1、增大数组元素的间隔使得不同线程存取的元素位于不同的cache line上
2、在每个线程中创建全局数组各个元素的本地拷贝,然后结束后再写回全局数组

考虑清楚哪些变量是不变的,哪些是经常变化的,哪些变化是完全相互独立的,哪些属性一起变化
将该对象属性分组,将一起变化的放在一组,与其他属性无关的属性放到一组,将不变的属性放到一组
1、Padding 方式
2、Contended注解方式
```java
@sun.misc.Contended
@SuppressWarnings("restriction")
public class ContendedData {
    int value;
    long modifyTime;
    boolean flag;
    long createTime;
    char key;
}
```
使用Disruptor
一个线程内通信框架,用于线程里共享数据。它确保任何数据只由一个线程拥有以进行写访问,从而消除写争用的设计
数组比链表、树更具有缓存友好性
http://geek.csdn.net/news/detail/114619

 

 

参考资料


https://segmentfault.com/a/1190000011172823
https://segmentfault.com/q/1010000005167289




 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CSDN是一个知名的IT技术社区,对Java程序进行优化可以提高程序的性能和效率。Java程序优化的主要目的是减少程序运行时的资源消耗,提高程序的响应速度和稳定性。 首先,可以优化Java程序的内存管理。Java虚拟机采用垃圾回收机制来自动管理内存,但是不合理的内存使用会导致频繁的垃圾回收或内存泄漏。可以通过合理地使用对象池、避免过多地创建临时对象、及时释放不再使用的资源等方式来优化内存管理,减少频繁的垃圾回收。 其次,可以优化Java程序的算法和数据结构。在程序设计中选择合适的算法和数据结构可以显著提高程序的运行效率。可以利用哈希表、树等数据结构来优化查找和排序等操作,尽量避免使用嵌套循环和递归等低效的算法。 另外,可以通过多线程编程来提高Java程序的并发性能。多线程可以将程序中的任务并行执行,提高程序的处理能力和吞吐量。但是多线程编程也存在一些问题,如线程同步、死锁等。可以通过合理地设计和管理线程,避免这些问题的发生,提高程序的并发性能。 还有,可以采用缓存技术来优化Java程序。缓存可以存储频繁使用的数据,减少对底层存储系统的访问,提高程序的响应速度。可以使用内存缓存、数据库缓存或分布式缓存等方式来优化程序的缓存机制。 最后,可以利用性能分析工具来监测和调优Java程序。性能分析工具可以帮助我们找到程序运行中的瓶颈和潜在问题,并给出相应的优化建议。其中包括JVisualVM、Java Mission Control等。 综上所述,通过合理的内存管理、优化算法和数据结构、多线程编程、缓存技术和性能分析工具等方式,可以有效地优化Java程序,提高程序的性能和效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值