[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-commits] [stem/master] Benchmarks for our descriptor parsing libraries



commit 6dd30e5beda287cd02fabd257028e1711aa63f99
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date:   Sat Jan 16 19:18:25 2016 -0800

    Benchmarks for our descriptor parsing libraries
    
    Karsten, Philipp, and I have been discussing library benchmarks on tor-dev@.
    Adding the findings...
    
      https://lists.torproject.org/pipermail/tor-dev/2016-January/010230.html
---
 docs/_static/example/benchmark_metrics_lib.java    |  302 ++++++++++++++++++++
 .../benchmark_server_descriptor_metrics_lib.java   |   49 ++++
 .../example/benchmark_server_descriptor_stem.py    |   21 ++
 .../example/benchmark_server_descriptor_zoossh.go  |   68 +++++
 docs/_static/example/benchmark_stem.py             |   81 ++++++
 docs/_static/example/benchmark_zoossh.go           |  120 ++++++++
 docs/tutorials/mirror_mirror_on_the_wall.rst       |   96 +++++--
 7 files changed, 712 insertions(+), 25 deletions(-)

diff --git a/docs/_static/example/benchmark_metrics_lib.java b/docs/_static/example/benchmark_metrics_lib.java
new file mode 100644
index 0000000..63b5e4b
--- /dev/null
+++ b/docs/_static/example/benchmark_metrics_lib.java
@@ -0,0 +1,302 @@
+/* Copyright 2016 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.SortedMap;
+
+public class MeasurePerformance {
+
+  /* Check if all necessary files are available and then measure
+   * performance of some more or less common use cases. */
+  public static void main(String[] args) {
+    if (!filesAvailable()) {
+      return;
+    }
+    /*measureListOutdatedRelays(null);
+    pause();*/
+    measureAverageAdvertisedBandwidth(new File(resDir, resPaths[0]));
+    pause();
+    measureAverageAdvertisedBandwidth(new File(resDir, resPaths[1]));
+    pause();
+    measureAverageAdvertisedBandwidth(new File(resDir, resPaths[2]));
+    pause();
+    /*measureFractionRelaysExit80ServerDescriptors(null);
+    pause();
+    measureSumOfWrittenAndReadBytes(null);
+    pause();*/
+    measureCountriesV3Requests(new File(resDir, resPaths[3]));
+    pause();
+    measureCountriesV3Requests(new File(resDir, resPaths[4]));
+    pause();
+    measureAverageRelaysExit(new File(resDir, resPaths[5]));
+    pause();
+    measureAverageRelaysExit(new File(resDir, resPaths[6]));
+    pause();
+    measureAverageRelaysExit(new File(resDir, resPaths[7]));
+    /*pause();
+    measureVotesByBandwidthAuthorities(null);
+    pause();
+    measureExtendedFamilies(null);
+    pause();*/
+    measureFractionRelaysExit80Microdescriptors(
+        new File(resDir, resPaths[8]));
+    measureFractionRelaysExit80Microdescriptors(
+        new File(resDir, resPaths[9]));
+  }
+
+  private static File resDir = new File("res");
+  private static String[] resPaths = new String[] {
+    "archive/relay-descriptors/server-descriptors/"
+        + "server-descriptors-2015-11.tar.xz",
+    "archive/relay-descriptors/server-descriptors/"
+        + "server-descriptors-2015-11.tar",
+    "archive/relay-descriptors/server-descriptors/"
+        + "server-descriptors-2015-11",
+    "archive/relay-descriptors/extra-infos/extra-infos-2015-11.tar.xz",
+    "archive/relay-descriptors/extra-infos/extra-infos-2015-11.tar",
+    "archive/relay-descriptors/consensuses/consensuses-2015-11.tar.xz",
+    "archive/relay-descriptors/consensuses/consensuses-2015-11.tar",
+    "archive/relay-descriptors/consensuses/consensuses-2015-11",
+    "archive/relay-descriptors/microdescs/"
+        + "microdescs-2015-11-micro.tar.xz",
+    "archive/relay-descriptors/microdescs/microdescs-2015-11-micro.tar"
+  };
+
+  private static boolean filesAvailable() {
+    if (!resDir.exists() || !resDir.isDirectory()) {
+      return false;
+    }
+    for (String resPath : resPaths) {
+      if (!(new File(resDir, resPath).exists())) {
+        System.err.println(resPath);
+        return false;
+      }
+    }
+    return true;
+  }
+
+  private static void pause() {
+    try {
+      Thread.sleep(15L * 1000L);
+    } catch (InterruptedException e) {
+      e.printStackTrace();
+    }
+  }
+
+  private static void measureListOutdatedRelays(File tarballFile) {
+    
+  }
+
+  private static void measureAverageAdvertisedBandwidth(
+      File tarballFileOrDirectory) {
+    System.out.println("Starting measureAverageAdvertisedBandwidth");
+    long startedMillis = System.currentTimeMillis();
+    long sumAdvertisedBandwidth = 0, countedServerDescriptors = 0;
+    DescriptorReader descriptorReader =
+        DescriptorSourceFactory.createDescriptorReader();
+    descriptorReader.addTarball(tarballFileOrDirectory);
+    descriptorReader.addDirectory(tarballFileOrDirectory);
+    Iterator<DescriptorFile> descriptorFiles =
+        descriptorReader.readDescriptors();
+    while (descriptorFiles.hasNext()) {
+      DescriptorFile descriptorFile = descriptorFiles.next();
+      for (Descriptor descriptor : descriptorFile.getDescriptors()) {
+        if (!(descriptor instanceof ServerDescriptor)) {
+          continue;
+        }
+        ServerDescriptor serverDescriptor = (ServerDescriptor) descriptor;
+        sumAdvertisedBandwidth += (long) Math.min(Math.min(
+            serverDescriptor.getBandwidthRate(),
+            serverDescriptor.getBandwidthBurst()),
+            serverDescriptor.getBandwidthObserved());
+        countedServerDescriptors++;
+      }
+    }
+    long endedMillis = System.currentTimeMillis();
+    System.out.println("Ending measureAverageAdvertisedBandwidth");
+    System.out.printf("Total time: %d millis%n",
+        endedMillis - startedMillis);
+    System.out.printf("Processed server descriptors: %d%n",
+        countedServerDescriptors);
+    System.out.printf("Average advertised bandwidth: %d%n",
+        sumAdvertisedBandwidth / countedServerDescriptors);
+    System.out.printf("Time per server descriptor: %.6f millis%n",
+        ((double) (endedMillis - startedMillis))
+        / ((double) countedServerDescriptors));
+  }
+
+  private static void measureFractionRelaysExit80ServerDescriptors(
+      File tarballFile) {
+    
+  }
+
+  private static void measureSumOfWrittenAndReadBytes(File tarballFile) {
+
+  }
+
+  private static void measureCountriesV3Requests(File tarballFile) {
+    System.out.println("Starting measureCountriesV3Requests");
+    long startedMillis = System.currentTimeMillis();
+    Set<String> countries = new HashSet<>();
+    long countedExtraInfoDescriptors = 0;
+    DescriptorReader descriptorReader =
+        DescriptorSourceFactory.createDescriptorReader();
+    descriptorReader.addTarball(tarballFile);
+    Iterator<DescriptorFile> descriptorFiles =
+        descriptorReader.readDescriptors();
+    while (descriptorFiles.hasNext()) {
+      DescriptorFile descriptorFile = descriptorFiles.next();
+      for (Descriptor descriptor : descriptorFile.getDescriptors()) {
+        if (!(descriptor instanceof ExtraInfoDescriptor)) {
+          continue;
+        }
+        ExtraInfoDescriptor extraInfoDescriptor =
+            (ExtraInfoDescriptor) descriptor;
+        SortedMap<String, Integer> dirreqV3Reqs =
+            extraInfoDescriptor.getDirreqV3Reqs();
+        if (dirreqV3Reqs != null) {
+          countries.addAll(dirreqV3Reqs.keySet());
+        }
+        countedExtraInfoDescriptors++;
+      }
+    }
+    long endedMillis = System.currentTimeMillis();
+    System.out.println("Ending measureCountriesV3Requests");
+    System.out.printf("Total time: %d millis%n",
+        endedMillis - startedMillis);
+    System.out.printf("Processed extra-info descriptors: %d%n",
+        countedExtraInfoDescriptors);
+    System.out.printf("Number of countries: %d%n",
+        countries.size());
+    System.out.printf("Time per extra-info descriptor: %.6f millis%n",
+        ((double) (endedMillis - startedMillis))
+        / ((double) countedExtraInfoDescriptors));
+  }
+
+  private static void measureAverageRelaysExit(
+      File tarballFileOrDirectory) {
+    System.out.println("Starting measureAverageRelaysExit");
+    long startedMillis = System.currentTimeMillis();
+    long totalRelaysWithExitFlag = 0L, totalRelays = 0L,
+        countedConsensuses = 0L;
+    DescriptorReader descriptorReader =
+        DescriptorSourceFactory.createDescriptorReader();
+    descriptorReader.addTarball(tarballFileOrDirectory);
+    descriptorReader.addDirectory(tarballFileOrDirectory);
+    Iterator<DescriptorFile> descriptorFiles =
+        descriptorReader.readDescriptors();
+    while (descriptorFiles.hasNext()) {
+      DescriptorFile descriptorFile = descriptorFiles.next();
+      for (Descriptor descriptor : descriptorFile.getDescriptors()) {
+        if (!(descriptor instanceof RelayNetworkStatusConsensus)) {
+          continue;
+        }
+        RelayNetworkStatusConsensus consensus =
+            (RelayNetworkStatusConsensus) descriptor;
+        for (NetworkStatusEntry entry :
+            consensus.getStatusEntries().values()) {
+          if (entry.getFlags().contains("Exit")) {
+            totalRelaysWithExitFlag++;
+          }
+          totalRelays++;
+        }
+        countedConsensuses++;
+      }
+    }
+    long endedMillis = System.currentTimeMillis();
+    System.out.println("Ending measureAverageRelaysExit");
+    System.out.printf("Total time: %d millis%n",
+        endedMillis - startedMillis);
+    System.out.printf("Processed consensuses: %d%n", countedConsensuses);
+    System.out.printf("Total number of status entries: %d%n",
+        totalRelays);
+    System.out.printf("Total number of status entries with Exit flag: "
+        + "%d%n", totalRelaysWithExitFlag);
+    System.out.printf("Average number of relays with Exit Flag: %.2f%n",
+        (double) totalRelaysWithExitFlag / (double) totalRelays);
+    System.out.printf("Time per consensus: %.6f millis%n",
+        ((double) (endedMillis - startedMillis))
+        / ((double) countedConsensuses));
+  }
+
+  private static void measureVotesByBandwidthAuthorities(
+      File tarballFile) {
+    
+  }
+
+  private static void measureExtendedFamilies(File tarballFile) {
+    
+  }
+
+  private static void measureFractionRelaysExit80Microdescriptors(
+      File tarballFile) {
+    System.out.println("Starting "
+        + "measureFractionRelaysExit80Microdescriptors");
+    long startedMillis = System.currentTimeMillis();
+    long totalRelaysWithExitFlag = 0L, countedMicrodescriptors = 0L;
+    DescriptorReader descriptorReader =
+        DescriptorSourceFactory.createDescriptorReader();
+    descriptorReader.addTarball(tarballFile);
+    Iterator<DescriptorFile> descriptorFiles =
+        descriptorReader.readDescriptors();
+    while (descriptorFiles.hasNext()) {
+      DescriptorFile descriptorFile = descriptorFiles.next();
+      for (Descriptor descriptor : descriptorFile.getDescriptors()) {
+        if (!(descriptor instanceof Microdescriptor)) {
+          continue;
+        }
+        countedMicrodescriptors++;
+        Microdescriptor microdescriptor =
+            (Microdescriptor) descriptor;
+        String defaultPolicy = microdescriptor.getDefaultPolicy();
+        if (defaultPolicy == null) {
+          continue;
+        }
+        boolean accept = "accept".equals(
+            microdescriptor.getDefaultPolicy());
+        for (String ports : microdescriptor.getPortList().split(",")) {
+          if (ports.contains("-")) {
+            String[] parts = ports.split("-");
+            int from = Integer.parseInt(parts[0]);
+            int to = Integer.parseInt(parts[1]);
+            if (from <= 80 && to >= 80) {
+              if (accept) {
+                totalRelaysWithExitFlag++;
+              }
+            } else if (to > 80) {
+              if (!accept) {
+                totalRelaysWithExitFlag++;
+              }
+              break;
+            }
+          } else if ("80".equals(ports)) {
+            if (accept) {
+              totalRelaysWithExitFlag++;
+            }
+            break;
+          }
+        }
+      }
+    }
+    long endedMillis = System.currentTimeMillis();
+    System.out.println("Ending "
+        + "measureFractionRelaysExit80Microdescriptors");
+    System.out.printf("Total time: %d millis%n",
+        endedMillis - startedMillis);
+    System.out.printf("Processed microdescriptors: %d%n",
+        countedMicrodescriptors);
+    System.out.printf("Total number of microdescriptors that exit to 80: "
+        + "%d%n", totalRelaysWithExitFlag);
+    System.out.printf("Average number of relays that exit to 80: %.2f%n",
+        (double) totalRelaysWithExitFlag
+        / (double) countedMicrodescriptors);
+    System.out.printf("Time per microdescriptor: %.6f millis%n",
+        ((double) (endedMillis - startedMillis))
+        / ((double) countedMicrodescriptors));
+  }
+}
+
diff --git a/docs/_static/example/benchmark_server_descriptor_metrics_lib.java b/docs/_static/example/benchmark_server_descriptor_metrics_lib.java
new file mode 100644
index 0000000..efb8634
--- /dev/null
+++ b/docs/_static/example/benchmark_server_descriptor_metrics_lib.java
@@ -0,0 +1,49 @@
+package org.torproject.descriptor;
+
+import java.io.File;
+import java.util.Iterator;
+import org.torproject.descriptor.DescriptorSourceFactory;
+
+public class MeasurePerformance {
+  public static void main(String[] args) {
+    measureAverageAdvertisedBandwidth(new File("server-descriptors-2015-11.tar"));
+  }
+
+  private static void measureAverageAdvertisedBandwidth(
+      File tarballFileOrDirectory) {
+    System.out.println("Starting measureAverageAdvertisedBandwidth");
+    long startedMillis = System.currentTimeMillis();
+    long sumAdvertisedBandwidth = 0, countedServerDescriptors = 0;
+    DescriptorReader descriptorReader =
+        DescriptorSourceFactory.createDescriptorReader();
+    descriptorReader.addTarball(tarballFileOrDirectory);
+    descriptorReader.addDirectory(tarballFileOrDirectory);
+    Iterator<DescriptorFile> descriptorFiles =
+        descriptorReader.readDescriptors();
+    while (descriptorFiles.hasNext()) {
+      DescriptorFile descriptorFile = descriptorFiles.next();
+      for (Descriptor descriptor : descriptorFile.getDescriptors()) {
+        if (!(descriptor instanceof ServerDescriptor)) {
+          continue;
+        }
+        ServerDescriptor serverDescriptor = (ServerDescriptor) descriptor;
+        sumAdvertisedBandwidth += (long) Math.min(Math.min(
+            serverDescriptor.getBandwidthRate(),
+            serverDescriptor.getBandwidthBurst()),
+            serverDescriptor.getBandwidthObserved());
+        countedServerDescriptors++;
+      }
+    }
+    long endedMillis = System.currentTimeMillis();
+    System.out.println("Ending measureAverageAdvertisedBandwidth");
+    System.out.printf("Total time: %d millis%n",
+        endedMillis - startedMillis);
+    System.out.printf("Processed server descriptors: %d%n",
+        countedServerDescriptors);
+    System.out.printf("Average advertised bandwidth: %d%n",
+        sumAdvertisedBandwidth / countedServerDescriptors);
+    System.out.printf("Time per server descriptor: %.6f millis%n",
+        ((double) (endedMillis - startedMillis))
+        / ((double) countedServerDescriptors));
+  }
+}
diff --git a/docs/_static/example/benchmark_server_descriptor_stem.py b/docs/_static/example/benchmark_server_descriptor_stem.py
new file mode 100644
index 0000000..c475652
--- /dev/null
+++ b/docs/_static/example/benchmark_server_descriptor_stem.py
@@ -0,0 +1,21 @@
+import time
+import stem.descriptor
+
+def measure_average_advertised_bandwidth(path):
+  start_time = time.time()
+  total_bw, count = 0, 0
+
+  for desc in stem.descriptor.parse_file(path):
+    total_bw += min(desc.average_bandwidth, desc.burst_bandwidth, desc.observed_bandwidth)
+    count += 1
+
+  runtime = time.time() - start_time
+  print("Finished measure_average_advertised_bandwidth('%s')" % path)
+  print('  Total time: %i seconds' % runtime)
+  print('  Processed server descriptors: %i' % count)
+  print('  Average advertised bandwidth: %i' % (total_bw / count))
+  print('  Time per server descriptor: %0.5f seconds' % (runtime / count))
+  print('')
+
+if __name__ == '__main__':
+  measure_average_advertised_bandwidth('server-descriptors-2015-11.tar')
diff --git a/docs/_static/example/benchmark_server_descriptor_zoossh.go b/docs/_static/example/benchmark_server_descriptor_zoossh.go
new file mode 100644
index 0000000..f72b3fb
--- /dev/null
+++ b/docs/_static/example/benchmark_server_descriptor_zoossh.go
@@ -0,0 +1,68 @@
+package main
+
+import (
+  "fmt"
+  "os"
+  "path/filepath"
+  "time"
+
+  "git.torproject.org/user/phw/zoossh.git"
+)
+
+var processedDescs int64 = 0
+var totalBw uint64 = 0
+
+func Min(a uint64, b uint64, c uint64) uint64 {
+  min := a
+
+  if b < min {
+    min = b
+  }
+
+  if c < min {
+    min = c
+  }
+
+  return min
+}
+
+func ProcessDescriptors(path string, info os.FileInfo, err error) error {
+  if _, err := os.Stat(path); err != nil {
+    return err
+  }
+
+  if info.IsDir() {
+    return nil
+  }
+
+  consensus, err := zoossh.ParseDescriptorFile(path)
+  if err != nil {
+    return err
+  }
+
+  if (processedDescs % 100) == 0 {
+    fmt.Printf(".")
+  }
+
+  for _, getDesc := range consensus.RouterDescriptors {
+    desc := getDesc()
+    totalBw += Min(desc.BandwidthAvg, desc.BandwidthBurst, desc.BandwidthObs)
+    processedDescs++
+  }
+
+  return nil
+}
+
+func main() {
+  before = time.Now()
+  filepath.Walk("server-descriptors-2015-11", ProcessDescriptors)
+  fmt.Println()
+  after = time.Now()
+
+  duration = after.Sub(before)
+  fmt.Println("Total time for descriptors:", duration)
+  fmt.Printf("Time per descriptor: %dns\n",
+    duration.Nanoseconds()/processedDescs)
+  fmt.Printf("Processed %d descriptors.\n", processedDescs)
+  fmt.Printf("Average advertised bandwidth: %d\n", totalBw/uint64(processedDescs))
+}
diff --git a/docs/_static/example/benchmark_stem.py b/docs/_static/example/benchmark_stem.py
new file mode 100644
index 0000000..8b71d73
--- /dev/null
+++ b/docs/_static/example/benchmark_stem.py
@@ -0,0 +1,81 @@
+import time
+import stem.descriptor
+
+def measure_average_advertised_bandwidth(path):
+  start_time = time.time()
+  total_bw, count = 0, 0
+
+  for desc in stem.descriptor.parse_file(path):
+    total_bw += min(desc.average_bandwidth, desc.burst_bandwidth, desc.observed_bandwidth)
+    count += 1
+
+  runtime = time.time() - start_time
+  print("Finished measure_average_advertised_bandwidth('%s')" % path)
+  print('  Total time: %i seconds' % runtime)
+  print('  Processed server descriptors: %i' % count)
+  print('  Average advertised bandwidth: %i' % (total_bw / count))
+  print('  Time per server descriptor: %0.5f seconds' % (runtime / count))
+  print('')
+
+def measure_countries_v3_requests(path):
+  start_time = time.time()
+  countries, count = set(), 0
+
+  for desc in stem.descriptor.parse_file(path):
+    if desc.dir_v3_responses:
+      countries.update(desc.dir_v3_responses.keys())
+
+    count += 1
+
+  runtime = time.time() - start_time
+  print("Finished measure_countries_v3_requests('%s')" % path)
+  print('  Total time: %i seconds' % runtime)
+  print('  Processed extrainfo descriptors: %i' % count)
+  print('  Number of countries: %i' % len(countries))
+  print('  Time per extrainfo descriptor: %0.5f seconds' % (runtime / count))
+  print('')
+
+def measure_average_relays_exit(path):
+  start_time = time.time()
+  total_relays, exits, consensuses = 0, 0, 0
+
+  for consensus in stem.descriptor.parse_file(path, document_handler = stem.descriptor.DocumentHandler.DOCUMENT):
+    for desc in consensus.routers.values():
+      if 'Exit' in desc.flags:
+        exits += 1
+
+      total_relays += 1
+
+    consensuses += 1
+
+  runtime = time.time() - start_time
+  print("Finished measure_average_relays_exit('%s')" % path)
+  print('  Total time: %i seconds' % runtime)
+  print('  Processed %i consensuses with %i router status entries' % (consensuses, total_relays))
+  print('  Total exits: %i (%0.2f%%)' % (exits, float(exits) / total_relays))
+  print('  Time per consensus: %0.5f seconds' % (runtime / consensuses))
+  print('')
+
+def measure_fraction_relays_exit_80_microdescriptors(path):
+  start_time = time.time()
+  exits, count = 0, 0
+
+  for desc in stem.descriptor.parse_file(path):
+    if desc.exit_policy.can_exit_to(port = 80):
+      exits += 1
+
+    count += 1
+
+  runtime = time.time() - start_time
+  print("Finished measure_fraction_relays_exit_80_microdescriptors('%s')" % path)
+  print('  Total time: %i seconds' % runtime)
+  print('  Processed microdescriptors: %i' % count)
+  print('  Total exits to port 80: %i (%0.2f%%)' % (exits, float(exits) / count))
+  print('  Time per microdescriptor: %0.5f seconds' % (runtime / count))
+  print('')
+
+measure_average_advertised_bandwidth('/home/atagar/Desktop/server-descriptors-2015-11.tar')
+measure_countries_v3_requests('/home/atagar/Desktop/extra-infos-2015-11.tar')
+measure_average_relays_exit('/home/atagar/Desktop/consensuses-2015-11.tar')
+measure_fraction_relays_exit_80_microdescriptors('/home/atagar/Desktop/microdescs-2015-11.tar')
+
diff --git a/docs/_static/example/benchmark_zoossh.go b/docs/_static/example/benchmark_zoossh.go
new file mode 100644
index 0000000..7cfc04f
--- /dev/null
+++ b/docs/_static/example/benchmark_zoossh.go
@@ -0,0 +1,120 @@
+package main
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"time"
+
+	"git.torproject.org/user/phw/zoossh.git"
+)
+
+var processedCons int64 = 0
+var processedDescs int64 = 0
+var totalExits int64 = 0
+var totalRelays int64 = 0
+var totalBw uint64 = 0
+
+func Min(a uint64, b uint64, c uint64) uint64 {
+
+	min := a
+
+	if b < min {
+		min = b
+	}
+
+	if c < min {
+		min = c
+	}
+
+	return min
+}
+
+func ProcessDescriptors(path string, info os.FileInfo, err error) error {
+
+	if _, err := os.Stat(path); err != nil {
+		return err
+	}
+
+	if info.IsDir() {
+		return nil
+	}
+
+	consensus, err := zoossh.ParseDescriptorFile(path)
+	if err != nil {
+		return err
+	}
+
+	if (processedDescs % 100) == 0 {
+		fmt.Printf(".")
+	}
+
+	for _, getDesc := range consensus.RouterDescriptors {
+		desc := getDesc()
+		totalBw += Min(desc.BandwidthAvg, desc.BandwidthBurst, desc.BandwidthObs)
+		processedDescs++
+	}
+
+	return nil
+}
+
+func ProcessConsensus(path string, info os.FileInfo, err error) error {
+
+	if _, err := os.Stat(path); err != nil {
+		return err
+	}
+
+	if info.IsDir() {
+		return nil
+	}
+
+	consensus, err := zoossh.ParseConsensusFile(path)
+	if err != nil {
+		return err
+	}
+	fmt.Printf(".")
+
+	for _, getStatus := range consensus.RouterStatuses {
+		status := getStatus()
+		totalRelays++
+		if status.Flags.Exit == true {
+			totalExits++
+		}
+	}
+	processedCons++
+
+	return nil
+}
+
+func main() {
+
+	if len(os.Args) != 3 {
+		log.Fatalf("Usage: %s CONSENSUS_ARCHIVE DESCRIPTOR_ARCHIVE", os.Args[0])
+	}
+
+	before := time.Now()
+	filepath.Walk(os.Args[1], ProcessConsensus)
+	fmt.Println()
+	after := time.Now()
+
+	duration := after.Sub(before)
+	fmt.Println("Total time for consensuses:", duration)
+	fmt.Printf("Time per consensus: %dms\n",
+		duration.Nanoseconds()/processedCons/int64(1000000))
+	fmt.Printf("Processed %d consensuses with %d router status entries.\n",
+		processedCons, totalRelays)
+	fmt.Printf("Total exits: %d\n", totalExits)
+
+	before = time.Now()
+	filepath.Walk(os.Args[2], ProcessDescriptors)
+	fmt.Println()
+	after = time.Now()
+
+	duration = after.Sub(before)
+	fmt.Println("Total time for descriptors:", duration)
+	fmt.Printf("Time per descriptor: %dns\n",
+		duration.Nanoseconds()/processedDescs)
+	fmt.Printf("Processed %d descriptors.\n", processedDescs)
+	fmt.Printf("Average advertised bandwidth: %d\n", totalBw/uint64(processedDescs))
+}
diff --git a/docs/tutorials/mirror_mirror_on_the_wall.rst b/docs/tutorials/mirror_mirror_on_the_wall.rst
index ee524e4..656d228 100644
--- a/docs/tutorials/mirror_mirror_on_the_wall.rst
+++ b/docs/tutorials/mirror_mirror_on_the_wall.rst
@@ -263,31 +263,77 @@ Each library has its own capabilities...
 .. role:: red
 .. role:: green
 
