publicclassMain{publicstaticvoidmain(String[]args){finalMyDatadata=https://www.sohu.com/a/newMyData();newThread(newRunnable(){publicvoidrun(){data.add(0);}}).start();newThread(newRunnable(){publicvoidrun(){data.add(0);}}).start();try{Thread....
简介: 目前手机SOC的性能越来越少 , 很多程序员在终端程序的开发过程中也不太注意性能方面的优化 , 尤其是不注意对齐和分支优化 , 但是这两种问题一旦出现所引发的问题 , 是非常非常隐蔽难查的 , 不过好在项目中用到了移动端的性能排查神器友盟U-APM工具的支持下 , 最终几个问题得到了圆满解决 。
我们先来看对齐的问题 , 对齐在没有并发竞争的情况下不会有什么问题 , 编译器一般都会帮助程序员按照CPU字长进行对齐 , 但这在终端多线程同时工作的情况下可能会隐藏着巨大的性能问题 , 在多线程并发的情况下 , 即使没有共享变量 , 也可能会造成伪共享 , 由于具体的代码涉密 , 因此我们来看以下抽象后的代码 。
public class Main {
public static void main(String[] args) {
final MyData data = https://www.sohu.com/a/new MyData();
new Thread(new Runnable() {
public void run() {
data.add(0);
}
}).start();
new Thread(new Runnable() {
public void run() {
data.add(0);
}
}).start();
try{
Thread.sleep(100);
} catch (InterruptedException e){
e.printStackTrace();
}
long[][] arr=data.Getitem();
System.out.println("arr0 is "+arr[0]+"arr1 is"+arr[1]);
}
}
class MyData {
private long[] arr={0,0};
public long[] Getitem(){
return arr;
}
public void add(int j){
for (;true;){
arr[j]++;
}
}
}
在这段代码中 , 两个子线程执行类似任务 , 分别操作arr数组当中的两个成员 , 由于两个子线程的操作对象分别是arr[0]和arr[1]并不存在交叉的问题 , 因此当时判断判断不会造成并发竞争问题 , 也没有加synchronized关键字 。
但是这段程序却经常莫名的卡顿 , 后来经过多方的查找 , 并最终通过友盟的卡顿分析功能我们最终定位到了上述代码段 , 发现这是一个由于没有按照缓存行进行对齐而产生的问题 , 这里先将修改完成后的伪代码向大家说明一下:
public class Main {
public static void main(String[] args) {
final MyData data = https://www.sohu.com/a/new MyData();
new Thread(new Runnable() {
public void run() {
data.add(0);
}
}).start();
new Thread(new Runnable() {
public void run() {
data.add(0);
}
}).start();
try{
Thread.sleep(10);
} catch (InterruptedException e){
e.printStackTrace();
}
long[][] arr=data.Getitem();
System.out.println("arr0 is "+arr[0][0]+"arr1 is"+arr[1][0]);
}
}
class MyData {
private long[][] arr={{0,0,0,0,0,0,0,0,0},{0,0}};
public long[][] Getitem(){
return arr;
}
public void add(int j){
for (;true;){
arr[j][0]++;
}
}
}
可以看到整体程序没有作何变化 , 只是将原来的数组变成了二维数组 , 其中除了第一个数组中除arr[0][0]元素外 , 其余arr[0][1]-a[0][8]元素除完全不起作何与程序运行有关的作用 , 但就这么一个小小的改动 , 却带来了性能有了接近20%的大幅提升 , 如果并发更多的话提升幅度还会更加明显 。
缓存行对齐排查分析过程 首先我们把之前代码的多线程改为单线程串行执行 , 结果发现效率与原始的代码一并没有差很多 , 这就让我基本确定了这是一个由伪共享引发的问题 , 但是我初始代码中并没有变量共享的问题 , 所以这基本可以判断是由于对齐惹的祸 。
现代的CPU一般都不是按位进行内存访问 , 而是按照字长来访问内存 , 当CPU从内存或者磁盘中将读变量载入到寄存器时 , 每次操作的最小单位一般是取决于CPU的字长 。 比如8位字是1字节 , 那么至少由内存载入1字节也就是8位长的数据 , 再比如32位CPU每次就至少载入4字节数据, 64位系统8字节以此类推 。 那么以8位机为例咱们来看一下这个问题 。 假如变量1是个bool类型的变量 , 它占用1位空间 , 而变量2为byte类型占用8位空间 , 假如程序目前要访问变量2那么 , 第一次读取CPU会从开始的0x00位置读取8位 , 也就是将bool型的变量1与byte型变量2的高7位全部读入内存 , 但是byte变量的最低位却没有被读进来 , 还需要第二次的读取才能把完整的变量2读入 。
特别声明:本站内容均来自网友提供或互联网,仅供参考,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
