PID hash table entries: 4096 (order: 12, 32768 bytes)? - linux

Reading a server boot sequence of a redhat server 5.8, i saw this and for me is very unclear, maybe i wrong, but i know the linux kernel body allocator uses a power of two mechanism for allocate and dellocate the system memory,
From boot messagges:
PID hash table entries: 4096 (order: 12, 32768 bytes)
Console: colour VGA+ 80x25
Dentry cache hash table entries: 33554432 (order: 16, 268435456 bytes)
Inode-cache hash table entries: 16777216 (order: 15, 134217728 bytes)
Order in power of two
python -c 'import math ; print int(math.pow(2,12))*4096'
16777216
python -c 'import math ; print int(math.pow(2,16))*4096'
268435456
python -c 'import math ; print int(math.pow(2,15))*4096'
134217728
So, my question is, Why the first line "PID hash table entrie" isn't 16777216 bytes?

PID hash table entries allocated as 2^N struct hlist_heads, which on a 64bit system are 8 bytes each. 2^12*8 = 32768.
Inode/Dentry caches are allocated as 2^N pages, usually 4096 bytes each. 2^15*4096 = 134217728.
This info is available in the source, kernel/pid.c and fs/inode.c respectively.

Related

u-boot gives Error 22 for ubi partition, but mounts ok in linux

I have a buildroot system, which mounts ubi ok in linux, but in u-boot I get error 22
When starting in linux this is in dmesg:
ubi0: scanning is finished
ubi0: attached mtd2 (name "rootfs", size 32 MiB)
ubi0: PEB size: 131072 bytes (128 KiB), LEB size: 126976 bytes
ubi0: min./max. I/O unit sizes: 2048/2048, sub-page size 2048
ubi0: VID header offset: 2048 (aligned 2048), data offset: 4096
ubi0: good PEBs: 256, bad PEBs: 0, corrupted PEBs: 0
ubi0: user volume: 1, internal volumes: 1, max. volumes count: 128
ubi0: max/mean erase counter: 2/0, WL threshold: 4096, image sequence number: 894512245
ubi0: available PEBs: 0, total reserved PEBs: 256, PEBs reserved for bad PEB handling: 40
ubi0: background thread "ubi_bgt0d" started, PID 1103
--
UBIFS (ubi0:0): UBIFS: mounted UBI device 0, volume 0, name "rootfs", R/O mode
UBIFS (ubi0:0): LEB size: 126976 bytes (124 KiB), min./max. I/O unit sizes: 2048 bytes/2048 bytes
UBIFS (ubi0:0): FS size: 25649152 bytes (24 MiB, 202 LEBs), journal size 4444160 bytes (4 MiB, 35 LEBs)
UBIFS (ubi0:0): reserved for root: 0 bytes (0 KiB)
UBIFS (ubi0:0): media format: w4/r0 (latest is w4/r0), UUID 29B5D4CF-8B0B-465A-8D03-F3A464E6250E, small LPT model
UBIFS (ubi0:0): full atime support is enabled.
VFS: Mounted root (ubifs filesystem) readonly on device 0:13.
in u-boot mtd returns:
device nand0 <nand0>, # parts = 4
#: name size offset mask_flags
0: u-boot 0x00200000 0x00000000 0
1: kernel 0x01e00000 0x00200000 0
2: rootfs 0x02000000 0x02000000 0
3: user 0x0c000000 0x04000000 0
active partition: nand0,0 - (u-boot) 0x00200000 # 0x00000000
defaults:
mtdids : nand0=nand0
mtdparts: mtdparts=nand0:0x200000#0x0(u-boot),0x1e00000#0x200000(kernel),0x2000000#0x2000000(rootfs),-(user)
but when it try to attach:
=> ubi part rootfs
ubi0: attaching mtd1
UBI init error 22
It's on an embedded system which uses older versions U-Boot 2016.11 and Linux/arm 4.4.289 Kernel
I suppose some parameter is wrong somewhere, can somebody give me some advise where to look?

Extend rootfs partition at runtime

