Back
Featured image of post Benchmarking Go Ethereum account creation in Android

Benchmarking Go Ethereum account creation in Android

How to run native Go code In Android and bench results!

Table of Content

How to run native Go code In Android and bench results!

I was wondering today about how Android faces some of the cryptographic challenges of current Blockchain implementations. In order to solve this question, I just tried the best way I know: code, benchmark and plot it.

How a single Ethereum address is created?

Following the official Geth Ethereum implementation, an account creation is defined as follows in Go:

1
2
3
func GenerateKey() (*ecdsa.PrivateKey, error) {
    return ecdsa.GenerateKey(S256(), rand.Reader)
}

To generate the so typical Ethereum address and private key encoded as hexadecimal string, we just need to call following methods over previously created ECDSA struct:

To get the public address string

1
2
// Get the address
address := crypto.PubkeyToAddress(key.PublicKey).Hex()

To get the private key string

1
2
// Get the private key
privateKey := hex.EncodeToString(key.D.Bytes())

How single core benchmark is designed?

Since we are going to run benchmarks at runtime, we cannot make use of go benchmarking tools and we need to implement our own benchmarking method. For this purpose, we will design a wrapper function that takes two time snapshots and computes the difference, returning metrics results.

The data we are going to collect is wrapped in BenchResponse struct

1
2
3
4
5
6
type BenchResponse struct {
   Ms      int   `json:"micros"`
   Millis  int   `json:"millis"`
   Size    int   `json:"size"`
   Created int   `json:"created"`
}

To measure the single core runtime execution time, we will make use of following wrapper function:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
func measureFunc(size int, f func() bool) BenchResponse {
   ts := time.Now()
   i := 0
   for i < size {
      if f() {
         break
      }
      i++
   }
   duration := time.Since(ts)
   return BenchResponse{
      Ms:      int(duration.Microseconds()),
      Millis:  int(duration.Milliseconds()),
      Size:    size,
      Created: i,
   }
}

Our example test, will generate 1000 Ethereum addresses sequentially by running:

1
2
3
4
5
6
func AccountBenchSingleCore(size int) BenchResponse {
   return measureFunc(size, func() bool {
      pk, genErr := crypto.GenerateKey()
      return genErr != nil || pk == nil
   })
}

Prior executing this test in Android, we run a simple unit test to make sure it works as expected:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
t.Run("single-core", func(t *testing.T) {
   r := AccountBenchSingleCore(1000)

   assert.NotNil(t, r)
   assert.Equal(t, r.Created, 1000)

   t.Log(r)
   t.Log("time micros:", r.Ms)
   t.Log("time millis: ", r.Millis)
})

How multi-core benchmark is designed?

In order to allow parallel creations of Ethereum accounts over multiple cores, we will modify previous code and implement both channels and wait groups to create the same amount of accounts.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
func AccountBenchMultiCore(size int) BenchResponse {
   var wg sync.WaitGroup
   wg.Add(size)
   ts := time.Now()

   for i := 0; i < size; i++ {
      go func() {
         _,_ = crypto.GenerateKey()
         wg.Done()
      }()
   }
   wg.Wait()
   duration := time.Since(ts)
   return BenchResponse{
      Ms:      int(duration.Microseconds()),
      Millis:  int(duration.Milliseconds()),
      Size:    size,
      Created: size,
   }
}

Prior executing this test in Android, we run a simple unit test to make sure it works as expected with no data race conditions.

1
2
3
4
5
6
7
8
t.Run("multi-core", func(t *testing.T) {
   r := AccountBenchMultiCore(1000)
   assert.NotNil(t, r)
   assert.Equal(t, r.Created, 1000)
   t.Log(r)
   t.Log("time micros:", r.Ms)
   t.Log("time millis: ", r.Millis)
})

Running unit test using go test give us an initial result and difference between single core version and concurrent version

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
go test -c -o /tmp/test
go tool test2json -t /tmp/test -test.v -test.run ^TestAccountBench$
=== RUN   TestAccountBench
--- PASS: TestAccountBench (0.32s)
=== RUN   TestAccountBench/single-core
    --- PASS: TestAccountBench/single-core (0.21s)
        api_bench_test.go:15: &{208782 208 1000 1000}
        api_bench_test.go:16: time micros: 208782
        api_bench_test.go:17: time millis:  208
=== RUN   TestAccountBench/multi-core
    --- PASS: TestAccountBench/multi-core (0.11s)
        api_bench_test.go:23: &{114739 114 1000 1000}
        api_bench_test.go:24: time micros: 114739
        api_bench_test.go:25: time millis:  114
PASSProcess finished with exit code 0

After plotting results, we can view the huge difference that we can get by running the same code in single core or multi-core.

Benchmarking results: Concurrent vs Parallel
Benchmarking results: Concurrent vs Parallel

Compiling for Android device

To test our Go code in Android, it is necessary to compile it to a shared object library (*.aar) using Gomobile. In this case, the command that triggers the compilation of our Go code is:

1
gomobile bind -o ./benchtest.aar -a -target=android projects/benchtest

The result is a pair of files: *aar library and source code:

1
2
rw-rw-r — 1 root root 9,5M dic 2 20:23 benchtest.aar
rw-rw-r — 1 root root  11K dic 2 20:23 benchtest-sources.jar

Testing on real Android device!

We created a demo application, include the shared library and its sources and create a dummy trigger from Activity setting view as shown below.

Let’s execute or Benchmarking function from AppCompatActivity
Let’s execute or Benchmarking function from AppCompatActivity

The “Run Benchmarking” button will call native Go code inside of the *.so file and return our benchmark data result to the user as Dialog popup.

Benchmark results running on Android Emulator.
Benchmark results running on Android Emulator.



💬 Share this post in social media

Thanks for checking this out and I hope you found the info useful! If you have any questions, don't hesitate to write me a comment below. And remember that if you like to see more content on, just let me know it and share this post with your colleges, co-workers, FFF, etc.

Please, don't try to hack this website servers. Guess why...