v23/syncbase: Add extra flags to syncbase ping pong benchmark

You can now add numSync and numGroup flags to the test runner in order
to vary the number of peers in the syncgroup as well as the number of
overall syncgroups.

This is going to be used to examine how latency changes as these
numbers vary.

For example, you can run with 2 syncgroups and 3 peers:
jiri go test -test.bench=BenchmarkPingPongPair -test.benchtime=5s
  -run XXX -numGroup=2 -numSync=3 -v23test-syncbase-debug-args "-vpath=*=0"

Change-Id: I23922f1740d7101538597acabd35c0c35cd81715
diff --git a/syncbase/featuretests/ping_pong_test.go b/syncbase/featuretests/ping_pong_test.go
index c9654ba..18aee4f 100644
--- a/syncbase/featuretests/ping_pong_test.go
+++ b/syncbase/featuretests/ping_pong_test.go
@@ -5,6 +5,7 @@
 package featuretests_test
 
 import (
+	"flag"
 	"fmt"
 	"os"
 	"testing"
@@ -20,6 +21,9 @@
 
 const pingPongPairIterations = 500
 
+var numSync *int = flag.Int("numSync", 2, "run test with 2 or more syncbases")
+var numGroup *int = flag.Int("numGroup", 1, "run test with 1 or more syncgroups")
+
 // BenchmarkPingPongPair measures the round trip sync latency between a pair of
 // Syncbase instances that Ping Pong data to each other over a syncgroup.
 //
@@ -34,6 +38,13 @@
 // After the benchmark completes, the "ns/op" value refers to the average time
 // for |pingPongPairIterations| of Ping Pong roundtrips completed.
 func BenchmarkPingPongPair(b *testing.B) {
+	flag.Parse()
+	if *numSync < 2 {
+		b.Fatalf("numSync should be at least 2, received %d\n", *numSync)
+	}
+	if *numGroup < 1 {
+		b.Fatalf("numGroup should be at least 1, received %d\n", *numGroup)
+	}
 	sh := v23test.NewShell(b, nil)
 	defer sh.Cleanup()
 	sh.StartRootMountTable()
@@ -42,27 +53,38 @@
 	b.StopTimer()
 
 	for iter := 0; iter < b.N; iter++ {
-		// Setup 2 Syncbases.
-		sbs := setupSyncbases(b, sh, 2)
+		// Setup *numSync Syncbases.
+		sbs := setupSyncbases(b, sh, *numSync)
 
-		// Syncbase s0 is the creator.
-		sgName := naming.Join(sbs[0].sbName, constants.SyncbaseSuffix, "SG1")
+		// Setup *numGroup Syncgroups
+		for g := 0; g < *numGroup; g++ {
+			// Syncbase s0 is the creator.
+			sgSuffix := fmt.Sprintf("SG%d", g+1)
+			sgName := naming.Join(sbs[0].sbName, constants.SyncbaseSuffix, sgSuffix)
 
-		// TODO(alexfandrianto): Was unable to use the empty prefix ("tb:").
-		// Observation: w0's watch isn't working with the empty prefix.
-		// Possible Explanation: The empty prefix ACL receives an initial value from
-		// the Table ACL. If this value is synced over from the opposing peer,
-		// conflict resolution can mean that s0 loses the ability to watch.
-		syncString := fmt.Sprintf("%s:p", testTable)
-		ok(b, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgName, syncString, "", sbBlessings(sbs), nil))
+			// TODO(alexfandrianto): Was unable to use the empty prefix ("tb:").
+			// Observation: w0's watch isn't working with the empty prefix.
+			// Possible Explanation: The empty prefix ACL receives an initial value from
+			// the Table ACL. If this value is synced over from the opposing peer,
+			// conflict resolution can mean that s0 loses the ability to watch.
+			syncString := fmt.Sprintf("%s:p", testTable)
+			ok(b, createSyncgroup(sbs[0].clientCtx, sbs[0].sbName, sgName, syncString, "", sbBlessings(sbs), nil))
 
-		// Syncbase s1 will attempt to join the syncgroup.
-		ok(b, joinSyncgroup(sbs[1].clientCtx, sbs[1].sbName, sgName))
+			// The other syncbases will attempt to join the syncgroup.
+			for i := 1; i < *numSync; i++ {
+				ok(b, joinSyncgroup(sbs[i].clientCtx, sbs[i].sbName, sgName))
+			}
+		}
 
 		// Obtain the handles to the databases.
 		db0, _ := getDbAndTable(sbs[0].sbName)
 		db1, _ := getDbAndTable(sbs[1].sbName)
 
+		// Setup the remaining syncbases
+		for i := 2; i < *numSync; i++ {
+			getDbAndTable(sbs[i].sbName)
+		}
+
 		// Set up the watch streams (watching the other syncbase's prefix).
 		prefix0, prefix1 := "prefix0", "prefix1"
 		w0, err := db0.Watch(sbs[0].clientCtx, testTable, prefix1, watch.ResumeMarker("now"))