Source File
memory.go
Belonging Package
modernc.org/memory
// Copyright 2017 The Memory Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.// Package memory implements a memory allocator.//// Build status//// available at https://modern-c.appspot.com/-/builder/?importpath=modernc.org%2fmemory//// Changelog//// 2017-10-03 Added alternative, unsafe.Pointer-based API.//// Package memory implements a memory allocator.//// Changelog//// 2017-10-03 Added alternative, unsafe.Pointer-based API.//// Benchmarks//// AMD Ryzen 9 3900X 12-Core Processor × 24//// jnml@3900x:~/src/modernc.org/memory$ date ; go version ; go test -run @ -bench . -benchmem |& tee log// Fri Nov 20 17:23:04 CET 2020// go version go1.15.5 linux/amd64// goos: linux// goarch: amd64// pkg: modernc.org/memory// BenchmarkFree16-24 141188362 8.26 ns/op 0 B/op 0 allocs/op// BenchmarkFree32-24 100000000 11.4 ns/op 0 B/op 0 allocs/op// BenchmarkFree64-24 67160647 18.3 ns/op 0 B/op 0 allocs/op// BenchmarkCalloc16-24 60612698 19.8 ns/op 0 B/op 0 allocs/op// BenchmarkCalloc32-24 47968105 23.8 ns/op 0 B/op 0 allocs/op// BenchmarkCalloc64-24 40752181 28.6 ns/op 0 B/op 0 allocs/op// BenchmarkGoCalloc16-24 66487354 17.8 ns/op 16 B/op 1 allocs/op// BenchmarkGoCalloc32-24 56009206 21.2 ns/op 32 B/op 1 allocs/op// BenchmarkGoCalloc64-24 52086571 23.4 ns/op 64 B/op 1 allocs/op// BenchmarkMalloc16-24 113943390 10.2 ns/op 0 B/op 0 allocs/op// BenchmarkMalloc32-24 113520471 10.2 ns/op 0 B/op 0 allocs/op// BenchmarkMalloc64-24 108787056 10.7 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrFree16-24 146110286 7.94 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrFree32-24 93052707 12.0 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrFree64-24 69805262 17.3 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrCalloc16-24 85282725 13.7 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrCalloc32-24 66489789 17.9 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrCalloc64-24 53561092 22.7 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrMalloc16-24 222978858 5.28 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrMalloc32-24 210443384 5.30 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrMalloc64-24 213706227 5.47 ns/op 0 B/op 0 allocs/op// PASS// ok modernc.org/memory 70.528s// jnml@3900x:~/src/modernc.org/memory$//// Intel® Core™ i5-4670 CPU @ 3.40GHz × 4//// ==== jnml@4670:~/src/modernc.org/memory> date ; go version ; go test -run @ -bench . -benchmem |& tee log// Sat Dec 8 12:56:53 CET 2018// go version go1.11.2 linux/amd64// goos: linux// goarch: amd64// pkg: modernc.org/memory// BenchmarkFree16-4 100000000 14.7 ns/op 0 B/op 0 allocs/op// BenchmarkFree32-4 100000000 20.5 ns/op 0 B/op 0 allocs/op// BenchmarkFree64-4 50000000 32.8 ns/op 0 B/op 0 allocs/op// BenchmarkCalloc16-4 50000000 24.4 ns/op 0 B/op 0 allocs/op// BenchmarkCalloc32-4 50000000 29.2 ns/op 0 B/op 0 allocs/op// BenchmarkCalloc64-4 50000000 35.7 ns/op 0 B/op 0 allocs/op// BenchmarkGoCalloc16-4 50000000 27.0 ns/op 16 B/op 1 allocs/op// BenchmarkGoCalloc32-4 50000000 27.3 ns/op 32 B/op 1 allocs/op// BenchmarkGoCalloc64-4 30000000 37.9 ns/op 64 B/op 1 allocs/op// BenchmarkMalloc16-4 100000000 12.9 ns/op 0 B/op 0 allocs/op// BenchmarkMalloc32-4 100000000 12.9 ns/op 0 B/op 0 allocs/op// BenchmarkMalloc64-4 100000000 13.2 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrFree16-4 100000000 12.0 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrFree32-4 100000000 17.5 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrFree64-4 50000000 28.9 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrCalloc16-4 100000000 17.8 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrCalloc32-4 100000000 22.9 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrCalloc64-4 50000000 29.6 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrMalloc16-4 200000000 7.31 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrMalloc32-4 200000000 7.47 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrMalloc64-4 200000000 7.68 ns/op 0 B/op 0 allocs/op// PASS// ok modernc.org/memory 73.859s// //// Intel® Xeon(R) CPU E5-1650 v2 @ 3.50GHz × 12//// ==== jnml@e5-1650:~/src/modernc.org/memory> date ; go version ; go test -run @ -bench . -benchmem// Fri Dec 7 14:18:50 CET 2018// go version go1.11.2 linux/amd64// goos: linux// goarch: amd64// pkg: modernc.org/memory// BenchmarkFree16-12 100000000 16.7 ns/op 0 B/op 0 allocs/op// BenchmarkFree32-12 50000000 25.0 ns/op 0 B/op 0 allocs/op// BenchmarkFree64-12 30000000 39.7 ns/op 0 B/op 0 allocs/op// BenchmarkCalloc16-12 50000000 26.3 ns/op 0 B/op 0 allocs/op// BenchmarkCalloc32-12 50000000 33.4 ns/op 0 B/op 0 allocs/op// BenchmarkCalloc64-12 30000000 38.3 ns/op 0 B/op 0 allocs/op// BenchmarkGoCalloc16-12 50000000 26.6 ns/op 16 B/op 1 allocs/op// BenchmarkGoCalloc32-12 50000000 26.8 ns/op 32 B/op 1 allocs/op// BenchmarkGoCalloc64-12 30000000 35.1 ns/op 64 B/op 1 allocs/op// BenchmarkMalloc16-12 100000000 13.5 ns/op 0 B/op 0 allocs/op// BenchmarkMalloc32-12 100000000 13.4 ns/op 0 B/op 0 allocs/op// BenchmarkMalloc64-12 100000000 14.1 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrFree16-12 100000000 14.4 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrFree32-12 100000000 21.7 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrFree64-12 50000000 36.7 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrCalloc16-12 100000000 20.4 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrCalloc32-12 50000000 27.1 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrCalloc64-12 50000000 33.4 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrMalloc16-12 200000000 8.02 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrMalloc32-12 200000000 8.28 ns/op 0 B/op 0 allocs/op// BenchmarkUintptrMalloc64-12 200000000 8.29 ns/op 0 B/op 0 allocs/op// PASS// ok modernc.org/memory 80.896spackage memory // import "modernc.org/memory"import ()const (headerSize = unsafe.Sizeof(page{})mallocAllign = 2 * unsafe.Sizeof(uintptr(0))maxSlotSize = 1 << maxSlotSizeLogmaxSlotSizeLog = pageSizeLog - 2pageAvail = pageSize - headerSizepageMask = pageSize - 1pageSize = 1 << pageSizeLog)func () {if unsafe.Sizeof(page{})%mallocAllign != 0 {panic("internal error")}}// if n%m != 0 { n += m-n%m }. m must be a power of 2.func (, int) int { return ( + - 1) &^ ( - 1) }type node struct {prev, next uintptr // *node}type page struct {brk intlog uintsize intused int}// Allocator allocates and frees memory. Its zero value is ready for use. The// exported counters are updated only when build tag memory.counters is// present.type Allocator struct {Allocs int // # of allocs.Bytes int // Asked from OS.cap [64]intlists [64]uintptr // *nodeMmaps int // Asked from OS.pages [64]uintptr // *pageregs map[uintptr]struct{} // map[*page]struct{}}func ( *Allocator) ( int) (uintptr /* *page */, error) {, , := mmap()if != nil {return 0,}if counters {.Mmaps++.Bytes +=}if .regs == nil {.regs = map[uintptr]struct{}{}}(*page)(unsafe.Pointer()).size =.regs[] = struct{}{}return , nil}func ( *Allocator) ( int) (uintptr /* *page */, error) {+= int(headerSize), := .mmap()if != nil {return 0,}(*page)(unsafe.Pointer()).log = 0return , nil}func ( *Allocator) ( uint) (uintptr /* *page */, error) {if .cap[] == 0 {.cap[] = int(pageAvail) / (1 << )}:= int(headerSize) + .cap[]<<, := .mmap()if != nil {return 0,}.pages[] =(*page)(unsafe.Pointer()).log =return , nil}func ( *Allocator) ( uintptr /* *page */) error {delete(.regs, )if counters {.Mmaps--}return unmap(, (*page)(unsafe.Pointer()).size)}// UintptrCalloc is like Calloc except it returns an uintptr.func ( *Allocator) ( int) ( uintptr, error) {if trace {defer func() {fmt.Fprintf(os.Stderr, "Calloc(%#x) %#x, %v\n", , , )}()}if , = .UintptrMalloc(); == 0 || != nil {return 0,}:= ((*rawmem)(unsafe.Pointer()))[::]for := range {[] = 0}return , nil}// UintptrFree is like Free except its argument is an uintptr, which must have// been acquired from UintptrCalloc or UintptrMalloc or UintptrRealloc.func ( *Allocator) ( uintptr) ( error) {if trace {defer func() {fmt.Fprintf(os.Stderr, "Free(%#x) %v\n", , )}()}if == 0 {return nil}if counters {.Allocs--}:= &^ uintptr(pageMask):= (*page)(unsafe.Pointer()).logif == 0 {if counters {.Bytes -= (*page)(unsafe.Pointer()).size}return .unmap()}(*node)(unsafe.Pointer()).prev = 0(*node)(unsafe.Pointer()).next = .lists[]if := (*node)(unsafe.Pointer()).next; != 0 {(*node)(unsafe.Pointer()).prev =}.lists[] =(*page)(unsafe.Pointer()).used--if (*page)(unsafe.Pointer()).used != 0 {return nil}for := 0; < (*page)(unsafe.Pointer()).brk; ++ {:= + headerSize + uintptr()<<:= (*node)(unsafe.Pointer()).next:= (*node)(unsafe.Pointer()).prevswitch {case == 0:.lists[] =if != 0 {(*node)(unsafe.Pointer()).prev = 0}case == 0:(*node)(unsafe.Pointer()).next = 0default:(*node)(unsafe.Pointer()).next =(*node)(unsafe.Pointer()).prev =}}if .pages[] == {.pages[] = 0}if counters {.Bytes -= (*page)(unsafe.Pointer()).size}return .unmap()}// UintptrMalloc is like Malloc except it returns an uinptr.func ( *Allocator) ( int) ( uintptr, error) {if trace {defer func() {fmt.Fprintf(os.Stderr, "Malloc(%#x) %#x, %v\n", , , )}()}if < 0 {panic("invalid malloc size")}if == 0 {return 0, nil}if counters {.Allocs++}:= uint(bits.Len(uint((+int(mallocAllign)-1)&^int(mallocAllign-1) - 1)))if > maxSlotSizeLog {, := .newPage()if != nil {return 0,}return + headerSize, nil}if .lists[] == 0 && .pages[] == 0 {if , := .newSharedPage(); != nil {return 0,}}if := .pages[]; != 0 {(*page)(unsafe.Pointer()).used++(*page)(unsafe.Pointer()).brk++if (*page)(unsafe.Pointer()).brk == .cap[] {.pages[] = 0}return + headerSize + uintptr((*page)(unsafe.Pointer()).brk-1)<<, nil}:= .lists[]:= &^ uintptr(pageMask).lists[] = (*node)(unsafe.Pointer()).nextif := (*node)(unsafe.Pointer()).next; != 0 {(*node)(unsafe.Pointer()).prev = 0}(*page)(unsafe.Pointer()).used++return , nil}// UintptrRealloc is like Realloc except its first argument is an uintptr,// which must have been returned from UintptrCalloc, UintptrMalloc or// UintptrRealloc.func ( *Allocator) ( uintptr, int) ( uintptr, error) {if trace {defer func() {fmt.Fprintf(os.Stderr, "UnsafeRealloc(%#x, %#x) %#x, %v\n", , , , )}()}switch {case == 0:return .UintptrMalloc()case == 0 && != 0:return 0, .UintptrFree()}:= UintptrUsableSize()if > {return , nil}if , = .UintptrMalloc(); != nil {return 0,}if < {=}copy((*rawmem)(unsafe.Pointer())[::], (*rawmem)(unsafe.Pointer())[::])return , .UintptrFree()}// UintptrUsableSize is like UsableSize except its argument is an uintptr,// which must have been returned from UintptrCalloc, UintptrMalloc or// UintptrRealloc.func ( uintptr) ( int) {if trace {defer func() {fmt.Fprintf(os.Stderr, "UsableSize(%#x) %#x\n", , )}()}if == 0 {return 0}return usableSize()}func ( uintptr) ( int) {:= &^ uintptr(pageMask)if := (*page)(unsafe.Pointer()).log; != 0 {return 1 <<}return (*page)(unsafe.Pointer()).size - int(headerSize)}// Calloc is like Malloc except the allocated memory is zeroed.func ( *Allocator) ( int) ( []byte, error) {, := .UintptrCalloc()if != nil {return nil,}var []byte:= (*reflect.SliceHeader)(unsafe.Pointer(&)).Cap = usableSize().Data =.Len =return , nil}// Close releases all OS resources used by a and sets it to its zero value.//// It's not necessary to Close the Allocator when exiting a process.func ( *Allocator) () ( error) {for := range .regs {if := .unmap(); != nil && == nil {=}}* = Allocator{}return}// Free deallocates memory (as in C.free). The argument of Free must have been// acquired from Calloc or Malloc or Realloc.func ( *Allocator) ( []byte) ( error) {if = [:cap()]; len() == 0 {return nil}return .UintptrFree(uintptr(unsafe.Pointer(&[0])))}// Malloc allocates size bytes and returns a byte slice of the allocated// memory. The memory is not initialized. Malloc panics for size < 0 and// returns (nil, nil) for zero size.//// It's ok to reslice the returned slice but the result of appending to it// cannot be passed to Free or Realloc as it may refer to a different backing// array afterwards.func ( *Allocator) ( int) ( []byte, error) {, := .UintptrMalloc()if == 0 || != nil {return nil,}:= (*reflect.SliceHeader)(unsafe.Pointer(&)).Cap = usableSize().Data =.Len =return , nil}// Realloc changes the size of the backing array of b to size bytes or returns// an error, if any. The contents will be unchanged in the range from the// start of the region up to the minimum of the old and new sizes. If the// new size is larger than the old size, the added memory will not be// initialized. If b's backing array is of zero size, then the call is// equivalent to Malloc(size), for all values of size; if size is equal to// zero, and b's backing array is not of zero size, then the call is equivalent// to Free(b). Unless b's backing array is of zero size, it must have been// returned by an earlier call to Malloc, Calloc or Realloc. If the area// pointed to was moved, a Free(b) is done.func ( *Allocator) ( []byte, int) ( []byte, error) {var uintptrif = [:cap()]; len() != 0 {= uintptr(unsafe.Pointer(&[0]))}if , = .UintptrRealloc(, ); == 0 || != nil {return nil,}:= (*reflect.SliceHeader)(unsafe.Pointer(&)).Cap = usableSize().Data =.Len =return , nil}// UsableSize reports the size of the memory block allocated at p, which must// point to the first byte of a slice returned from Calloc, Malloc or Realloc.// The allocated memory block size can be larger than the size originally// requested from Calloc, Malloc or Realloc.func ( *byte) ( int) { return UintptrUsableSize(uintptr(unsafe.Pointer())) }// UnsafeCalloc is like Calloc except it returns an unsafe.Pointer.func ( *Allocator) ( int) ( unsafe.Pointer, error) {, := .UintptrCalloc()if != nil {return nil,}return unsafe.Pointer(), nil}// UnsafeFree is like Free except its argument is an unsafe.Pointer, which must// have been acquired from UnsafeCalloc or UnsafeMalloc or UnsafeRealloc.func ( *Allocator) ( unsafe.Pointer) ( error) { return .UintptrFree(uintptr()) }// UnsafeMalloc is like Malloc except it returns an unsafe.Pointer.func ( *Allocator) ( int) ( unsafe.Pointer, error) {, := .UintptrMalloc()if != nil {return nil,}return unsafe.Pointer(), nil}// UnsafeRealloc is like Realloc except its first argument is an// unsafe.Pointer, which must have been returned from UnsafeCalloc,// UnsafeMalloc or UnsafeRealloc.func ( *Allocator) ( unsafe.Pointer, int) ( unsafe.Pointer, error) {, := .UintptrRealloc(uintptr(), )if != nil {return nil,}return unsafe.Pointer(), nil}// UnsafeUsableSize is like UsableSize except its argument is an// unsafe.Pointer, which must have been returned from UnsafeCalloc,// UnsafeMalloc or UnsafeRealloc.func ( unsafe.Pointer) ( int) { return UintptrUsableSize(uintptr()) }
![]() |
The pages are generated with Golds v0.3.6. (GOOS=darwin GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |