From 6c1b00307fa7f2b3babf68006190120ac640c06c Mon Sep 17 00:00:00 2001 From: Nick Rabinowitz Date: Thu, 2 Nov 2023 17:00:24 -0700 Subject: [PATCH] Generate polygonToCells benchmark with Natural Earth country geometry (#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. --- .github/workflows/test-bench.yml | 2 +- .gitignore | 2 + CMakeLists.txt | 11 ++ scripts/make_countries.js | 178 ++++++++++++++++++ .../docs/core-library/compilation-options.md | 6 + 5 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 scripts/make_countries.js diff --git a/.github/workflows/test-bench.yml b/.github/workflows/test-bench.yml index 33ca6930d..5e59bb9d9 100644 --- a/.github/workflows/test-bench.yml +++ b/.github/workflows/test-bench.yml @@ -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: | diff --git a/.gitignore b/.gitignore index c6c22e644..5ecab9725 100644 --- a/.gitignore +++ b/.gitignore @@ -83,6 +83,7 @@ RunCoverage.cmake # Generated coverage runner scripts/coverage.sh + # CTest / coverage CTestTestfile.cmake Testing/ @@ -105,3 +106,4 @@ KML/res*centers.kml # Generated files src/h3lib/include/h3api.h +src/apps/benchmarks/benchmarkCountries.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 11198211d..4c90917a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) @@ -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) diff --git a/scripts/make_countries.js b/scripts/make_countries.js new file mode 100644 index 000000000..4e8a495d4 --- /dev/null +++ b/scripts/make_countries.js @@ -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); + + + diff --git a/website/docs/core-library/compilation-options.md b/website/docs/core-library/compilation-options.md index b74223cb5..dcc55f12c 100644 --- a/website/docs/core-library/compilation-options.md +++ b/website/docs/core-library/compilation-options.md @@ -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.