76 lines
2.4 KiB
Go
76 lines
2.4 KiB
Go
|
|
package basic
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"math"
|
|||
|
|
"sync"
|
|||
|
|
"sync/atomic"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// Nutation2000B 是 jd 的纯函数,但升落、星历与掩星路径会对同一批瞬时反复求值:一条计算链里
|
|||
|
|
// 地球自转、状态上下文与几何装配各自求一次同一个瞬时。这里用有界直接映射表记住结果:
|
|||
|
|
// 槽位固定、无分配、RWMutex 保证 c-shared 宿主多线程安全。章动只依赖传入的 jd,与 ΔT 世代无关。
|
|||
|
|
// Nutation2000B is a pure function of jd, yet the rise/set, ephemeris and occultation chains evaluate
|
|||
|
|
// it repeatedly for the same instants from independent code paths. This bounded direct-mapped memo
|
|||
|
|
// removes that redundancy with fixed slots, no allocation and an RWMutex for the c-shared host.
|
|||
|
|
// Nutation depends only on its argument, so no ΔT generation stamp is needed.
|
|||
|
|
const nutationMemoBits = 13
|
|||
|
|
|
|||
|
|
const nutationMemoSize = 1 << nutationMemoBits
|
|||
|
|
|
|||
|
|
type nutationMemoEntry struct {
|
|||
|
|
// key 是 math.Float64bits(jd)+1,0 表示空槽(避免 jd=0 与空槽同码)。
|
|||
|
|
key uint64
|
|||
|
|
dpsi, deps float64
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var (
|
|||
|
|
nutationMemoMu sync.RWMutex
|
|||
|
|
nutationMemoTable [nutationMemoSize]nutationMemoEntry
|
|||
|
|
nutationMemoHits uint64
|
|||
|
|
nutationMemoMiss uint64
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
func nutationMemoIndex(jd float64) uint64 {
|
|||
|
|
bits := math.Float64bits(jd)
|
|||
|
|
return (bits ^ (bits >> 31)) & (nutationMemoSize - 1)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func nutationMemoLoad(jd float64) (float64, float64, bool) {
|
|||
|
|
key := math.Float64bits(jd) + 1
|
|||
|
|
entry := &nutationMemoTable[nutationMemoIndex(jd)]
|
|||
|
|
nutationMemoMu.RLock()
|
|||
|
|
entryKey, dpsi, deps := entry.key, entry.dpsi, entry.deps
|
|||
|
|
nutationMemoMu.RUnlock()
|
|||
|
|
if key != 0 && entryKey == key {
|
|||
|
|
atomic.AddUint64(&nutationMemoHits, 1)
|
|||
|
|
return dpsi, deps, true
|
|||
|
|
}
|
|||
|
|
atomic.AddUint64(&nutationMemoMiss, 1)
|
|||
|
|
return 0, 0, false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func nutationMemoStore(jd, dpsi, deps float64) {
|
|||
|
|
key := math.Float64bits(jd) + 1
|
|||
|
|
if key == 0 {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
nutationMemoMu.Lock()
|
|||
|
|
nutationMemoTable[nutationMemoIndex(jd)] = nutationMemoEntry{key: key, dpsi: dpsi, deps: deps}
|
|||
|
|
nutationMemoMu.Unlock()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// nutationMemoStats 返回命中/未命中计数,供测试守护命中率。
|
|||
|
|
func nutationMemoStats() (uint64, uint64) {
|
|||
|
|
return atomic.LoadUint64(&nutationMemoHits), atomic.LoadUint64(&nutationMemoMiss)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func resetNutationMemo() {
|
|||
|
|
nutationMemoMu.Lock()
|
|||
|
|
for i := range nutationMemoTable {
|
|||
|
|
nutationMemoTable[i] = nutationMemoEntry{}
|
|||
|
|
}
|
|||
|
|
nutationMemoMu.Unlock()
|
|||
|
|
atomic.StoreUint64(&nutationMemoHits, 0)
|
|||
|
|
atomic.StoreUint64(&nutationMemoMiss, 0)
|
|||
|
|
}
|