I have an embedded Linux board with 16Go eMMC flash.
When I boot the image and I run fdisk -l I get this:
root#menzu:~# fdisk -l
Disk /dev/mmcblk2: 14.62 GiB, 15678308352 bytes, 30621696 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xe00e5569
Device Boot Start End Sectors Size Id Type
/dev/mmcblk2p1 * 16384 186775 170392 83.2M c W95 FAT32 (LBA)
/dev/mmcblk2p2 196608 9177991 8981384 4.3G 83 Linux
As you can see, my eMMC /dev/mmcblk2 has 14.62 Gb size.
But, my Linux rootfs partiton has only 4.3G,
How can I extend its size at runtime to be 10Gb or 12Gb for example?
I tried resize2fs /dev/mmcblk2p2 but it changed the blocks size to 1K and after that it only shows:
root#menzu:~# resize2fs /dev/mmcblk2p2
resize2fs 1.45.3 (14-Jul-2019)
The filesystem is already 1122673 (4k) blocks long. Nothing to do!
I can force the Yocto build to be 12Go, but that's not a good solution cuz the image will be large.
You need to change the size of your partition with fdisk before using resize2fs: delete the current partition and create a new one that starts at the same block as the one you deleted.
For example:
$ fdisk /dev/mmcblk1
Welcome to fdisk (util-linux 2.34).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Command (m for help): p
Disk /dev/mmcblk1: 27.86 GiB, 29896998912 bytes, 58392576 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x35a60061
Device Boot Start End Sectors Size Id Type
/dev/mmcblk1p1 * 16384 186775 170392 83.2M c W95 FAT32 (LBA)
/dev/mmcblk1p2 196608 7071881 6875274 3.3G 83 Linux
Command (m for help): d
Partition number (1,2, default 2): 2
Partition 2 has been deleted.
Command (m for help): p
Disk /dev/mmcblk1: 27.86 GiB, 29896998912 bytes, 58392576 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x35a60061
Device Boot Start End Sectors Size Id Type
/dev/mmcblk1p1 * 16384 186775 170392 83.2M c W95 FAT32 (LBA)
Command (m for help): n
Partition type
p primary (1 primary, 0 extended, 3 free)
e extended (container for logical partitions)
Select (default p): p
Partition number (2-4, default 2): 2
First sector (2048-58392575, default 2048): 196608
Last sector, +/-sectors or +/-size{K,M,G,T,P} (196608-58392575, default 58392575):
Created a new partition 2 of type 'Linux' and of size 27.8 GiB.
Partition #2 contains a ext4 signature.
Do you want to remove the signature? [Y]es/[N]o: N
Command (m for help): p
Disk /dev/mmcblk1: 27.86 GiB, 29896998912 bytes, 58392576 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x35a60061
Device Boot Start End Sectors Size Id Type
/dev/mmcblk1p1 * 16384 186775 170392 83.2M c W95 FAT32 (LBA)
/dev/mmcblk1p2 196608 58392575 58195968 27.8G 83 Linux
Command (m for help): w
The partition table has been altered.
Syncing disks.
After that you need to reboot and run resize2fs.

Python3 open buffering argument looks strange

