Back
Featured image of post Detecting a valid IPv4 in Go like a boss

Detecting a valid IPv4 in Go like a boss

A short analysis of different alternatives to check IPv4 validity comparing complexity vs performance of their implementations.

Table of Content

A short analysis of different alternatives to check IPv4 validity comparing complexity vs performance of their implementations.

In this post, we will analyze how Go can be used to squeeze computer performance at maximum, with a tiny example of how to parse and validate content. In this case, we will be using IPv4 detection methods in order to make different approaches, and to benchmark & compare how Go behaves in each of them. We will be testing:

  • net package.
  • regular expressions.
  • custom implementation.

All tests are executed with latest available version of Go at the time of writing this article which is go version go1.14.4 linux/amd64.

1
go version
1
go version go1.14.4 linux/amd64

Introducing different implementations

Method 1: net package

This method will use go net package to check if given string contains a valid IPv4 or not.

1
2
3
func IsIpv4Net(host string) bool {
   return net.ParseIP(host) != nil
}

Method 2: regex implementation

This method will use go regex package to check if given string contains a valid IPv4 or not. The selected regular expression for this purpose is:

1
^(([0–9]|[1–9][0–9]|1[0–9]{2}|2[0–4][0–9]|25[0–5])\.){3}([0–9]|[1–9][0–9]|1[0–9]{2}|2[0–4][0–9]|25[0–5])$

And this is how we use it:

1
2
3
4
5
6
7
8
var (
   ipRegex, _ = regexp.Compile(`^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$`)
)

func IsIpv4Regex(ipAddress string) bool {
   ipAddress = strings.Trim(ipAddress, " ")
   return ipRegex.MatchString(ipAddress)
}

Note: it is important to compile regex once, and call it multiple times.

Method 3: custom implementation

Our custom approach will be based on net.Parse method as implemented in standard Go package.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
func IsIpv4(s string) bool {
   for i := 0; i < IPv4len; i++ {
    if len(s) == 0 {
      // Missing octets.
      return false
    }
    if i > 0 {
      if s[0] != '.' {
        return false
      }
      s = s[1:]
    }
    var n int
    var i int
    for i = 0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
      n = n*10 + int(s[i]-'0')
      if n >= big {
        n = big
      }
    }
    if i == 0 {
      n = 0; i = 0
    }
    if n > 0xFF {
      return false
    }
    s = s[i:]
  }
  return len(s) == 0
}

Benchmarking our implementations

Let’s prepare a simple benchmark tests in where we receive IP value as string and we need to determine if it is a valid IPv4 or not just returning a boolean value with the result.

All test will have following configuration:

  • Measure allocations per operation
  • Measure ns per operation
  • Measure bytes per operation

All benchmarking test will be executed of course, with testing machine in idle state with no other workloads being executed at same time to avoid bias results.

Method 1 Benchmarking: net package

1
2
3
4
5
6
7
8
b.Run("is-valid-ipv4-net-pkg", func(b *testing.B) {
   b.ReportAllocs()
   b.SetBytes(1)
   b.ResetTimer()
   for n := 0; n < b.N; n++ {
      _ = IsIpv4Net("10.41.132.6")
   }
})
1
2
3
4
5
6
7
8
goos: linux
goarch: amd64
BenchmarkIpv4
BenchmarkIpv4/is-valid-ipv4-net-pkg
BenchmarkIpv4/is-valid-ipv4-net-pkg-4     14063838       81.1 ns/op	  12.33 MB/s      16 B/op       1 allocs/op
PASS

Process finished with exit code 0

Method 2 Benchmarking: regex package

1
2
3
4
5
6
7
8
b.Run("is-valid-ipv4-method-regex", func(b *testing.B) {
   b.ReportAllocs()
   b.SetBytes(1)
   b.ResetTimer()
   for n := 0; n < b.N; n++ {
      _ = IsIpv4Regex("10.41.132.6")
   }
})
1
2
3
4
5
6
7
8
goos: linux
goarch: amd64
BenchmarkIpv4
BenchmarkIpv4/is-valid-ipv4-method-regex
BenchmarkIpv4/is-valid-ipv4-method-regex-4     1899319       604 ns/op   1.65 MB/s       0 B/op     0 allocs/op
PASS

Process finished with exit code 0

Method 3 Benchmarking: custom method

1
2
3
4
5
6
7
8
b.Run("is-valid-ipv4-custom-method", func(b *testing.B) {
   b.ReportAllocs()
   b.SetBytes(1)
   b.ResetTimer()
   for n := 0; n < b.N; n++ {
      _ = IsIpv4("10.41.132.6")
   }
})
1
2
3
4
5
6
7
8
goos: linux
goarch: amd64
BenchmarkIpv4
BenchmarkIpv4/is-valid-ipv4-custom-method
BenchmarkIpv4/is-valid-ipv4-custom-method-4     	41939558	       29.6 ns/op	  33.78 MB/s	       0 B/op	       0 allocs/op
PASS

Process finished with exit code 0

Final Results

Once tested and benchmarking all different methods, and having solid results, let’s compare the performance gain between our different options. But first, lets take a look at final results!!

1
2
3
4
5
6
7
8
9
goos: linux
goarch: amd64
BenchmarkIpv4
is-valid-ipv4-net-pkg-4           14063838      81.1 ns/op  12.33 MB/s  16 B/op   1 allocs/op
is-valid-ipv4-method-regex-4       1899319       604 ns/op   1.65 MB/s   0 B/op   0 allocs/op
is-valid-ipv4-custom-method-4     41939558      29.6 ns/op  33.78 MB/s   0 B/op   0 allocs/op
PASS

Process finished with exit code 0

…and plotting results, we get:

Final result comparison
Final result comparison

You can check and run performance test by yourself. Get the code at Github

Conclusions

Obviously this optimizations was quite a tiny one, and it was used in order to show you the relationship between readable code and optimized code. Just take into account the huge impact that can have writing good code vs writing code when we scale our software. For a http server, this means that for each validation it will require to make one allocations while the optimized version is zero-alloc! And compared to regex version, the optimized code is x20 times faster. Thanks for checking this out and I hope you found the info useful! This is a basic introduction to how to optimize stuff for performance.



πŸ’¬ 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...