如何优化使用Skyfield进行日出日落计算的执行性能
Skyfield批量太阳升落判断性能优化方案
一、完全替换Python循环的NumPy向量化实现
wgs84.latlon不支持数组输入是上层接口限制,而非Skyfield底层不支持批量计算,你可以直接调用底层的GeographicPosition构造批量位置对象,一次性完成所有坐标的计算,完全消除Python循环开销:
import datetime as dt import numpy as np from skyfield import almanac from skyfield.api import Loader from skyfield.toposlib import GeographicPosition from skyfield.units import Angle from skyfield_data import get_skyfield_data_path load = Loader(get_skyfield_data_path()) eph = load("de421.bsp") ts = load.timescale() # 构造测试时间 datetimes = [ dt.datetime(2000, 1, 1, tzinfo=dt.timezone.utc) + dt.timedelta(minutes=m) for m in range(24 * 60) ] times = ts.from_datetimes(datetimes) # 构造测试坐标 coordinates = np.array([ (50.0, lon) for lon in np.linspace(0.0, 30.0, 1000) ]) # 批量构造位置对象,无需循环 lat = Angle(degrees=coordinates[:, 0]) lon = Angle(degrees=coordinates[:, 1]) batch_pos = GeographicPosition(latitude=lat, longitude=lon) # 一次性计算所有坐标所有时间的昼夜状态 is_day_batch = almanac.sunrise_sunset(eph, batch_pos) results = is_day_batch(times) # 最终results形状为 (坐标数量, 时间点数量),和原循环输出完全一致
该方案和原代码计算精度完全一致,性能提升幅度和坐标数量正相关,1000个坐标的场景下通常能达到几十到上百倍的加速。
二、可牺牲精度的额外加速方案
如果性能仍不满足需求,可以通过以下方式折中精度换速度:
- 更换低精度星历:将
de421.bsp替换为体积更小的de440s.bsp,或使用Skyfield内置的低精度太阳位置计算接口,太阳位置计算速度可提升3-5倍,误差在1角秒以内,对昼夜判断的影响可以忽略。 - 直接计算太阳高度角判断:跳过
almanac.sunrise_sunset的封装逻辑,预先计算所有时间点的太阳地心位置,再批量计算每个坐标点的太阳视高度,判断高度角大于-0.83°即为白天,可避免almanac模块的额外校验开销,速度再提升2倍左右,误差小于1分钟。 - 时间维度降采样:如果不需要分钟级判断,可将时间采样间隔从1分钟扩大到5-15分钟,中间结果用线性插值填充,速度提升幅度和采样间隔正相关,昼夜判断误差小于采样间隔的一半。
- 用Numba JIT编译:如果仍有自定义逻辑的循环无法向量化,用Numba装饰对应的计算函数,可获得接近C语言的运算速度。
内容的提问来源于stack exchange,提问作者Florian Brucker
相关产品推荐
相关产品推荐

