Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions bench_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package h3

import (
"strconv"
"testing"
)

Expand Down Expand Up @@ -53,6 +54,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)
Expand Down
36 changes: 36 additions & 0 deletions h3.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ package h3
#include <h3_h3Index.h>
#include <h3_polygon.h>
#include <h3_polyfill.h>
#include <h3_latLngBatch.h>
*/
import "C"

Expand Down Expand Up @@ -234,6 +235,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)
Expand Down
18 changes: 18 additions & 0 deletions h3_latLngBatch.c

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions h3_latLngBatch.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 49 additions & 0 deletions h3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,55 @@ func TestLatLngToCell(t *testing.T) {
assertErrIs(t, err, ErrResolutionDomain)
}

func TestLatLngToCellBatch(t *testing.T) {
t.Parallel()
t.Run("matches per-call result across resolutions", func(t *testing.T) {
t.Parallel()

lls := []LatLng{validLatLng1, validLatLng2}
for res := 0; res <= MaxResolution; res++ {
want := make([]Cell, len(lls))
for i, ll := range lls {
c, err := LatLngToCell(ll, res)
assertNoErr(t, err)
want[i] = c
}
got, err := LatLngToCellBatch(lls, res)
assertNoErr(t, err)
assertEqual(t, len(want), len(got))

for i := range got {
assertEqual(t, want[i], got[i])
}
}
})

t.Run("empty", func(t *testing.T) {
t.Parallel()

cells, err := LatLngToCellBatch(nil, 9)
assertNil(t, cells)
assertNil(t, err)
})

t.Run("single element", func(t *testing.T) {
t.Parallel()

cells, err := LatLngToCellBatch([]LatLng{validLatLng1}, 5)
assertNoErr(t, err)
assertEqual(t, 1, len(cells))
assertEqual(t, validCell, cells[0])
})

t.Run("invalid resolution surfaces error", func(t *testing.T) {
t.Parallel()

_, err := LatLngToCellBatch([]LatLng{validLatLng1}, MaxResolution+1)
assertErr(t, err)
assertErrIs(t, err, ErrResolutionDomain)
})
}

func TestCellToLatLng(t *testing.T) {
t.Parallel()

Expand Down
Loading