Skip to content

Commit

Permalink
Generate polygonToCells benchmark with Natural Earth country geometry (
Browse files Browse the repository at this point in the history
…#795)

Adds a script and CMake targets to download relatively simple geometry for all countries and create a benchmark file. The benchmark currently runs both old and new algos for comparison.
  • Loading branch information
nrabinowitz authored Nov 3, 2023
1 parent 5d7c1c1 commit 6c1b003
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .github/workflows/test-bench.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
run: |
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_COUNTRY_BENCHMARKS=ON ..
- name: Build
run: |
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ RunCoverage.cmake
# Generated coverage runner
scripts/coverage.sh


# CTest / coverage
CTestTestfile.cmake
Testing/
Expand All @@ -105,3 +106,4 @@ KML/res*centers.kml

# Generated files
src/h3lib/include/h3api.h
src/apps/benchmarks/benchmarkCountries.c
11 changes: 11 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ endif()

option(ENABLE_COVERAGE "Enable compiling tests with coverage." OFF)
option(BUILD_BENCHMARKS "Build benchmarking applications." ON)
option(ENABLE_COUNTRY_BENCHMARKS "Build benchmarking applications with Natural Earth geometries." OFF)
option(BUILD_FUZZERS "Build fuzzer applications (for use with afl)." ON)
option(BUILD_FILTERS "Build filter applications." ON)
option(BUILD_GENERATORS "Build code generation applications." ON)
Expand Down Expand Up @@ -577,6 +578,16 @@ if(BUILD_BENCHMARKS)
if(ENABLE_REQUIRES_ALL_SYMBOLS)
add_h3_benchmark(benchmarkPolygon src/apps/benchmarks/benchmarkPolygon.c)
endif()

if(ENABLE_COUNTRY_BENCHMARKS)
# Country benchmark: Downloads country geometry and generates the benchmark file
add_custom_command(OUTPUT src/apps/benchmarks/benchmarkCountries.c
COMMAND node ${CMAKE_CURRENT_SOURCE_DIR}/scripts/make_countries.js ${CMAKE_CURRENT_BINARY_DIR}/src/apps/benchmarks/benchmarkCountries.c
)

add_h3_benchmark(benchmarkCountries ${CMAKE_CURRENT_BINARY_DIR}/src/apps/benchmarks/benchmarkCountries.c)
# add_dependencies(bench_benchmarkCountries )
endif()
endif()

# Installation (https://github.com/forexample/package-example)
Expand Down
178 changes: 178 additions & 0 deletions scripts/make_countries.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
#!/usr/bin/env node
/*
* Copyright 2023 Uber Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/** @file make_countries.js
* @brief Script to make country test fixtures based on Natural Earth data.
* This makes the fixture with polygonToCells benchmarks.
*/

const fs = require('fs');
const path = require('path');
const https = require('https');

// Using GeoJSON version as it is easy to convert
const SOURCE_URL = 'https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/ne_50m_admin_0_countries.geojson';
const TARGET = process.argv[2];

// Use Node HTTPS module for download, to avoid dependencies
function getSource(url) {
return new Promise((resolve, reject) => {
https.get(url, (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
resolve(data);
});
}).on('error', reject);
});
}

function degsToRads(deg) {
return (deg * Math.PI) / 180;
}

function formatCoord([lng, lat]) {
return `{${degsToRads(lat)}, ${degsToRads(lng)}}`;
}

function formatGeoLoop(loop) {
return `{
.numVerts = ${loop.length},
.verts = (LatLng[]) {
${loop.map(formatCoord).join(',\n')}
}
}`
}

function formatGeoPolygon(poly, {i, name, verts}) {
const holes = poly.slice(1);

return `
// ${i}. ${name} (${verts} verts)
{
.geoloop = ${formatGeoLoop(poly[0])},
.numHoles = ${holes.length}${holes.length ? `,
.holes = (GeoLoop[]) {
${holes.map(formatGeoLoop).join(',\n')}
}` : ''}
}`
}

async function makeCountries(sourceUrl, targetPath) {
console.log(`Downloading from ${sourceUrl}...`)
const countriesJson = await getSource(sourceUrl);
const countries = JSON.parse(countriesJson);

console.log(`Download completed, found ${countries.features.length} countries`);

const polygons = [];
const names = [];

let i = 0;

for (const {geometry, properties: {ADMIN: name}} of countries.features) {
if (geometry.type === 'Polygon') {
polygons.push(geometry.coordinates);
names.push({i: String(i++), name, verts: geometry.coordinates[0].length});
} else if (geometry.type === 'MultiPolygon') {
for (const poly of geometry.coordinates) {
polygons.push(poly);
names.push({i: String(i++), name, verts: poly[0].length});
}
}
}

console.log(`Found ${polygons.length} polygons`);

// In order to only allocate memory once, we take the biggest size
// at res 7, pad it, and size up based on powers of 7. This number was
// empirically determined.
const MAX_SIZE_RES_7 = 17544338;

const out = `/*
* Copyright 2023 Uber Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This file is generated by scripts/make_countries.js.
* Made with Natural Earth. Contains data from nvkelso/natural-earth-vector.
* https://github.com/nvkelso/natural-earth-vector/blob/ca96624a56bd078437bca8184e78163e5039ad19/LICENSE.md
*/
#include "benchmark.h"
#include "h3api.h"
#include "mathExtensions.h"
#include "polyfill.h"
const GeoPolygon COUNTRIES[${polygons.length}] = {${
polygons.map((poly, i) => formatGeoPolygon(poly, names[i])).join(',')
}};
BEGIN_BENCHMARKS();
int MAX_RES = 5;
// Max size of largest polygon at res 5, rounded up with padding
int64_t numHexagons = 400000;
H3Index *hexagons = calloc(numHexagons, sizeof(H3Index));
for (int res = 0; res < MAX_RES + 1; res++) {
printf("Res %d", res);
BENCHMARK(polygonToCells_AllCountries1, 5, {
for (int index = 0; index < ${polygons.length}; index++) {
H3_EXPORT(polygonToCells)(&COUNTRIES[index], res, 0, hexagons);
}
});
BENCHMARK(polygonToCells_AllCountries2, 5, {
for (int index = 0; index < ${polygons.length}; index++) {
H3_EXPORT(polygonToCellsExperimental)(&COUNTRIES[index], res, 0, hexagons);
}
});
}
free(hexagons);
END_BENCHMARKS();
`

fs.mkdirSync(path.dirname(targetPath), { recursive:true });
fs.writeFileSync(targetPath, out, 'utf-8');

console.log(`Wrote fixture to ${targetPath}`);
}

makeCountries(SOURCE_URL, TARGET);



6 changes: 6 additions & 0 deletions website/docs/core-library/compilation-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ Whether to build the parts of the [test suite](./testing) that exercise the [H3_

Whether to build the [benchmark suite](./testing#benchmarks).

## BUILD_COUNTRY_BENCHMARKS

Whether to build benchmarks that use [Natural Earth](https://github.com/nvkelso/natural-earth-vector/) geometries.

The geometries will be downloaded at build time using Node.

## BUILD_FILTERS

Whether to build the [H3 command line filter](./filters) executables.
Expand Down

0 comments on commit 6c1b003

Please sign in to comment.