Gyu-Ho Lee
2015-07-30 22:11:49 UTC
Hello,
This great talk (*Kevin Cantwell - What Could Go Wrong?*
-
taught me to be more careful when I am updating map within range.
Here's an example to show non-deterministic behavior of map operations when
I update or delete inside for-loop range.
Try this Go PlayGround:
http://play.golang.org/p/1vMgYhs_3B
func nonDeterministicMapUpdateV1() {
mmap := map[string]int{
"hello": 10,
"world": 50,
"here": 5,
"go": 7,
"code": 11,
}
length := len(mmap)
for i := 0; i < 10; i++ {
fmt.Println("nonDeterministicMapUpdateV1 TRY =", i)
for k, v := range mmap {
mmap[strings.ToUpper(k)] = v * v
delete(mmap, k)
}
if length == len(mmap) {
fmt.Println("Luckily, Deterministic with nonDeterministicMapUpdateV1:",
length, len(mmap))
return
}
}
fmt.Println("Non-Deterministic with nonDeterministicMapUpdateV1:", length,
len(mmap))
}
func nonDeterministicMapUpdateV2() {
mmap := map[string]int{
"hello": 10,
"world": 50,
"here": 5,
"go": 7,
"code": 11,
}
length := len(mmap)
for i := 0; i < 10; i++ {
fmt.Println("nonDeterministicMapUpdateV2 TRY =", i)
ks := []string{}
for k, v := range mmap {
mmap[strings.ToUpper(k)] = v * v
ks = append(ks, k)
}
for _, k := range ks {
delete(mmap, k)
}
if length == len(mmap) {
fmt.Println("Luckily, Deterministic with nonDeterministicMapUpdateV2:",
length, len(mmap))
return
}
}
fmt.Println("Non-Deterministic with nonDeterministicMapUpdateV2:", length,
len(mmap))
}
func deterministicMapSet() {
mmap := make(map[int]bool)
for i := 0; i < 10000; i++ {
mmap[i] = true
}
length := len(mmap)
for i := 0; i < 10000; i++ {
for k := range mmap {
delete(mmap, k)
}
if len(mmap) == 0 {
fmt.Println("Deterministic with deterministicMapSet:", length, len(mmap))
return
}
}
fmt.Println("Non-Deterministic with deterministicMapSet:", length,
len(mmap))
}
func deterministicMapDelete() {
mmap := map[string]int{
"hello": 10,
"world": 50,
"here": 5,
"go": 7,
"code": 11,
}
length := len(mmap)
for i := 0; i < 10000; i++ {
fmt.Println("deterministicMapDelete TRY =", i)
for k := range mmap {
delete(mmap, k)
}
if len(mmap) == 0 {
fmt.Println("Deterministic with deterministicMapDelete:", length, len(mmap))
return
}
}
fmt.Println("Non-Deterministic with deterministicMapDelete:", length,
len(mmap))
}
func deterministicMapUpdate() {
mmap := map[string]int{
"hello": 10,
"world": 50,
"here": 5,
"go": 7,
"code": 11,
}
mmapCopy := make(map[string]int)
length := len(mmap)
for i := 0; i < 10000; i++ {
fmt.Println("deterministicMapUpdate TRY =", i)
for k, v := range mmap {
mmapCopy[strings.ToUpper(k)] = v * v
}
for k := range mmap {
delete(mmap, k)
}
if length == len(mmapCopy) {
fmt.Println("Deterministic with deterministicMapUpdate:", length,
len(mmapCopy))
return
} else {
mmapCopy = make(map[string]int) // to initialize(empty)
//
// (X)
// mmapCopy = nil
}
}
fmt.Println("Non-Deterministic with deterministicMapUpdate:", length,
len(mmap))
}
So I learned that I need to make a separate copy of a map if I want to
update the map with 'range'.
But I still do not understand why. Can anybody explain or point me to
related documentation?
The speaker mentioned Go Spec
(http://golang.org/ref/spec#Deletion_of_map_elements) but I couldn't find
it.
*Go FAQ *(http://golang.org/doc/faq#atomic_maps) instead explains:
Why are map operations not defined to be atomic?
above?
I do not use any goroutines in my code.
Thanks in advance!
This great talk (*Kevin Cantwell - What Could Go Wrong?*
-
taught me to be more careful when I am updating map within range.
Here's an example to show non-deterministic behavior of map operations when
I update or delete inside for-loop range.
Try this Go PlayGround:
http://play.golang.org/p/1vMgYhs_3B
func nonDeterministicMapUpdateV1() {
mmap := map[string]int{
"hello": 10,
"world": 50,
"here": 5,
"go": 7,
"code": 11,
}
length := len(mmap)
for i := 0; i < 10; i++ {
fmt.Println("nonDeterministicMapUpdateV1 TRY =", i)
for k, v := range mmap {
mmap[strings.ToUpper(k)] = v * v
delete(mmap, k)
}
if length == len(mmap) {
fmt.Println("Luckily, Deterministic with nonDeterministicMapUpdateV1:",
length, len(mmap))
return
}
}
fmt.Println("Non-Deterministic with nonDeterministicMapUpdateV1:", length,
len(mmap))
}
func nonDeterministicMapUpdateV2() {
mmap := map[string]int{
"hello": 10,
"world": 50,
"here": 5,
"go": 7,
"code": 11,
}
length := len(mmap)
for i := 0; i < 10; i++ {
fmt.Println("nonDeterministicMapUpdateV2 TRY =", i)
ks := []string{}
for k, v := range mmap {
mmap[strings.ToUpper(k)] = v * v
ks = append(ks, k)
}
for _, k := range ks {
delete(mmap, k)
}
if length == len(mmap) {
fmt.Println("Luckily, Deterministic with nonDeterministicMapUpdateV2:",
length, len(mmap))
return
}
}
fmt.Println("Non-Deterministic with nonDeterministicMapUpdateV2:", length,
len(mmap))
}
func deterministicMapSet() {
mmap := make(map[int]bool)
for i := 0; i < 10000; i++ {
mmap[i] = true
}
length := len(mmap)
for i := 0; i < 10000; i++ {
for k := range mmap {
delete(mmap, k)
}
if len(mmap) == 0 {
fmt.Println("Deterministic with deterministicMapSet:", length, len(mmap))
return
}
}
fmt.Println("Non-Deterministic with deterministicMapSet:", length,
len(mmap))
}
func deterministicMapDelete() {
mmap := map[string]int{
"hello": 10,
"world": 50,
"here": 5,
"go": 7,
"code": 11,
}
length := len(mmap)
for i := 0; i < 10000; i++ {
fmt.Println("deterministicMapDelete TRY =", i)
for k := range mmap {
delete(mmap, k)
}
if len(mmap) == 0 {
fmt.Println("Deterministic with deterministicMapDelete:", length, len(mmap))
return
}
}
fmt.Println("Non-Deterministic with deterministicMapDelete:", length,
len(mmap))
}
func deterministicMapUpdate() {
mmap := map[string]int{
"hello": 10,
"world": 50,
"here": 5,
"go": 7,
"code": 11,
}
mmapCopy := make(map[string]int)
length := len(mmap)
for i := 0; i < 10000; i++ {
fmt.Println("deterministicMapUpdate TRY =", i)
for k, v := range mmap {
mmapCopy[strings.ToUpper(k)] = v * v
}
for k := range mmap {
delete(mmap, k)
}
if length == len(mmapCopy) {
fmt.Println("Deterministic with deterministicMapUpdate:", length,
len(mmapCopy))
return
} else {
mmapCopy = make(map[string]int) // to initialize(empty)
//
// (X)
// mmapCopy = nil
}
}
fmt.Println("Non-Deterministic with deterministicMapUpdate:", length,
len(mmap))
}
So I learned that I need to make a separate copy of a map if I want to
update the map with 'range'.
But I still do not understand why. Can anybody explain or point me to
related documentation?
The speaker mentioned Go Spec
(http://golang.org/ref/spec#Deletion_of_map_elements) but I couldn't find
it.
*Go FAQ *(http://golang.org/doc/faq#atomic_maps) instead explains:
Why are map operations not defined to be atomic?
After long discussion it was decided that the typical use of maps did not
require safe access from multiple goroutines, and in those cases where it
did, the map was probably part of some larger data structure or computation
that was already synchronized. Therefore requiring that all map operations
grab a mutex would slow down most programs and add safety to few. This was
not an easy decision, however, since it means uncontrolled map access can
crash the program.
The language does not preclude atomic map updates. When required, such as
when hosting an untrusted program, the implementation could interlock map
access.
But how do I interpret this to explain the non-deterministic behaviorsrequire safe access from multiple goroutines, and in those cases where it
did, the map was probably part of some larger data structure or computation
that was already synchronized. Therefore requiring that all map operations
grab a mutex would slow down most programs and add safety to few. This was
not an easy decision, however, since it means uncontrolled map access can
crash the program.
The language does not preclude atomic map updates. When required, such as
when hosting an untrusted program, the implementation could interlock map
access.
above?
I do not use any goroutines in my code.
Thanks in advance!
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.