From the doc
buffering is an optional integer used to set the buffering policy.
Pass 0 to switch buffering off (only allowed in binary mode), 1 to
select line buffering (only usable in text mode), and an integer > 1
to indicate the size in bytes of a fixed-size chunk buffer. When no
buffering argument is given, the default buffering policy works as
follows:
Binary files are buffered in fixed-size chunks; the size of the buffer is chosen using a heuristic trying to determine the underlying
device’s “block size” and falling back on io.DEFAULT_BUFFER_SIZE. On
many systems, the buffer will typically be 4096 or 8192 bytes long.
“Interactive” text files (files for which isatty() returns True) use line buffering. Other text files use the policy described above
for binary files.
I open a file named test.log with text mode, and set the buffering to 16. So I think the chunk size is 16, and when I write 32 bytes string to the file. It will call write(syscall) twice. But acutally, it only call once.(test in Python 3.7.2 GCC 8.2.1 20181127 on Linux)
import os
try:
os.unlink('test.log')
except Exception:
pass
with open('test.log', 'a', buffering=16) as f:
for _ in range(10):
f.write('a' * 32)
Using strace -e write python3 test.py to trace syscall, and get following
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 320) = 320
What does the buffering means?
This answer is valid for CPython 3.7 other implementations of Python can differ.
The open() function in text mode returns _io.TextIOWrapper(). The _io.TextIOWrapper() has internal 'buffer' called pending_bytes with size of 8192 bytes (it is hard coded) and it also have handle on _io.BufferedWriter() for text mode w or _io.BufferedRandom() for text mode a. The size of _io.BufferedWriter()/_io.BufferedRandom() is specified by the argument buffering in the open() function.
When you call into _io.TextIOWrapper().write("some text") it will add the text into internal pending_bytes buffer. After some writes you will fill the pending_bytes buffer and then it will be written into buffer inside _io.BufferedWriter(). When you fill up also the buffer inside _io.BufferedWriter() then it will be written into target file.
When you open file in binary mode you will get directly the _io.BufferedWriter()/_io.BufferedRandom() object initialized with buffer size from buffering parametr.
Let's look at some examples. I will start with simpler one using binary mode.
# Case 1
with open('test.log', 'wb', buffering=16) as f:
for _ in range(5):
f.write(b'a'*15)
strace output:
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
In the first iteration it fill buffer with 15 bytes. In the second iteration it discovers that adding another 15 bytes would overflow the buffer so it first flush it (calls system write) and then save those new 15 bytes. In next iteration the same happens again. After last iteration in the buffer is 15 B which are written on close of the file (leaving the with context).
The second case, I will try write into buffer more data than the buffer's size:
# Case 2
with open('test.log', 'wb', buffering=16) as f:
for _ in range(5):
f.write(b'a'*17)
strace output:
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
What happens here is that in the first iteration it will try write into buffer 17 B but it cannot fit there so it is directly written into the file and buffer stays empty. This applies for every iteration.
Now let's look at the text mode.
# Case 3
with open('test.log', 'w', buffering=16) as f:
for _ in range(5):
f.write('a'*8192)
strace output:
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 8192) = 8192
First recall that pending_bytes has size 8192 B. In the first iteration it writes 8192 bytes (from code: 'a'*8192) into pending_bytes buffer. In the second iteration it adds to the pending_buffer another 8192 bytes and discovers it is more than 8192 (size of pending_bytes buffer) and writes it into underlying _io.BufferedWriter(). The buffer in _io.BufferedWriter() has size 16 B (buffering parameter) so it will immediately writes into file (same as case 2). Now the pending_buffer is empty and in the third iteration it's again filled with 8192 B. In the fourth iteration it adds another 8192 B pending_bytes buffer overflows and it again written directly into file as in the second iteration. In the last iteration it adds 8192 B into pending_bytes buffer which is flushed when the files is closed.
Last example contains buffering bigger than 8192 B. Also for better explanation I added 2 more iterations.
# Case 4
with open('test.log', 'w', buffering=30000) as f:
for _ in range(7):
f.write('a'*8192)
strace output:
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 24576) = 24576
Iterations:
Add 8192 B into pending_bytes.
Add 8192 B into pending_bytes but it is more than maximal size so it is written into underlying _io.BufferedWritter() and it stays there (pending_bytes is empty now).
Add 8192 B into pending_bytes.
Add 8192 B into pending_bytes but it is more than maximal size so it tries to write into into underlying _io.BufferedWritter(). But it would exceed maximal capacity of the underlying buffer cause 16384 + 16384 > 30000 (first 16384 B are still there from iteration 2) so it first writes the old 16384 B into file and then puts those new 16384 B (from pending_bytes) into buffer. (Now again the pending_bytes buffer is empty)
Same as 3
Same as 4
Currently pending_buffer is empty and _io.BufferedWritter() contains 16384 B. In this iteration it fills pending_buffer with 8192 B. And that's it.
When the program leave with section it close the file. The process of closing follows:
Writes 8192 B from pending_buffer into _io.BufferedWriter() (it is possible cause 8192 + 16384 < 30000)
Writes (8192 + 16384=) 24576 B into file.
Close the file descriptor.
Btw currently I have no idea why is there that pending_buffer when it can use for buffering the underlying buffer from _io.BufferedWritter(). My best guess is it's there because it improve performance with files working in text mode.

Linux SLUB: Unable to allocate memory on node

