Skip to content

Commit

Permalink
Use a sync.Pool for Unmarshal
Browse files Browse the repository at this point in the history
Attributes cause the most memory pressure. Start with caching those

Before
"""
12904 B/op         48 allocs/op
"""

After
"""
7483 B/op         34 allocs/op
"""
  • Loading branch information
Sean-Der committed Mar 7, 2024
1 parent 46f6da0 commit 138ce3c
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 10 deletions.
45 changes: 36 additions & 9 deletions unmarshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,22 @@ import (
"net/url"
"strconv"
"strings"
"sync"
)

var (
errSDPInvalidSyntax = errors.New("sdp: invalid syntax")
errSDPInvalidNumericValue = errors.New("sdp: invalid numeric value")
errSDPInvalidValue = errors.New("sdp: invalid value")
errSDPInvalidPortValue = errors.New("sdp: invalid port value")
errSDPCacheInvalid = errors.New("sdp: invalid cache")

//nolint: gochecknoglobals
unmarshalCachePool = sync.Pool{
New: func() interface{} {
return &unmarshalCache{}
},
}
)

// UnmarshalString is the primary function that deserializes the session description
Expand Down Expand Up @@ -100,16 +109,27 @@ var (
// | s16 | | 14 | | | | 15 | | | 12 | | | | | | | | |
// +--------+----+-------+----+-----+----+-----+---+----+----+---+---+-----+---+---+----+---+----+
func (s *SessionDescription) UnmarshalString(value string) error {
var ok bool
l := new(lexer)
if l.cache, ok = unmarshalCachePool.Get().(*unmarshalCache); !ok {
return errSDPCacheInvalid
}
defer unmarshalCachePool.Put(l.cache)

l.cache.reset()
l.desc = s
l.value = value

for state := s1; state != nil; {
var err error
state, err = state(l)
if err != nil {
return err
}
}

s.Attributes = l.cache.cloneSessionAttributes()
populateMediaAttributes(l.cache, l.desc)
return nil
}

Expand Down Expand Up @@ -730,18 +750,19 @@ func unmarshalSessionAttribute(l *lexer) (stateFn, error) {
}

i := strings.IndexRune(value, ':')
var a Attribute
a := l.cache.getSessionAttribute()
if i > 0 {
a = NewAttribute(value[:i], value[i+1:])
a.Key = value[:i]
a.Value = value[i+1:]
} else {
a = NewPropertyAttribute(value)
a.Key = value
}

l.desc.Attributes = append(l.desc.Attributes, a)
return s11, nil
}

func unmarshalMediaDescription(l *lexer) (stateFn, error) {
populateMediaAttributes(l.cache, l.desc)
var newMediaDesc MediaDescription

// <media>
Expand Down Expand Up @@ -869,15 +890,14 @@ func unmarshalMediaAttribute(l *lexer) (stateFn, error) {
}

i := strings.IndexRune(value, ':')
var a Attribute
a := l.cache.getMediaAttribute()
if i > 0 {
a = NewAttribute(value[:i], value[i+1:])
a.Key = value[:i]
a.Value = value[i+1:]
} else {
a = NewPropertyAttribute(value)
a.Key = value
}

latestMediaDesc := l.desc.MediaDescriptions[len(l.desc.MediaDescriptions)-1]
latestMediaDesc.Attributes = append(latestMediaDesc.Attributes, a)
return s14, nil
}

Expand Down Expand Up @@ -927,3 +947,10 @@ func parsePort(value string) (int, error) {

return port, nil
}

func populateMediaAttributes(c *unmarshalCache, s *SessionDescription) {
if len(s.MediaDescriptions) != 0 {
lastMediaDesc := s.MediaDescriptions[len(s.MediaDescriptions)-1]
lastMediaDesc.Attributes = c.cloneMediaAttributes()
}
}
44 changes: 44 additions & 0 deletions unmarshal_cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT

package sdp

type unmarshalCache struct {
sessionAttributes []Attribute
mediaAttributes []Attribute
}

func (c *unmarshalCache) reset() {
c.sessionAttributes = c.sessionAttributes[:0]
c.mediaAttributes = c.mediaAttributes[:0]
}

func (c *unmarshalCache) getSessionAttribute() *Attribute {
c.sessionAttributes = append(c.sessionAttributes, Attribute{})
return &c.sessionAttributes[len(c.sessionAttributes)-1]
}

func (c *unmarshalCache) cloneSessionAttributes() []Attribute {
if len(c.sessionAttributes) == 0 {
return nil
}
s := make([]Attribute, len(c.sessionAttributes))
copy(s, c.sessionAttributes)
c.sessionAttributes = c.sessionAttributes[:0]
return s
}

func (c *unmarshalCache) getMediaAttribute() *Attribute {
c.mediaAttributes = append(c.mediaAttributes, Attribute{})
return &c.mediaAttributes[len(c.mediaAttributes)-1]
}

func (c *unmarshalCache) cloneMediaAttributes() []Attribute {
if len(c.mediaAttributes) == 0 {
return nil
}
s := make([]Attribute, len(c.mediaAttributes))
copy(s, c.mediaAttributes)
c.mediaAttributes = c.mediaAttributes[:0]
return s
}
3 changes: 2 additions & 1 deletion util.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,8 @@ func (s *SessionDescription) GetPayloadTypeForCodec(wanted Codec) (uint8, error)
type stateFn func(*lexer) (stateFn, error)

type lexer struct {
desc *SessionDescription
desc *SessionDescription
cache *unmarshalCache
baseLexer
}

Expand Down

0 comments on commit 138ce3c

Please sign in to comment.