-=========================== ================= =============== ==============
-Capability                  Stem              Metrics-lib     Zoossh
-=========================== ================= =============== ==============
-Language                    :green:`Python`   :green:`Java`   :green:`Go`
-Checks signatures           :green:`Mostly`   :red:`No`       :red:`No`
-Create new descriptors      :red:`No`         :red:`No`       :red:`No`
-Lazy parsing                :green:`Yes`      :red:`No`       :green:`Yes`
-Type detection by @type     :green:`Yes`      :green:`Yes`    :green:`Yes`
-Type detection by filename  :green:`Yes`      :red:`No`       :red:`No`
-Packages                    :green:`Several`  :red:`None`     :red:`None`
+=========================== ===================== =================== ==============
+Capability                  Stem                  Metrics-lib         Zoossh
+=========================== ===================== =================== ==============
+Language                    :green:`Python`       :green:`Java`       :green:`Go`
+Checks signatures           :green:`Mostly`       :red:`No`           :red:`No`
+Create new descriptors      :red:`No`             :red:`No`           :red:`No`
+Lazy parsing                :green:`Yes`          :red:`No`           :green:`Yes`
+Type detection by @type     :green:`Yes`          :green:`Yes`        :green:`Yes`
+Type detection by filename  :green:`Yes`          :red:`No`           :red:`No`
+Packages                    :green:`Several`      :red:`None`         :red:`None`
 **Can Read/Download From**
