Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve func packCommand performance, the more args the more obvious eff... #5

Closed
wants to merge 1 commit into from

Conversation

walu
Copy link

@walu walu commented May 6, 2014

execute 100K times: packCommand("GET", "USERl-list", 123)
improve nearly 100%, 207->117

@xuyu
Copy link
Owner

xuyu commented May 7, 2014

func packCommand(args ...interface{}) ([]byte, error) {
    buf := buffers.GetBuffer()
    defer buffers.PutBuffer(buf)
    if _, err := fmt.Fprintf(buf, "*%d\r\n", len(args)); err != nil {
        return nil, err
    }
    var s string
    for _, arg := range args {
        switch v := arg.(type) {
        case string:
            s = v
        case int:
            s = strconv.Itoa(v)
        case int64:
            s = strconv.FormatInt(v, 10)
        case uint64:
            s = strconv.FormatUint(v, 10)
        case float64:
            s = strconv.FormatFloat(v, 'g', -1, 64)
        default:
            return nil, errors.New("invalid argument type when pack command")
        }
        if _, err := fmt.Fprintf(buf, "$%d\r\n%s\r\n", len(s), s); err != nil {
            return nil, err
        }
    }
    return buf.Bytes(), nil
}

func packCommand2(args ...interface{}) ([]byte, error) {
    var s, ret string
    for _, arg := range args {
        switch v := arg.(type) {
        case string:
            s = v
        case int:
            s = strconv.Itoa(v)
        case int64:
            s = strconv.FormatInt(v, 10)
        case uint64:
            s = strconv.FormatUint(v, 10)
        case float64:
            s = strconv.FormatFloat(v, 'g', -1, 64)
        default:
            return nil, errors.New("invalid argument type when pack command")
        }
        ret += "$" + strconv.Itoa(len(s)) + "\r\n" + s + "\r\n"
    }
    return []byte(ret), nil
}

func numLen(i int64) int64 {
    n, pos10 := int64(1), int64(10)
    if i < 0 {
        i = -i
        n++
    }
    for i >= pos10 {
        n++
        pos10 *= 10
    }
    return n
}

func packCommand3(args ...interface{}) ([]byte, error) {
    n := len(args)
    res := make([]byte, 0, 16*n)
    res = append(res, byte('*'))
    res = strconv.AppendInt(res, int64(n), 10)
    res = append(res, byte('\r'), byte('\n'))
    for _, arg := range args {
        res = append(res, byte('$'))
        switch v := arg.(type) {
        case []byte:
            res = strconv.AppendInt(res, int64(len(v)), 10)
            res = append(res, byte('\r'), byte('\n'))
            res = append(res, v...)
        case string:
            res = strconv.AppendInt(res, int64(len(v)), 10)
            res = append(res, byte('\r'), byte('\n'))
            res = append(res, []byte(v)...)
        case int:
            res = strconv.AppendInt(res, numLen(int64(v)), 10)
            res = append(res, byte('\r'), byte('\n'))
            res = strconv.AppendInt(res, int64(v), 10)
        case int64:
            res = strconv.AppendInt(res, numLen(v), 10)
            res = append(res, byte('\r'), byte('\n'))
            res = strconv.AppendInt(res, int64(v), 10)
        case uint64:
            res = strconv.AppendInt(res, numLen(int64(v)), 10)
            res = append(res, byte('\r'), byte('\n'))
            res = strconv.AppendUint(res, uint64(v), 10)
        case float64:
            var buf []byte
            buf = strconv.AppendFloat(buf, v, 'g', -1, 64)
            res = strconv.AppendInt(res, int64(len(buf)), 10)
            res = append(res, byte('\r'), byte('\n'))
            res = append(res, buf...)
        default:
            return nil, errors.New("invalid argument type when pack command")
        }
        res = append(res, byte('\r'), byte('\n'))
    }
    return res, nil
}

@xuyu
Copy link
Owner

xuyu commented May 7, 2014

func BenchmarkPackCommand(b *testing.B) {
    var (
        s = "Command"
        n = 123
        f = 7.11
    )
    for i := 0; i < b.N; i++ {
        packCommand(s, n, f)
    }
}

func BenchmarkPackCommand2(b *testing.B) {
    var (
        s = "Command"
        n = 123
        f = 7.11
    )
    for i := 0; i < b.N; i++ {
        packCommand2(s, n, f)
    }
}

func BenchmarkPackCommand3(b *testing.B) {
    var (
        s = "Command"
        n = 123
        f = 7.11
    )
    for i := 0; i < b.N; i++ {
        packCommand3(s, n, f)
    }
}

@xuyu
Copy link
Owner

xuyu commented May 7, 2014

go test -test.bench="^BenchmarkPackCommand" -test.benchmem=true -test.run="^$"
BenchmarkPackCommand      500000          2562 ns/op         123 B/op          8 allocs/op
BenchmarkPackCommand2    1000000          1678 ns/op         248 B/op         13 allocs/op
BenchmarkPackCommand3    1000000          1189 ns/op          96 B/op          6 allocs/op

@xuyu xuyu closed this May 7, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants