generalize invocation of C# microbenchmarks
diff --git a/src/csharp/Grpc.Microbenchmarks/Grpc.Microbenchmarks.csproj b/src/csharp/Grpc.Microbenchmarks/Grpc.Microbenchmarks.csproj
index 108357e..8a629f9 100644
--- a/src/csharp/Grpc.Microbenchmarks/Grpc.Microbenchmarks.csproj
+++ b/src/csharp/Grpc.Microbenchmarks/Grpc.Microbenchmarks.csproj
@@ -15,6 +15,10 @@
     <ProjectReference Include="../Grpc.Core/Grpc.Core.csproj" />
   </ItemGroup>
 
+  <ItemGroup>
+    <PackageReference Include="CommandLineParser" Version="2.1.1-beta" />
+  </ItemGroup>
+
   <ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
     <Reference Include="System" />
     <Reference Include="Microsoft.CSharp" />
diff --git a/src/csharp/Grpc.Microbenchmarks/Program.cs b/src/csharp/Grpc.Microbenchmarks/Program.cs
index d07d418..79ec308 100644
--- a/src/csharp/Grpc.Microbenchmarks/Program.cs
+++ b/src/csharp/Grpc.Microbenchmarks/Program.cs
@@ -20,14 +20,81 @@
 using Grpc.Core;
 using Grpc.Core.Internal;
 using Grpc.Core.Logging;
+using CommandLine;
+using CommandLine.Text;
 
 namespace Grpc.Microbenchmarks
 {
     class Program
     {
+        public enum MicrobenchmarkType
+        {
+            CompletionRegistry,
+            PInvokeByteArray,
+            SendMessage
+        }
+
+        private class BenchmarkOptions
+        {
+            [Option("benchmark", Required = true, HelpText = "Benchmark to run")]
+            public MicrobenchmarkType Benchmark { get; set; }
+        }
+
         public static void Main(string[] args)
         {
             GrpcEnvironment.SetLogger(new ConsoleLogger());
+            var parserResult = Parser.Default.ParseArguments<BenchmarkOptions>(args)
+                .WithNotParsed(errors => {
+                    Console.WriteLine("Supported benchmarks:");
+                    foreach (var enumValue in Enum.GetValues(typeof(MicrobenchmarkType)))
+                    {
+                        Console.WriteLine("  " + enumValue);
+                    }
+                    Environment.Exit(1);
+                })
+                .WithParsed(options =>
+                {
+                    switch (options.Benchmark)
+                    {
+                        case MicrobenchmarkType.CompletionRegistry:
+                          RunCompletionRegistryBenchmark();
+                          break;
+                        case MicrobenchmarkType.PInvokeByteArray:
+                          RunPInvokeByteArrayBenchmark();
+                          break;
+                        case MicrobenchmarkType.SendMessage:
+                          RunSendMessageBenchmark();
+                          break;
+                        default:
+                          throw new ArgumentException("Unsupported benchmark.");
+                    }
+                });
+        }
+
+        static void RunCompletionRegistryBenchmark()
+        {
+            var benchmark = new CompletionRegistryBenchmark();
+            benchmark.Init();
+            foreach (int threadCount in new int[] {1, 1, 2, 4, 8, 12})
+            {
+                benchmark.Run(threadCount, 4 * 1000 * 1000);
+            }
+            benchmark.Cleanup();
+        }
+
+        static void RunPInvokeByteArrayBenchmark()
+        {
+            var benchmark = new PInvokeByteArrayBenchmark();
+            benchmark.Init();
+            foreach (int threadCount in new int[] {1, 1, 2, 4, 8, 12})
+            {
+                benchmark.Run(threadCount, 4 * 1000 * 1000, 0);
+            }
+            benchmark.Cleanup();
+        }
+
+        static void RunSendMessageBenchmark()
+        {
             var benchmark = new SendMessageBenchmark();
             benchmark.Init();
             foreach (int threadCount in new int[] {1, 1, 2, 4, 8, 12})