1)Haversine公式代码
public static double distHaversineRAD(double lat1, double lon1, double lat2, double lon2) { double hsinX = Math.sin((lon1 - lon2) * 0.5); double hsinY = Math.sin((lat1 - lat2) * 0.5); double h = hsinY * hsinY + (Math.cos(lat1) * Math.cos(lat2) * hsinX * hsinX); return 2 * Math.atan2(Math.sqrt(h), Math.sqrt(1 - h)) * 6367000; }
2)Haversine公式性能
目前北京地区在线服务有5w个POI,计算一遍距离需要7ms。现在数据增长特别快,未来北京地区POI数目增大到100w时,我们筛选服务仅计算距离这一项就需要消耗144多ms,性能十分堪忧。(注:本文测试环境的处理器为2.9GHz Intel Core i7,内存为8GB 1600 MHz DDR3,操作系统为OS X10.8.3,实验在单线程环境下运行)
POI数目耗时(ms)
5w7
10w14
100w144
4 优化方案
通过抓栈我们发现消耗cpu较多的线程很多都在执行距离公式中的三角函数例如atan2等。因此我们的优化方向很直接—消减甚至消除三角函数。基于此方向我们尝试了多种方法,下文将一一介绍。
4.1 坐标转换方法
1)基本思路
之前POI保存的是经纬度(lon,lat),我们的计算场景是计算用户位置与所有筛选出来的poi的距离,这里会涉及到大量三角函数计算。一种优化思路是POI数据不保存经纬度而保存球面模型下的三维坐标(x,y,z),映射方法如下:
x = Math.cos(lat) * Math.cos(lon); y = Math.cos(lat) * Math.sin(lon); z = Math.sin(lat);
那么当我们求夹角AOB时,只需要做一次点乘操作。比如求(lon1,lat1)和(lon2,lat2)的夹角,只需要计算x1*x2 + y1*y