Benchmarking
diff --git a/src/ProtoBench/Program.cs b/src/ProtoBench/Program.cs
index 9b65c0b..e679858 100644
--- a/src/ProtoBench/Program.cs
+++ b/src/ProtoBench/Program.cs
@@ -16,49 +16,67 @@
     delegate void Action();
 
     public static int Main(string[] args) {
-      if (args.Length != 2) {
-        Console.Error.WriteLine("Usage: ProtoBecnh <descriptor type name> <input data>");
+      if (args.Length < 2 || (args.Length % 2) != 0) {
+        Console.Error.WriteLine("Usage: ProtoBench <descriptor type name> <input data>");
         Console.Error.WriteLine("The descriptor type name is the fully-qualified message name,");
         Console.Error.WriteLine("including assembly - e.g. Google.ProtocolBuffers.BenchmarkProtos.Message1,ProtoBench");
+        Console.Error.WriteLine("(You can specify multiple pairs of descriptor type name and input data.)");
         return 1;
       }
+      bool success = true;
+      for (int i = 0; i < args.Length; i += 2) {
+        success &= RunTest(args[i], args[i + 1]);
+      }
+      return success ? 0 : 1;
+    }
+
+    /// <summary>
+    /// Runs a single test. Error messages are displayed to Console.Error, and the return value indicates
+    /// general success/failure.
+    /// </summary>
+    public static bool RunTest(string typeName, string file) {
+      Console.WriteLine("Benchmarking {0} with file {1}", typeName, file);
       IMessage defaultMessage;
       try {
-        defaultMessage = MessageUtil.GetDefaultMessage(args[0]);
+        defaultMessage = MessageUtil.GetDefaultMessage(typeName);
       } catch (ArgumentException e) {
         Console.Error.WriteLine(e.Message);
-        return 1;
+        return false;
       }
       try {
-        IBuilder builder = defaultMessage.WeakCreateBuilderForType();
-        byte[] inputData = File.ReadAllBytes(args[1]);
+        byte[] inputData = File.ReadAllBytes(file);
+        MemoryStream inputStream = new MemoryStream(inputData);
         ByteString inputString = ByteString.CopyFrom(inputData);
         IMessage sampleMessage = defaultMessage.WeakCreateBuilderForType().WeakMergeFrom(inputString).WeakBuild();
         sampleMessage.ToByteString();
-        Console.WriteLine("Benchmarking {0} with file {1}", sampleMessage.GetType().Name, args[1]);
         Benchmark("Serialize to byte string", inputData.Length, () => sampleMessage.ToByteString());
         Benchmark("Serialize to byte array", inputData.Length, () => sampleMessage.ToByteArray());
         Benchmark("Serialize to memory stream", inputData.Length, () => sampleMessage.WriteTo(new MemoryStream()));
+        Benchmark("Deserialize from byte string", inputData.Length, () =>
+          defaultMessage.WeakCreateBuilderForType()
+              .WeakMergeFrom(inputString)
+              .WeakBuild()
+        );
         Benchmark("Deserialize from byte array", inputData.Length, () =>
           defaultMessage.WeakCreateBuilderForType()
               .WeakMergeFrom(CodedInputStream.CreateInstance(inputData))
               .WeakBuild()
         );
-        Benchmark("Deserialize from byte array", inputData.Length, () =>
+        Benchmark("Deserialize from memory stream", inputData.Length, () =>
           defaultMessage.WeakCreateBuilderForType()
-              .WeakMergeFrom(inputString)
+              .WeakMergeFrom(CodedInputStream.CreateInstance(inputStream))
               .WeakBuild()
         );
-        return 0;
+        return true;
       } catch (Exception e) {
         Console.Error.WriteLine("Error: {0}", e.Message);
         Console.Error.WriteLine();
         Console.Error.WriteLine("Detailed exception information: {0}", e);
-        return 1;
+        return false;
       }
     }
 
-    private static void Benchmark(string name, int dataSize, Action action) {
+    private static void Benchmark(string name, long dataSize, Action action) {
       // Make sure it's JITted
       action();
       // Run it progressively more times until we've got a reasonable sample