We are getting very frequently below message in /var/log/messages
kernel: SLUB: Unable to allocate memory on node -1 (gfp=0x8020)
In some cases followed by an allocation table
kernel: cache: sigqueue(12019:454c4ebd186d964699132181ad7367c669700f7d8991c47d4bc053ed101675bc), object size: 160, buffer size: 160, default order: 0, min order: 0
kernel: node 0: slabs: 57, objs: 23313, free: 0
kernel: node 1: slabs: 35, objs: 14315, free: 0
Ok, free is 0, but how may this be tuned?
Following is set information
OS - Centos7.3
Kernel - 3.10.0-327.36.3.el7.x86_64
Docker - 1.12.6
Kubernetes - 1.5.5
We have private cloud powered by kurbernetes, having 10 nodes; it was working fine till last month and now we are getting these alerts very frequently on every nodes, pods/container also increased in last few days.
We have enough memory and cpu available on each node.
Any fine tuning for these alert will be very helpful.
Additional information:
sysctl.conf options
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_max_syn_backlog = 4096
net.core.somaxconn = 1024
net.ipv4.tcp_syncookies = 1
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.rmem_default = 65535
net.core.wmem_default = 65535
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.ipv4.ip_local_port_range = 1024 65535
vm.max_map_count = 262144
vm.swappiness=10
vm.vfs_cache_pressure=100
Please look at this: https://pingcap.com/blog/try-to-fix-two-linux-kernel-bugs-while-testing-tidb-operator-in-k8s/. It's a kernel bug.
problems seems to be with kernel, first a fall check whether swap memory is properly allocated or not by free -m and mkswap -c, if swap is not properly allocated, do it. if swap is fine, then you might need to update the kernel.

Memory overhead of maps in Go

