问题:架构设计中的大小端模式(little-big endian)
TX公司有一款有关智能手机应用的产品,该产品包括支持四个智能主流手机平台(symbian, windows mobile, Android, iPhone),上线后随着业务需求的变化,从后台下载到前端的内容量也暴增,也因此Android前端加载内容的性能也突然下降,想了很多办法包括逻辑优化和算法优化也无法提升,尤其在计算资源尤为宝贵的智能手机上。但很奇怪的是,同样的内容量在其他手机平台上的前端应用中以同样的算法和同样的逻辑处理内容却没有性能突降的问题,于是不得已采取debug跟踪,最后终于发现是因为大小端转换费了90%的时间。于是client就去优化大小端转换的代码,因为当时的程序员不知道JDK有提供直接转换大小端的API,所以自己写了一个方法去转换,效率肯定是有问题,用JDK提供的一个方法一行代码替换了自己写的n行代码问题就搞定。事情到此该打住了吧?没有,问题又来了,Android client通过使用JDK提供的API效率提高了不少,那client加载并显示的性能解决了,可是server响应速度还是满足不了业务需求?
原因:
要说明白还得从产品的架构设计开始,产品初期,因为只有symbian平台,虽然在架构设计中考虑日后跨平台的需求,已经考虑了client/server通信协议的扩展性以及client为屏蔽硬件层和操作系统层而设计的分层结构,但比较细节的诸如大端小端的问题并没有提及。于是在symbian平台的client首先出笼与server联合调试时,大小端的问题就出来了:我们发现从server下载到client后总是字节顺序混乱而且还有一定规律,那就是不同数据类型有不同的规律,有经验的程序员很快就判断出是大小端的问题,随后程序员很快就把问题解决了,解决的方法就是修改 server封装内容数据的代码:不同的数据类型在保存是按照不同的规则重新排序,当然是根据client要求的顺序。改程序的程序员估计到现在也不一定清楚server为什么是大端,我估计是这样推理的:因为symian client是小端,现在client和server通信遇到大小端不一致的情况后我在server输出时修改为小端后就正确了(也就是client需要的小端顺序),所以server一定是大端。到这里,我只能说结果是对的,但原因是错的。为什么?因为server的运行环境是Java+linux+Intel-based computer( X86),我们知道linux+Intel-based computer( X86)这样的配置肯定是小端,那怎么会是大端?Java,就是因为Java。JVM相当于另外一个虚拟的操作系统,它屏蔽了底层硬件和真正的操作系统,缺省的就是大端,所以server是大端,另外一个引申出来的结论就是:凡是Java运行环境的都是大端模式(big-endian)。意识到这一点,server提升响应速度的问题就有眉目了,上面讲到server为了满足symbian client做了大端转小端的工作,内容数据越多这个工作花费的时间就越多,这么一分析,server响应速度的问题自然就解了。既然android client和server的运行环境都是JVM,那么为什么都要同时做大小端的转换呢?那不等于两头都做无用功吗?后来的修改确实证明了这个结论的正确性。
解决方法:
1、 验证“凡是Java运行环境的都是大端模式(big-endian)”
在基于windows的PC,android手机,基于linux的PC服务器三个环境执行以下测试代码,结果相同。
package com.test;
public class Test001 {
public static byte[] shortToByteArray(short s) {
byte[] shortBuf = new byte[2];
for(int i=0;i<2;i++) {
int offset = (shortBuf.length - 1 -i)*8;
shortBuf[i] = (byte)((s>>>offset)&0xff);
}
return shortBuf;
}
public static void main(String[] args) {
short s = 128;
byte[] b = shortToByteArray(s);
System.out.println(b[0]);
System.out.println(b[1]);
}
}
2、 server端屏蔽大端转小端的代码
3、 Android client屏蔽小端转大端的代码
4、 iPhone client屏蔽小端转大端的代码
5、 symbian client增加大端转小端的代码
经验总结:
1、 架构设计过程中虽然架构师一个人不可能想到很多的细节,比如到底是client转还是server转,怎么转等细节,但在进入到编程阶段时一定要让程序员了解系统是否存在大小端转换的问题,如果存在的话,是哪一部分或模块或子系统去转比较合适?
2、 程序员在在第一次修改server代码是应该质疑“为什么要我修改”,到底是大端还是小端?为什么是大端?
3、 解决问题一定要从根本上彻底解决,不能只看眼前的外在的问题。
参考资料:
Most Intel-based computers (x86, AMD, etc.) use Little-Endian. Non-Intel based Apple computers and other RISC-based processors use Big-Endian. It is also important to note that network traffic uses Big-Endian ordering.
In the ARM9TDMI Tech Ref, there's mention that the "BIGEND" can be set (0 or 1) for instruction and data fetches...but only affect 16bit or 8bit fetches, while leaving the 32-bit word access unaffected.Does this mean that the default is BIGEND = 0? Is the ARM processor on Symbian phones little-endian? Yes, ARM processors on Symbian devices are little endian. In fact, ARM processors could work in both modes either little and big endian, but on Symbian devices it is set by default to little endian - cannot be changed from user level.
DEC (Digital Equipment Corporation,现在是Compaq公司的一部分)和Intel的机器(X86平台)一般采用小端。
IBM, Motorola(Power PC), Sun的机器一般采用大端。
当然,这不代表所有情况。有的CPU即能工作于小端, 又能工作于大端, 比如ARM, Alpha,摩托罗拉的PowerPC。 具体情形参考处理器手册。
具体这类CPU是大端还是小端,应该和具体设置有关。
(如,Power PC支持little-endian字节序,但在默认配置时是big-endian字节序)
一般来说,大部分用户的操作系统(如windows, FreeBsd,Linux)是Little Endian的。少部分,如MAC OS ,是Big Endian 的。
所以说,Little Endian还是Big Endian与操作系统和芯片类型都有关系。
http://www.arm.com:8765/query.html?c...an&ql=&x=7&y=7
http://www.arm.com/products/CPUs/fam...RM9Family.html
http://en.wikipedia.org/wiki/Endian
http://os.chinaunix.net/a2008/0125/965/000000965189.shtml