diff --git a/bench_test.go b/bench_test.go index a5dbaf9..9686d53 100644 --- a/bench_test.go +++ b/bench_test.go @@ -1,6 +1,7 @@ package h3 import ( + "strconv" "testing" ) @@ -56,6 +57,41 @@ func BenchmarkLatLngToCell(b *testing.B) { } } +func BenchmarkLatLngToCellBatch(b *testing.B) { + for _, n := range []int{1, 64, 1024, 16384, 1_000_000, 10_000_000} { + lls := make([]LatLng, n) + for i := range lls { + lls[i] = geo + } + + b.Run(strconv.Itoa(n), func(b *testing.B) { + for b.Loop() { + cells, _ = LatLngToCellBatch(lls, 15) + } + }) + } +} + +// Baseline: same workload via the per-call LatLngToCell in a Go loop, +// so reviewers can confirm the speedup at each batch size by comparing +// matching sub-benchmark names with benchstat. +func BenchmarkLatLngToCellBaseline(b *testing.B) { + for _, n := range []int{1, 64, 1024, 16384, 1_000_000, 10_000_000} { + lls := make([]LatLng, n) + for i := range lls { + lls[i] = geo + } + + b.Run(strconv.Itoa(n), func(b *testing.B) { + for b.Loop() { + for _, ll := range lls { + cell, _ = LatLngToCell(ll, 15) + } + } + }) + } +} + func BenchmarkCellToBoundary(b *testing.B) { for range b.N { geoBndry, _ = CellToBoundary(cell) diff --git a/h3.go b/h3.go index 045683f..beaa752 100644 --- a/h3.go +++ b/h3.go @@ -27,6 +27,7 @@ package h3 #include #include #include +#include */ import "C" @@ -261,6 +262,41 @@ func LatLngToCell(latLng LatLng, resolution int) (Cell, error) { return Cell(i), toErr(errC) } +// LatLngToCellBatch resolves a slice of LatLng to H3 cells at the +// provided resolution with one cgo transition for the whole batch. +// +// Equivalent to calling LatLngToCell once per element, but amortizes +// the per-call cgo overhead. +// +// Output cell ordering matches the input ordering. Returns nil and +// the first H3 error encountered if any LatLng fails resolution; +// partial results are not returned. +func LatLngToCellBatch(lls []LatLng, resolution int) ([]Cell, error) { + n := len(lls) + if n == 0 { + return nil, nil + } + cLLs := make([]C.LatLng, n) + for i, ll := range lls { + cLLs[i] = ll.toC() + } + cOut := make([]C.H3Index, n) + errC := C.latLngToCellBatch( + &cLLs[0], + C.size_t(n), + C.int(resolution), + &cOut[0], + ) + if err := toErr(errC); err != nil { + return nil, err + } + out := make([]Cell, n) + for i := range cOut { + out[i] = Cell(cOut[i]) + } + return out, nil +} + // Cell returns the Cell at resolution for a geographic coordinate. func (g LatLng) Cell(resolution int) (Cell, error) { return LatLngToCell(g, resolution) diff --git a/h3_latLngBatch.c b/h3_latLngBatch.c new file mode 100644 index 0000000..ae4c82c --- /dev/null +++ b/h3_latLngBatch.c @@ -0,0 +1,18 @@ +#include + +H3Error latLngToCellBatch( + const LatLng *lls, + size_t n, + int res, + H3Index *out) +{ + for (size_t i = 0; i < n; i++) + { + H3Error err = latLngToCell(&lls[i], res, &out[i]); + if (err != E_SUCCESS) + { + return err; + } + } + return E_SUCCESS; +} \ No newline at end of file diff --git a/h3_latLngBatch.h b/h3_latLngBatch.h new file mode 100644 index 0000000..84570bc --- /dev/null +++ b/h3_latLngBatch.h @@ -0,0 +1,36 @@ +// LatLng Batch helpers maintained by h3-go on top of the cloned H3 core. +// Lives here so we can ship batched/amortized variants of single-shot +// APIs without modifying the upsteam C library. Each function takes +// input already in H3-native form (e.g. LatLng in radians), matching +// the wire convention the existing Go bindings use after toC(). +// +// If H3 core later exposes equivalents, the Go wrappers can be +// re-pointed at the core symbols and these can be deleted. + +#ifndef H3_EXT_H +#define H3_EXT_H + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + // latLngToCellBatch resolved n LatLng inputs (already in radians) to + // H3 cells at a single resolution. Output buffer must be sized n by + // the caller. Returns E_SUCCESS on success, or the first H3Error + // encountered (and stops). Output for rows past the failing index is + // undefined. + H3Error latLngToCellBatch( + const LatLng *lls, + size_t n, + int res, + H3Index *out); + +#ifdef __cplusplus +} +#endif + +#endif // H3_LAT_LNG_BATCH_H \ No newline at end of file