map[byte]byte{0:10} should be using least 2 bytes, one for value and one per key. But as each hashmap implmentation, there is also a hidden cost per item.
What is the memory overhead per entry in Go maps in both gccgo and gc?
Here's a cross-platform reimplementation of Nick's program. It includes changes where I think it was flawed. It also adds more measured data points.
Note: To allow for a wider "entries" range, the measured map bellow is map[int16]byte.
package main
import (
"fmt"
"runtime"
"unsafe"
)
func Alloc() uint64 {
var stats runtime.MemStats
runtime.GC()
runtime.ReadMemStats(&stats)
return stats.Alloc - uint64(unsafe.Sizeof(hs[0]))*uint64(cap(hs))
}
var hs = []*map[int16]byte{}
func main() {
hs := []*map[int16]byte{}
n := 1000
before := Alloc()
for i := 0; i < n; i++ {
h := map[int16]byte{}
hs = append(hs, &h)
}
after := Alloc()
emptyPerMap := float64(after-before) / float64(n)
fmt.Printf("Bytes used for %d empty maps: %d, bytes/map %.1f\n", n, after-before, emptyPerMap)
hs = nil
k := 1
for p := 1; p < 16; p++ {
before = Alloc()
for i := 0; i < n; i++ {
h := map[int16]byte{}
for j := 0; j < k; j++ {
h[int16(j)] = byte(j)
}
hs = append(hs, &h)
}
after = Alloc()
fullPerMap := float64(after-before) / float64(n)
fmt.Printf("Bytes used for %d maps with %d entries: %d, bytes/map %.1f\n", n, k, after-before, fullPerMap)
fmt.Printf("Bytes per entry %.1f\n", (fullPerMap-emptyPerMap)/float64(k))
k *= 2
}
}
Output
jnml#fsc-r630:~/src/tmp$ go build && ./tmp && go version && uname -a
Bytes used for 1000 empty maps: 146816, bytes/map 146.8
Bytes used for 1000 maps with 1 entries: 147040, bytes/map 147.0
Bytes per entry 0.2
Bytes used for 1000 maps with 2 entries: 147040, bytes/map 147.0
Bytes per entry 0.1
Bytes used for 1000 maps with 4 entries: 247136, bytes/map 247.1
Bytes per entry 25.1
Bytes used for 1000 maps with 8 entries: 439056, bytes/map 439.1
Bytes per entry 36.5
Bytes used for 1000 maps with 16 entries: 818688, bytes/map 818.7
Bytes per entry 42.0
Bytes used for 1000 maps with 32 entries: 1194688, bytes/map 1194.7
Bytes per entry 32.7
Bytes used for 1000 maps with 64 entries: 2102976, bytes/map 2103.0
Bytes per entry 30.6
Bytes used for 1000 maps with 128 entries: 4155072, bytes/map 4155.1
Bytes per entry 31.3
Bytes used for 1000 maps with 256 entries: 6698688, bytes/map 6698.7
Bytes per entry 25.6
Bytes used for 1000 maps with 512 entries: 14142976, bytes/map 14143.0
Bytes per entry 27.3
Bytes used for 1000 maps with 1024 entries: 51349184, bytes/map 51349.2
Bytes per entry 50.0
Bytes used for 1000 maps with 2048 entries: 102467264, bytes/map 102467.3
Bytes per entry 50.0
Bytes used for 1000 maps with 4096 entries: 157214816, bytes/map 157214.8
Bytes per entry 38.3
Bytes used for 1000 maps with 8192 entries: 407031200, bytes/map 407031.2
Bytes per entry 49.7
Bytes used for 1000 maps with 16384 entries: 782616864, bytes/map 782616.9
Bytes per entry 47.8
go version devel +83b0b94af636 Sat Mar 09 16:25:30 2013 +1100 linux/amd64
Linux fsc-r630 3.2.0-38-generic #61-Ubuntu SMP Tue Feb 19 12:18:21 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
jnml#fsc-r630:~/src/tmp$
It's nice to see the numbers are better (by a factor of about 4x). The numbers for the release version (1.0.3) are only slightly higher:
jnml#fsc-r630:~/src/tmp$ go build && ./tmp
Bytes used for 1000 empty maps: 144192, bytes/map 144.2
Bytes used for 1000 maps with 1 entries: 144192, bytes/map 144.2
Bytes per entry 0.0
Bytes used for 1000 maps with 2 entries: 144192, bytes/map 144.2
Bytes per entry 0.0
Bytes used for 1000 maps with 4 entries: 315648, bytes/map 315.6
Bytes per entry 42.9
Bytes used for 1000 maps with 8 entries: 436288, bytes/map 436.3
Bytes per entry 36.5
Bytes used for 1000 maps with 16 entries: 885824, bytes/map 885.8
Bytes per entry 46.4
Bytes used for 1000 maps with 32 entries: 1331264, bytes/map 1331.3
Bytes per entry 37.1
Bytes used for 1000 maps with 64 entries: 2292800, bytes/map 2292.8
Bytes per entry 33.6
Bytes used for 1000 maps with 128 entries: 4935920, bytes/map 4935.9
Bytes per entry 37.4
Bytes used for 1000 maps with 256 entries: 12164160, bytes/map 12164.2
Bytes per entry 47.0
Bytes used for 1000 maps with 512 entries: 29887808, bytes/map 29887.8
Bytes per entry 58.1
Bytes used for 1000 maps with 1024 entries: 56840768, bytes/map 56840.8
Bytes per entry 55.4
Bytes used for 1000 maps with 2048 entries: 108736064, bytes/map 108736.1
Bytes per entry 53.0
Bytes used for 1000 maps with 4096 entries: 184368752, bytes/map 184368.8
Bytes per entry 45.0
Bytes used for 1000 maps with 8192 entries: 431340576, bytes/map 431340.6
Bytes per entry 52.6
Bytes used for 1000 maps with 16384 entries: 815378816, bytes/map 815378.8
Bytes per entry 49.8
jnml#fsc-r630:~/src/tmp$
Overhead per map entry is not a constant value, since it depends on a number of buckets per map entry.
There is a great article on the map internals: https://www.ardanlabs.com/blog/2013/12/macro-view-of-map-internals-in-go.html
The hash table for a Go map is structured as an array of buckets. The number of buckets is always equal to a power of 2.
...
How Maps Grow
As we continue to add or remove key/value pairs from the map, the efficiency of the map lookups begin to deteriorate. The load threshold values that determine when to grow the hash table are based on these four factors:
% overflow : Percentage of buckets which have an overflow bucket
bytes/entry : Number of overhead bytes used per key/value pair
hitprobe : Number of entries that need to be checked when looking up a key
missprobe : Number of entries that need to be checked when looking up an absent key
For example a very simple benchmark can show a dramatic increase in overhead per entry when increasing number of entries just by 1:
func Benchmark(b *testing.B) {
m := make(map[int64]struct{})
// also resets mem stats
b.ResetTimer()
for i := 0; i < b.N; i++ {
m[int64(i)] = struct{}{}
}
}
Benching with 106496 entries:
go test -bench . -benchtime 106496x -benchmem
Benchmark-2 106495 65.7 ns/op 31 B/op 0 allocs/op
e.g. 31 bytes per entry
Now increase the number of entries by one:
go test -bench . -benchtime 106497x -benchmem
Benchmark-2 106497 65.7 ns/op 57 B/op 0 allocs/op
e.g. 57 bytes per entry
Increasing the number of entries by 1 resulted in the doubling of the number of underlying buckets, which resulted in an additional overhead. The overhead will decrease when more entries are added, until the number of buckets is doubled again.
It seems like there is a buffer involved, and it grows only when needed. I can't tell for gccgo, though, I just tried it on the playground. Basically, it allocates 128 bytes for the empty map, then it grows when needed.
You can see it here : http://play.golang.org/p/RjohbSOq0x
Here is an experiment to measure the overhead of maps. Works under Linux only.
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"runtime"
"strconv"
"strings"
)
func ReadRss() int {
data, err := ioutil.ReadFile("/proc/self/statm")
if err != nil {
log.Fatal(err)
}
rss, err := strconv.Atoi(strings.Fields(string(data))[1])
if err != nil {
log.Fatal(err)
}
return rss * os.Getpagesize()
}
func main() {
hs := []*map[byte]byte{}
before := ReadRss()
n := 10000
for i := 0; i < n; i++ {
h := map[byte]byte{}
hs = append(hs, &h)
}
after := ReadRss()
empty_per_map := float64(after-before)/float64(n)
fmt.Printf("Bytes used for %d empty maps: %d, bytes/map %.1f\n", n, after-before, empty_per_map)
hs = nil
runtime.GC()
before = ReadRss()
for i := 0; i < n; i++ {
h := map[byte]byte{}
for j := byte(0); j < 100; j++ {
h[j] = j
}
hs = append(hs, &h)
}
after = ReadRss()
full_per_map := float64(after-before)/float64(n)
fmt.Printf("Bytes used for %d maps with 100 entries: %d, bytes/map %.1f\n", n, after-before, full_per_map)
fmt.Printf("Bytes per entry %.1f\n", (full_per_map - empty_per_map)/100)
}
It prints this on my 64 bit Linux machine using go 1.0.3
Bytes used for 10000 empty maps: 1695744, bytes/map 169.6
Bytes used for 10000 maps with 100 entries: 43876352, bytes/map 4387.6
Bytes per entry 42.2
Or using go 1.0
Bytes used for 10000 empty maps: 1388544, bytes/map 138.9
Bytes used for 10000 maps with 100 entries: 199323648, bytes/map 19932.4
Bytes per entry 197.9
The memory measurements are done using the Linux OS rather than Go's internal memory stats. The memory measurements are coarse as they are returned in 4k pages, hence the large number of maps created.
So in round numbers each map costs about 170 bytes and each entry costs 42 bytes using go 1.0.3 (much more for 1.0)
/* Hal3 Mon Jul 18 20:58:16 BST 2016 go version go1.5.1 linux/amd64
Bytes used for 1000 empty maps: 0, bytes/map 0.0
Bytes used for 1000 maps with 1 entries: 112192, bytes/map 112.2
Bytes per entry 112.2
Bytes used for 1000 maps with 2 entries: 113472, bytes/map 113.5
Bytes per entry 56.7
Bytes used for 1000 maps with 4 entries: 110912, bytes/map 110.9
Bytes per entry 27.7
Bytes used for 1000 maps with 8 entries: 112192, bytes/map 112.2
Bytes per entry 14.0
Bytes used for 1000 maps with 16 entries: 231600, bytes/map 231.6
Bytes per entry 14.5
Bytes used for 1000 maps with 32 entries: 413768, bytes/map 413.8
Bytes per entry 12.9
Bytes used for 1000 maps with 64 entries: 736920, bytes/map 736.9
Bytes per entry 11.5
Bytes used for 1000 maps with 128 entries: 1419624, bytes/map 1419.6
Bytes per entry 11.1
Bytes used for 1000 maps with 256 entries: 2735192, bytes/map 2735.2
Bytes per entry 10.7
Bytes used for 1000 maps with 512 entries: 5655168, bytes/map 5655.2
Bytes per entry 11.0
Bytes used for 1000 maps with 1024 entries: 10919888, bytes/map 10919.9
Bytes per entry 10.7
Bytes used for 1000 maps with 2048 entries: 21224528, bytes/map 21224.5
Bytes per entry 10.4
Bytes used for 1000 maps with 4096 entries: 42391024, bytes/map 42391.0
Bytes per entry 10.3
Bytes used for 1000 maps with 8192 entries: 84613344, bytes/map 84613.3
Bytes per entry 10.3
Bytes used for 1000 maps with 16384 entries: 169152560, bytes/map 169152.6
Bytes per entry 10.3
Mon Jul 18 20:58:25 BST 2016 */

Resources