Commit 0100afbd authored by Rui Ueyama's avatar Rui Ueyama Committed by Nigel Tao

image/png: use branch-free abs function

benchmark                        old ns/op     new ns/op     delta
BenchmarkPaeth                   5.06          6.02          +18.97%
BenchmarkDecodeGray              1010551       956911        -5.31%
BenchmarkDecodeNRGBAGradient     3877813       3754160       -3.19%
BenchmarkDecodeNRGBAOpaque       3194058       3079094       -3.60%
BenchmarkDecodePaletted          699243        700211        +0.14%
BenchmarkDecodeRGB               2835733       2692120       -5.06%
BenchmarkDecodeInterlacing       3651805       3563124       -2.43%
BenchmarkEncodeGray              4399183       4404113       +0.11%
BenchmarkEncodeNRGBOpaque        13323627      13306485      -0.13%
BenchmarkEncodeNRGBA             15840092      15751188      -0.56%
BenchmarkEncodePaletted          4396622       4404373       +0.18%
BenchmarkEncodeRGBOpaque         13320475      13279189      -0.31%
BenchmarkEncodeRGBA              36898392      36781002      -0.32%

LGTM=nigeltao
R=nigeltao
CC=golang-codereviews
https://golang.org/cl/117290043
parent d3fb02b5
...@@ -4,6 +4,21 @@ ...@@ -4,6 +4,21 @@
package png package png
// intSize is either 32 or 64.
const intSize = 32 << (^uint(0) >> 63)
func abs(x int) int {
// m := -1 if x < 0. m := 0 otherwise.
m := x >> (intSize - 1)
// In two's complement representation, the negative number
// of any number (except the smallest one) can be computed
// by flipping all the bits and add 1. This is faster than
// code with a branch.
// See Hacker's Delight, section 2-4.
return (x ^ m) - m
}
// paeth implements the Paeth filter function, as per the PNG specification. // paeth implements the Paeth filter function, as per the PNG specification.
func paeth(a, b, c uint8) uint8 { func paeth(a, b, c uint8) uint8 {
// This is an optimized version of the sample code in the PNG spec. // This is an optimized version of the sample code in the PNG spec.
...@@ -16,16 +31,9 @@ func paeth(a, b, c uint8) uint8 { ...@@ -16,16 +31,9 @@ func paeth(a, b, c uint8) uint8 {
pc := int(c) pc := int(c)
pa := int(b) - pc pa := int(b) - pc
pb := int(a) - pc pb := int(a) - pc
pc = pa + pb pc = abs(pa + pb)
if pa < 0 { pa = abs(pa)
pa = -pa pb = abs(pb)
}
if pb < 0 {
pb = -pb
}
if pc < 0 {
pc = -pc
}
if pa <= pb && pa <= pc { if pa <= pb && pa <= pc {
return a return a
} else if pb <= pc { } else if pb <= pc {
...@@ -44,16 +52,9 @@ func filterPaeth(cdat, pdat []byte, bytesPerPixel int) { ...@@ -44,16 +52,9 @@ func filterPaeth(cdat, pdat []byte, bytesPerPixel int) {
b = int(pdat[j]) b = int(pdat[j])
pa = b - c pa = b - c
pb = a - c pb = a - c
pc = pa + pb pc = abs(pa + pb)
if pa < 0 { pa = abs(pa)
pa = -pa pb = abs(pb)
}
if pb < 0 {
pb = -pb
}
if pc < 0 {
pc = -pc
}
if pa <= pb && pa <= pc { if pa <= pb && pa <= pc {
// No-op. // No-op.
} else if pb <= pc { } else if pb <= pc {
......
...@@ -10,7 +10,7 @@ import ( ...@@ -10,7 +10,7 @@ import (
"testing" "testing"
) )
func abs(x int) int { func slowAbs(x int) int {
if x < 0 { if x < 0 {
return -x return -x
} }
...@@ -21,9 +21,9 @@ func abs(x int) int { ...@@ -21,9 +21,9 @@ func abs(x int) int {
// It is a straight port of the sample code in the PNG spec, section 9.4. // It is a straight port of the sample code in the PNG spec, section 9.4.
func slowPaeth(a, b, c uint8) uint8 { func slowPaeth(a, b, c uint8) uint8 {
p := int(a) + int(b) - int(c) p := int(a) + int(b) - int(c)
pa := abs(p - int(a)) pa := slowAbs(p - int(a))
pb := abs(p - int(b)) pb := slowAbs(p - int(b))
pc := abs(p - int(c)) pc := slowAbs(p - int(c))
if pa <= pb && pa <= pc { if pa <= pb && pa <= pc {
return a return a
} else if pb <= pc { } else if pb <= pc {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment