Permalink
Browse files

use forked ParseUint which doesn't require allocating a string

strconv.Parse[U]int requires a string argument.  Because we have a
byte slice, a conversion was required to provide a string (which
allocates a new piece of memory to store an immutable copy of our byte
slice).  This is somewhat silly - even though copying and pasting code
is 'bad', thousands of allocations are worse (6 allocations + memcpy
for each entry in each smaps file).
  • Loading branch information...
bpowers committed Jan 2, 2013
1 parent 33d7fe8 commit 77e0408f812b2e5056b4fe06be82654985b9e040
Showing with 18 additions and 81 deletions.
  1. +5 −68 atoi.go
  2. +13 −13 main.go
73 atoi.go
@@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package strconv
package main
import "errors"
import "strconv"
// ErrRange indicates that a value is out of range for the target type.
var ErrRange = errors.New("value out of range")
@@ -44,7 +45,7 @@ func cutoff64(base int) uint64 {
}
// ParseUint is like ParseInt but for unsigned numbers.
func ParseUint(s string, base int, bitSize int) (n uint64, err error) {
func ParseUint(s []byte, base int, bitSize int) (n uint64, err error) {
var cutoff, maxVal uint64
if bitSize == 0 {
@@ -77,7 +78,7 @@ func ParseUint(s string, base int, bitSize int) (n uint64, err error) {
}
default:
err = errors.New("invalid base " + Itoa(base))
err = errors.New("invalid base " + strconv.Itoa(base))
goto Error
}
@@ -127,69 +128,5 @@ func ParseUint(s string, base int, bitSize int) (n uint64, err error) {
return n, nil
Error:
return n, &NumError{"ParseUint", s0, err}
}
// ParseInt interprets a string s in the given base (2 to 36) and
// returns the corresponding value i. If base == 0, the base is
// implied by the string's prefix: base 16 for "0x", base 8 for
// "0", and base 10 otherwise.
//
// The bitSize argument specifies the integer type
// that the result must fit into. Bit sizes 0, 8, 16, 32, and 64
// correspond to int, int8, int16, int32, and int64.
//
// The errors that ParseInt returns have concrete type *NumError
// and include err.Num = s. If s is empty or contains invalid
// digits, err.Error = ErrSyntax; if the value corresponding
// to s cannot be represented by a signed integer of the
// given size, err.Error = ErrRange.
func ParseInt(s string, base int, bitSize int) (i int64, err error) {
const fnParseInt = "ParseInt"
if bitSize == 0 {
bitSize = int(IntSize)
}
// Empty string bad.
if len(s) == 0 {
return 0, syntaxError(fnParseInt, s)
}
// Pick off leading sign.
s0 := s
neg := false
if s[0] == '+' {
s = s[1:]
} else if s[0] == '-' {
neg = true
s = s[1:]
}
// Convert unsigned and check range.
var un uint64
un, err = ParseUint(s, base, bitSize)
if err != nil && err.(*NumError).Err != ErrRange {
err.(*NumError).Func = fnParseInt
err.(*NumError).Num = s0
return 0, err
}
cutoff := uint64(1 << uint(bitSize-1))
if !neg && un >= cutoff {
return int64(cutoff - 1), rangeError(fnParseInt, s0)
}
if neg && un > cutoff {
return -int64(cutoff), rangeError(fnParseInt, s0)
}
n := int64(un)
if neg {
n = -n
}
return n, nil
}
// Atoi is shorthand for ParseInt(s, 10, 0).
func Atoi(s string) (i int, err error) {
i64, err := ParseInt(s, 10, 0)
return int(i64), err
return n, &NumError{"ParseUint", string(s0), err}
}
26 main.go
@@ -2,7 +2,6 @@ package main
import (
"bufio"
"bytes"
"flag"
"fmt"
"io"
@@ -27,6 +26,7 @@ const (
// pss calculations
PssAdjust = .5
pageSize = 4096
mapDetailLen = len("Size: 4 kB")
usage = `Usage: %s [OPTION...]
Simple, accurate RAM and swap reporting.
@@ -46,13 +46,13 @@ type CmdMemInfo struct {
PIDs []int
Name string
Pss float64
Shared int64
Private int64
Swapped int64
Shared uint64
Private uint64
Swapped uint64
}
type MapInfo struct {
Inode int64
Inode uint64
Name string
}
@@ -65,7 +65,7 @@ func NewMapInfo(mapLine []byte) MapInfo {
if len(pieces) == 6 {
mi.Name = string(pieces[5])
}
mi.Inode, err = strconv.ParseInt(string(pieces[4]), 10, 64)
mi.Inode, err = ParseUint(pieces[4], 10, 64)
if err != nil {
panic(fmt.Sprintf("NewMapInfo: Atoi(%s): %s (%s)",
string(pieces[4]), err, string(mapLine)))
@@ -168,7 +168,7 @@ func splitSpaces(b []byte) [][]byte {
// procMem returns the amount of Pss, shared, and swapped out memory
// used. The swapped out amount refers to anonymous pages only.
func procMem(pid int) (pss float64, shared, priv, swap int64, err error) {
func procMem(pid int) (pss float64, shared, priv, swap uint64, err error) {
fPath := fmt.Sprintf("/proc/%d/smaps", pid)
f, err := os.Open(fPath)
if err != nil {
@@ -197,37 +197,37 @@ func procMem(pid int) (pss float64, shared, priv, swap int64, err error) {
return
}
if bytes.Contains(l, []byte{'-'}) {
if len(l) != mapDetailLen {
//curr = NewMapInfo(l)
continue
}
pieces := splitSpaces(l)
ty := string(pieces[0])
var v int64
var v uint64
switch ty {
case "Pss:":
v, err = strconv.ParseInt(string(pieces[1]), 10, 64)
v, err = ParseUint(pieces[1], 10, 64)
if err != nil {
err = fmt.Errorf("Atoi(%s): %s", string(pieces[1]), err)
return
}
pss += float64(v) + PssAdjust
case "Shared_Clean:", "Shared_Dirty:":
v, err = strconv.ParseInt(string(pieces[1]), 10, 64)
v, err = ParseUint(pieces[1], 10, 64)
if err != nil {
err = fmt.Errorf("Atoi(%s): %s", string(pieces[1]), err)
return
}
shared += v
case "Private_Clean:", "Private_Dirty:":
v, err = strconv.ParseInt(string(pieces[1]), 10, 64)
v, err = ParseUint(pieces[1], 10, 64)
if err != nil {
err = fmt.Errorf("Atoi(%s): %s", string(pieces[1]), err)
return
}
priv += v
case "Swap:":
v, err = strconv.ParseInt(string(pieces[1]), 10, 64)
v, err = ParseUint(pieces[1], 10, 64)
if err != nil {
err = fmt.Errorf("Atoi(%s): %s", string(pieces[1]), err)
return

0 comments on commit 77e0408

Please sign in to comment.