-Files                       :green:`Yes`      :green:`Yes`    :green:`Yes`
-Tarballs                    :green:`Yes`      :green:`Yes`    :red:`No`
-Tor Process                 :green:`Yes`      :red:`No`       :red:`No`
-Directory Authorities       :green:`Yes`      :green:`Yes`    :red:`No`
-CollecTor                   :red:`No`         :green:`Yes`    :red:`No`
+Files                       :green:`Yes`          :green:`Yes`        :green:`Yes`
+Tarballs                    :green:`Yes`          :green:`Yes`        :red:`No`
+Tor Process                 :green:`Yes`          :red:`No`           :red:`No`
+Directory Authorities       :green:`Yes`          :green:`Yes`        :red:`No`
+CollecTor                   :red:`No`             :green:`Yes`        :red:`No`
 **Supported Types**
-Server Descriptors          :green:`Yes`      :green:`Yes`    :green:`Partly`
-Extrainfo Descriptors       :green:`Yes`      :green:`Yes`    :red:`No`
-Microdescriptors            :green:`Yes`      :green:`Yes`    :red:`No`
-Consensus                   :green:`Yes`      :green:`Yes`    :green:`Partly`
-Bridge Descriptors          :green:`Yes`      :green:`Yes`    :red:`No`
-Hidden Service Descriptors  :green:`Yes`      :red:`No`       :red:`No`
-Bridge Pool Assignments     :red:`No`         :green:`Yes`    :red:`No`
-Torperf                     :red:`No`         :green:`Yes`    :red:`No`
-Tordnsel                    :green:`Yes`      :green:`Yes`    :red:`No`
-=========================== ================= =============== ==============
+Server Descriptors          :green:`Yes`          :green:`Yes`        :green:`Partly`
+Extrainfo Descriptors       :green:`Yes`          :green:`Yes`        :red:`No`
+Microdescriptors            :green:`Yes`          :green:`Yes`        :red:`No`
+Consensus                   :green:`Yes`          :green:`Yes`        :green:`Partly`
+Bridge Descriptors          :green:`Yes`          :green:`Yes`        :red:`No`
+Hidden Service Descriptors  :green:`Yes`          :red:`No`           :red:`No`
+Bridge Pool Assignments     :red:`No`             :green:`Yes`        :red:`No`
+Torperf                     :red:`No`             :green:`Yes`        :red:`No`
+Tordnsel                    :green:`Yes`          :green:`Yes`        :red:`No`
+**Benchmarks**
+Server Descriptors          :green:`0.63 ms`      :green:`0.29 ms`    :green:`0.46 ms`
+Extrainfo Descriptors       :green:`0.42 ms`      :green:`0.22 ms`    :red:`unsupported`
+Microdescriptors            :green:`0.34 ms`      :green:`0.07 ms`    :red:`unsupported`
+Consensus                   :green:`876.09 ms`    :green:`246.71 ms`  :green:`83.00 ms`
+Benchmarked With Commit     :green:`c01a9cd`      :green:`8767f3e`    :green:`2380e55`
+Language Interpreter        :green:`Python 3.5.1` :green:`Java 1.7.0` :green:`Go 1.5.2`
+=========================== ===================== =================== ==============
+
+Few things to note about these benchmarks...
+
+* **Zoossh is the fastest.** Its benchmarks were at a disadvantage due to not
+  reading from tarballs.
+
+* Your Python version makes a very large difference for Stem. For instance,
+  with Python 2.7 reading a consensus takes **1,393.10 ms** (almost twice as
+  long).
+
+* Metrics-lib and Stem can both read from compressed tarballs at a small
+  performance cost. For instance, Metrics-lib can read an `lzma compressed
+  <../faq.html#how-do-i-read-tar-xz-descriptor-archives>`_ consensus in
+  **255.76 ms** and Stem can do it in **913.12 ms**.
+
+So what does code with each of these look like?
+
+Stem Example
+------------
+
+* `Benchmark Script <../.../../_static/example/benchmark_stem.py>`_
+
+.. literalinclude:: /_static/example/benchmark_server_descriptor_stem.py
+   :language: python
+
+Metrics-lib Example
+-------------------
+
+* `Benchmark Script <../.../../_static/example/benchmark_metrics_lib.java>`_
+
+.. literalinclude:: /_static/example/benchmark_server_descriptor_metrics_lib.java
+   :language: java
+
+Zoossh Example
+--------------
+
+* `Benchmark Script <../.../../_static/example/benchmark_zoossh.go>`_
 
+.. literalinclude:: /_static/example/benchmark_server_descriptor_zoossh.go
+   :language: go

_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits