Skip to content

Commit

Permalink
Optimization merge transformations before performing
Browse files Browse the repository at this point in the history
  • Loading branch information
aldor007 committed Aug 5, 2018
1 parent 8c7e2e7 commit 17fba59
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 42 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
* 0.12.0
* Feature: Add new monitoring metrics (time of image generation and count of it)
* Feature: Do eror placeholder in background (returns it faster to user)
* Feature: Do error placeholder in background (returns it faster to user)
* Feature: Try to merge transformations before performing them
* 0.11.2
* Bugfix: Fix compress plugin (don't compress on range or condition)
* 0.11.1
Expand Down
2 changes: 1 addition & 1 deletion pkg/monitoring/prometheus.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func (p *PrometheusReporter) Histogram(metric string, val float64) {
func (p *PrometheusReporter) Timer(metric string) Timer {
t := Timer{time.Now(), metric, func(start time.Time, metricName string) {
timeDiff := time.Since(start)
p.Histogram(metricName, float64(timeDiff.Nanoseconds()) / 1000.0)
p.Histogram(metricName, float64(timeDiff.Nanoseconds())/1000.0)
}}

return t
Expand Down
20 changes: 7 additions & 13 deletions pkg/processor/processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,17 +360,6 @@ func (r *RequestProcessor) handleNotFound(obj, parentObj *object.FileObject, tra

defer parentRes.Close()

transLen := len(transformsTab)
if transLen > 1 {
// revers order of transforms
for i := 0; i < len(transformsTab)/2; i++ {
j := len(transformsTab) - i - 1
transformsTab[i], transformsTab[j] = transformsTab[j], transformsTab[i]
}

}

monitoring.Log().Info("Performing transforms", zap.String("obj.Bucket", obj.Bucket), zap.String("obj.Key", obj.Key), zap.Int("transformsLen", len(transformsTab)))
return r.processImage(obj, parentRes, transformsTab)
} else if obj.HasTransform() {
parentRes.Close()
Expand Down Expand Up @@ -415,7 +404,7 @@ func handleS3Get(req *http.Request, obj *object.FileObject) *response.Response {

}

func (r *RequestProcessor) processImage(obj *object.FileObject, parent *response.Response, transforms []transforms.Transforms) *response.Response {
func (r *RequestProcessor) processImage(obj *object.FileObject, parent *response.Response, transformsTab []transforms.Transforms) *response.Response {
monitoring.Report().Inc("request_type;type:transform")
ctx := obj.Ctx
taked := r.throttler.Take(ctx)
Expand All @@ -426,8 +415,13 @@ func (r *RequestProcessor) processImage(obj *object.FileObject, parent *response
}
defer r.throttler.Release()

transformsLen := len(transformsTab)
mergedTrans := transforms.Merge(transformsTab)
mergedLen := len(mergedTrans)

monitoring.Log().Info("Performing transforms", zap.String("obj.Bucket", obj.Bucket), zap.String("obj.Key", obj.Key), zap.Int("transformsLen", transformsLen), zap.Int("mergedLen", mergedLen))
eng := engine.NewImageEngine(parent)
res, err := eng.Process(obj, transforms)
res, err := eng.Process(obj, mergedTrans)
if err != nil {
return response.NewError(400, err)
}
Expand Down
98 changes: 98 additions & 0 deletions pkg/transforms/transform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,3 +248,101 @@ func TestTransforms_Watermark(t *testing.T) {

assert.NotNil(t, err)
}

func TestTransforms_Merge_Resize(t *testing.T) {
tab := make([]Transforms, 2)
tab[0].Resize(100, 0, false)

tab[1].Resize(0, 300, true)

result := Merge(tab)

assert.Equal(t, len(result), 1)
assert.Equal(t, result[0].width, 100)
assert.Equal(t, result[0].height, 300)
assert.Equal(t, result[0].enlarge, true)
}

func TestTransforms_Merge_Crop(t *testing.T) {
tab := make([]Transforms, 2)
tab[0].Crop(4444, 0, "smart", false)

tab[1].Crop(0, 120, "smart", false)

result := Merge(tab)

assert.Equal(t, len(result), 1)
assert.Equal(t, result[0].width, 4444)
assert.Equal(t, result[0].height, 120)
assert.Equal(t, result[0].enlarge, false)
assert.Equal(t, result[0].crop, true)
assert.Equal(t, result[0].gravity, bimg.GravitySmart)
}

func TestTransforms_Merge_Blur(t *testing.T) {
tab := make([]Transforms, 3)
tab[0].Blur(1., 3.)
tab[1].Blur(2., 4.)
tab[2].Blur(3., 3.)

result := Merge(tab)

assert.Equal(t, len(result), 1)
assert.Equal(t, result[0].blur.sigma, 6.)
assert.Equal(t, result[0].blur.minAmpl, 10.)
}

func TestTransforms_Merge_Single(t *testing.T) {
tab := make([]Transforms, 1)
tab[0].Blur(1., 3.)

result := Merge(tab)

assert.Equal(t, len(result), 1)
assert.Equal(t, result[0].blur.sigma, 1.)
assert.Equal(t, result[0].blur.minAmpl, 3.)
}

func TestTransforms_Merge_MultiTrans(t *testing.T) {
tab := make([]Transforms, 4)
tab[0].Blur(1., 3.)
tab[0].Quality(10)
tab[1].Interlace()
tab[2].StripMetadata()
tab[3].Format("webp")

result := Merge(tab)

assert.Equal(t, len(result), 1)
assert.Equal(t, result[0].blur.sigma, 1.)
assert.Equal(t, result[0].blur.minAmpl, 3.)
assert.Equal(t, result[0].interlace, true)
assert.Equal(t, result[0].stripMetadata, true)
assert.Equal(t, result[0].format, bimg.WEBP)
assert.Equal(t, result[0].FormatStr, "webp")
assert.Equal(t, result[0].quality, 10)
}

func TestTransforms_Merge_Empty(t *testing.T) {
tab := make([]Transforms, 1)

result := Merge(tab)

assert.Equal(t, len(result), 1)
assert.Equal(t, result[0].NotEmpty, false)
}

func TestTransforms_Merge_Watermark(t *testing.T) {
tab := make([]Transforms, 3)
tab[0].Blur(1., 3.)
tab[0].Watermark("image2", "top-left", 2.)
tab[1].Watermark("image", "top-left", 2.)
tab[2].Blur(3., 3.)

result := Merge(tab)

assert.Equal(t, len(result), 2)
assert.Equal(t, result[0].blur.sigma, 3.)
assert.Equal(t, result[0].blur.minAmpl, 3.)
assert.Equal(t, result[1].watermark.image, "image2")
}
137 changes: 110 additions & 27 deletions pkg/transforms/transforms.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func (w watermark) calculatePostion(width, height int) (top int, left int) {
// ImageInfo holds information about image
type ImageInfo struct {
width int // width of image in px
height int // HeightValue of image in px
height int // height of image in px
format string // format of image in string e.x. "jpg"
}

Expand All @@ -76,7 +76,7 @@ func NewImageInfo(metadata bimg.ImageMetadata, format string) ImageInfo {

// Transforms struct hold information about what operations should be performed on image
type Transforms struct {
HeightValue int
height int
width int
areaHeight int
areaWidth int
Expand All @@ -97,11 +97,9 @@ type Transforms struct {
rotate bimg.Angle
interpretation bimg.Interpretation
gravity bimg.Gravity

blur blur

format bimg.ImageType
FormatStr string
blur blur
format bimg.ImageType
FormatStr string

watermark watermark

Expand All @@ -110,13 +108,13 @@ type Transforms struct {
transHash fnvI64
}

// Resize change image width and HeightValue
// Resize change image width and height
func (t *Transforms) Resize(width, height int, enlarge bool) error {
t.width = width
t.HeightValue = height
t.height = height
t.enlarge = enlarge

t.transHash.write(1111, uint64(t.width)*7, uint64(t.HeightValue)*3)
t.transHash.write(1111, uint64(t.width)*7, uint64(t.height)*3)

if t.enlarge {
t.transHash.write(12311)
Expand All @@ -129,7 +127,7 @@ func (t *Transforms) Resize(width, height int, enlarge bool) error {
// Crop extract part of image
func (t *Transforms) Crop(width, height int, gravity string, enlarge bool) error {
t.width = width
t.HeightValue = height
t.height = height
t.enlarge = enlarge
t.crop = true
t.NotEmpty = true
Expand All @@ -139,7 +137,7 @@ func (t *Transforms) Crop(width, height int, gravity string, enlarge bool) error
t.gravity = bimg.GravitySmart
}

t.transHash.write(1212, uint64(t.width)*5, uint64(t.HeightValue), uint64(t.gravity))
t.transHash.write(1212, uint64(t.width)*5, uint64(t.height), uint64(t.gravity))
return nil
}

Expand Down Expand Up @@ -178,11 +176,11 @@ func (t *Transforms) Blur(sigma, minAmpl float64) error {

// Hash return unique transform identifier
func (t *Transforms) Hash() hash.Hash64 {
hash := murmur3.New64WithSeed(20171108)
hashValue := murmur3.New64WithSeed(20171108)
transHashB := make([]byte, 8)
binary.LittleEndian.PutUint64(transHashB, t.transHash.value())
hash.Write(transHashB)
return hash
hashValue.Write(transHashB)
return hashValue
}

// Format change image format
Expand Down Expand Up @@ -247,6 +245,91 @@ func (t *Transforms) Rotate(angle int) error {
return errors.New("wrong angle")
}

// Merge append transformation from other object
func (t *Transforms) Merge(other Transforms) error {
if other.NotEmpty == false {
return nil
}

if other.watermark.image != "" {
if t.watermark.image != "" {
return errors.New("already have watermark")
}
t.watermark = other.watermark
}

if other.width != 0 {
t.width = other.width
}

if other.height != 0 {
t.height = other.height
}

if other.crop {
t.crop = other.crop
}

if other.gravity != 0 {
t.gravity = other.gravity
}

if other.blur.minAmpl != 0 {
t.blur.minAmpl = t.blur.minAmpl + other.blur.minAmpl
}

if other.blur.sigma != 0 {
t.blur.sigma = t.blur.sigma + other.blur.sigma
}

if other.interlace {
t.interlace = other.interlace
}

if other.quality != 0 {
t.quality = other.quality
}

if other.format != 0 {
t.format = other.format
t.FormatStr = other.FormatStr
}

if other.stripMetadata {
t.stripMetadata = other.stripMetadata
}

t.transHash.write(other.transHash.value())
t.NotEmpty = other.NotEmpty

return nil
}

// Merge will merge tab of transformation into single one
func Merge(transformsTab []Transforms) []Transforms {
transLen := len(transformsTab)
if transLen <= 1 {
return transformsTab
}

// revers order of transforms
for i := 0; i < transLen/2; i++ {
j := transLen - i - 1
transformsTab[i], transformsTab[j] = transformsTab[j], transformsTab[i]
}

result := make([]Transforms, 1)
baseTrans := transformsTab[0]
result[0] = baseTrans
for i := 1; i < transLen; i++ {
if result[0].Merge(transformsTab[i]) != nil {
result = append(result, transformsTab[i])
}
}

return result
}

func imageFormat(format string) (bimg.ImageType, error) {
switch format {
case "jpeg", "jpg":
Expand All @@ -270,7 +353,7 @@ func imageFormat(format string) (bimg.ImageType, error) {
func (t *Transforms) BimgOptions(imageInfo ImageInfo) (bimg.Options, error) {
b := bimg.Options{
Width: t.width,
Height: t.HeightValue,
Height: t.height,
Enlarge: t.enlarge,
Crop: t.crop,
Interlace: t.interlace,
Expand Down Expand Up @@ -306,15 +389,15 @@ func (t *Transforms) BimgOptions(imageInfo ImageInfo) (bimg.Options, error) {
width := imageInfo.width
height := imageInfo.height

if t.width != 0 && t.HeightValue != 0 {
if t.width != 0 && t.height != 0 {
width = t.width
height = t.HeightValue
height = t.height
} else if t.width != 0 {
width = t.width
height = t.width * height / imageInfo.width
} else if t.HeightValue != 0 {
height = t.HeightValue
width = t.HeightValue * width / imageInfo.height
} else if t.height != 0 {
height = t.height
width = t.height * width / imageInfo.height
}

top, left := t.watermark.calculatePostion(width, height)
Expand All @@ -334,18 +417,18 @@ func (t *Transforms) BimgOptions(imageInfo ImageInfo) (bimg.Options, error) {
type fnvI64 uint64

func (f *fnvI64) write(data ...uint64) {
hash := *f
hashValue := *f

if hash == 0 {
hash = fnvI64(1231)
if hashValue == 0 {
hashValue = fnvI64(1231)
}

for _, d := range data {
hash ^= fnvI64(d)
hash *= fnvI64(prime64)
hashValue ^= fnvI64(d)
hashValue *= fnvI64(prime64)
}

*f = hash
*f = hashValue
}

func (f fnvI64) value() uint64 {
Expand Down

0 comments on commit 17fba59

Please sign in to comment.