Which approach is better, and does it matter?


(Jos) #1

I’m a beginner that studied 2 books about Go, and I’d say I know the coding a bit. But something that I still struggle with (also from a confidence standpoint) is choosing amongst different options.

Say for instance I want to reverse a string. I found/wrote at least 3 ways to do that today:

func reverseString(s string) string {
	runes := []rune(s)
	for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
		runes[i], runes[j] = runes[j], runes[i]
	}
	return string(runes)
}
func reverseString2(s string) string {
	r := []rune(s)
	for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
		r[i], r[j] = r[j], r[i]
	}
	return string(r)
}
func reverseString3(s string) string {
	size := len(s)
	buf := make([]byte, size)
	for start := 0; start < size; {
		r, n := utf8.DecodeRuneInString(s[start:])
		start += n
		utf8.EncodeRune(buf[size-start:], r)
	}
	return string(buf)
}

My questions:

  • How do I know or can tell which of these approaches is ‘the best’?
  • Does it matter which approach I use? (Given how performance-oriented Go is, I’d say yes.)
  • And from a broader perspective: how to deal with these ambiguous situations as a beginner?

(Yamil Bracho) #2

First you have to determine how to measure your options. Speed ? Memory consumption ? ease of programming ?

HTH,
Yamil


(Bogdan Dinu) #3

The best approach would be the one in which you don’t convert from string to []byte and back, meaning reverseString3.


(Jos) #4

Good question. But I don’t really know. I don’t expect the speed of the three options to be very different. The same for the memory. So I’d suspect that ease of programming outweighs those other factors?

Ah I didn’t knew that. Thanks. Are the []rune arrays (and their operations) from the first two options are much more expensive than calling two additional methods? (DecodeRuneInString(), EncodeRune()).


(Jos) #5

Just made my first benchmark test in trying to figure it out. :slight_smile:

These are the results on my pc:

C:\Go practice\strings\stringtest>go test -bench=. -benchmem
goos: windows
goarch: amd64
BenchmarkReverseString-4         2000000               967 ns/op             208 B/op          2 allocs/op
BenchmarkReverseString2-4        2000000               987 ns/op             208 B/op          2 allocs/op
BenchmarkReverseString3-4        2000000               731 ns/op              96 B/op          2 allocs/op
PASS
ok      _/C_/Go_practice/strings/stringtest   8.121s

The third option is indeed quicker as @badu already knew. :slight_smile: The first and second option are the same.

So from a performance perspective the third option is not good. But from a code readability I think this option is most difficult. So the ease of programming that Yamil pointed out is less good.


(Bogdan Dinu) #6

Indeed, you should prefer readability over performance, until you are being asked to improve it. However, when being asked to improve performance, one should know which is the pedal-to-the-metal :slight_smile:


(Jos) #7

Thanks for your help and insights. :slight_smile:

Have a nice day!


(Grigory Dryapak) #8

Please be aware that the third function may panic if the string contains an invalid UTF-8 byte sequence, e.g.:

reverseString3("\x80")

https://play.golang.org/p/h5D0kaOZ6w_E


(Jos) #9

Thank you, that’s a very good catch. Much appreciated that you took the time to comment. :slight_smile: