private static Results run(Path tmpDir, int writeWorkers, int readThreads, long items, int numChunks,
    boolean mmap, boolean parallelSort, boolean madvise, PlanetilerConfig config) {
    long chunkSizeLimit = items * ITEM_MEMORY_BYTES / numChunks;
    if (chunkSizeLimit > Integer.MAX_VALUE) {
      throw new IllegalStateException("Chunk size too big: " + chunkSizeLimit);
    }
    boolean gzip = false;
    int sortWorkers = Runtime.getRuntime().availableProcessors();
    FileUtils.delete(tmpDir);
    var sorter =
      new ExternalMergeSort(tmpDir, sortWorkers, (int) chunkSizeLimit, gzip, mmap, parallelSort, madvise, config,
        Stats.inMemory());

    var writeTimer = Timer.start();
    doWrites(writeWorkers, items, sorter);
    writeTimer.stop();

    var sortTimer = Timer.start();
    sorter.sort();
    sortTimer.stop();

    var readTimer = Timer.start();
    doReads(readThreads, items, sorter);
    readTimer.stop();

    return new Results(
      FORMAT.numeric(items * NANOSECONDS_PER_SECOND / writeTimer.elapsed().wall().toNanos()) + "/s",
      FORMAT.numeric(items * NANOSECONDS_PER_SECOND / readTimer.elapsed().wall().toNanos()) + "/s",
      FORMAT.duration(sortTimer.elapsed().wall()),
      sorter.chunks(),
      writeWorkers,
      readThreads,
      items,
      (int) chunkSizeLimit,
      gzip,
      mmap,
      parallelSort,
      madvise
    );
  }
Performance tests for {@link ExternalMergeSort}. Times how long it takes to write temp features, sort, then read them
back with different parameters.
<p>
Usage: {@code BenchmarkExternalMergeSort &lt;number of GB of features to write&gt;}
planetiler-benchmarks/src/main/java/com/onthegomap/planetiler/collection/BenchmarkExternalMergeSort.java