你需要问自己的基本问题是:能否减少数据集?
如果不能,我有一个坏消息:500000 * 2000000等于1e12
。这意味着你要执行一万亿次操作。
角分离涉及到一些三角函数(我认为这里涉及到cos
、sin
和sqrt
),因此每个操作大约需要数百纳秒至微秒级。假设每个操作需要1微秒,你仍需要12天才能完成这个任务。而且这还假设你没有任何Python循环或IO开销,我认为1微秒对于这种操作是合理的。
但肯定有优化的方法:SkyCoord
可以进行矢量化,但只支持1D:
c2 = SkyCoord(ra=RA2*u.degree, dec=DEC2*u.degree)
for idx, (ra, dec) in enumerate(zip(RA1, DEC1)):
c1 = SkyCoord(ra=ra*u.degree, dec=dec*u.degree)
x = c1.separation(c2)
这将已经产生数个数量级的加速:
RA1 = np.random.uniform(0,360,5)
DEC1 = np.random.uniform(-90,90,5)
RA2 = np.random.uniform(0,360,10)
DEC2 = np.random.uniform(-90,90,10)
def test(RA1, DEC1, RA2, DEC2):
"""Version with vectorized inner loop."""
c2 = SkyCoord(ra=RA2*u.degree, dec=DEC2*u.degree)
for idx, (ra, dec) in enumerate(zip(RA1, DEC1)):
c1 = SkyCoord(ra=ra*u.degree, dec=dec*u.degree)
x = c1.separation(c2)
def test2(RA1, DEC1, RA2, DEC2):
"""Double loop."""
for ra, dec in zip(RA1, DEC1):
c1 = SkyCoord(ra=ra*u.degree, dec=dec*u.degree)
for ra, dec in zip(RA2, DEC2):
c2 = SkyCoord(ra=ra*u.degree, dec=dec*u.degree)
x = c1.separation(c2)
%timeit test(RA1, DEC1, RA2, DEC2)
%timeit test2(RA1, DEC1, RA2, DEC2)
这已经快了10倍,而且它的可扩展性要好得多:
RA1 = np.random.uniform(0,360,5)
DEC1 = np.random.uniform(-90,90,5)
RA2 = np.random.uniform(0,360,2000000)
DEC2 = np.random.uniform(-90,90,2000000)
%timeit test(RA1, DEC1, RA2, DEC2) # 1 loop, best of 3: 2.8 s per loop
# test2 scales so bad I only use 50 elements here
RA2 = np.random.uniform(0,360,50)
DEC2 = np.random.uniform(-90,90,50)
%timeit test2(RA1, DEC1, RA2, DEC2) # 1 loop, best of 3: 11.4 s per loop
请注意,通过向量化内部循环,我能够在1/4的时间内计算出40000倍以上的元素。因此,通过向量化内部循环,你应该能够快大约200k倍。
在这里,我们在3秒钟内计算了5次200万个分离操作,所以每个操作大约需要300纳秒。以这个速度,完成这个任务需要3天时间。
即使你也可以消除剩余循环中的向量化,我认为这不会产生任何巨大的加速,因为在那个层面上,循环开销远小于每个循环中的计算时间。使用
line-profiler
也支持这种说法。
Line
==============================================================
11 def test(RA1, DEC1, RA2, DEC2):
12 1 216723 216723.0 2.6 c2 = SkyCoord(ra=RA2*u.degree, dec=DEC2*u.degree)
13 6 222 37.0 0.0 for idx, (ra, dec) in enumerate(zip(RA1, DEC1)):
14 5 206796 41359.2 2.5 c1 = SkyCoord(ra=ra*u.degree, dec=dec*u.degree)
15 5 7847321 1569464.2 94.9 x = c1.separation(c2)
如果从这个Hits
中看不出来,它是从5个2,000,000次运行中得出的结果。为了比较,这里是从一个5x20次运行中得到的test2
结果:
Line
==============================================================
17 def test2(RA1, DEC1, RA2, DEC2):
18 6 80 13.3 0.0 for ra, dec in zip(RA1, DEC1):
19 5 195030 39006.0 0.6 c1 = SkyCoord(ra=ra*u.degree, dec=dec*u.degree)
20 105 1737 16.5 0.0 for ra, dec in zip(RA2, DEC2):
21 100 3871427 38714.3 11.8 c2 = SkyCoord(ra=ra*u.degree, dec=dec*u.degree)
22 100 28870724 288707.2 87.6 x = c1.separation(c2)
test2
比
test1
效率更低的原因是,
c2 = SkyCoord
部分占用了总时间的12%,而不仅仅是2.5%,每个单独调用
separation
都有一定的开销。因此它变慢的原因并不是Python循环的开销,而是
SkyCoord
构造函数和
separation
的静态部分。
显然需要将pa
计算和保存到文件的过程向量化(我没有使用过PyAstronomy
和numpy.savetext
,所以无法给出建议)。
但是,仍然存在一个问题,即在普通计算机上进行一万亿次三角函数运算是不可行的。
一些减少时间的额外想法:
使用多处理器,使您计算机的每个核心并行工作,理论上可以通过您的核心数量加速运行。实际情况下,这可能达不到目标,并且我建议仅在拥有>= 8个内核或可用集群时才执行此操作。否则,花费在正确地获取多处理器运行的时间可能超过单核心运行时间。特别是因为多处理器可能无法正常工作,然后您必须重新运行计算。
预处理元素:删除RA或DEC差异使匹配变得不可能的项目。但是,如果这不能删除一部分元素,则额外的减法和比较实际上可能会降低速度。
numpy.array
用作list
的替代品。 - Eli Korvigorange
替换为xrange
。 - Laurent LAPORTE