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

Subscription subject interning #819

Closed
moredure opened this issue Sep 11, 2021 · 7 comments
Closed

Subscription subject interning #819

moredure opened this issue Sep 11, 2021 · 7 comments
Labels
feature New feature request

Comments

@moredure
Copy link
Contributor

moredure commented Sep 11, 2021

Feature Request

Reduce allocation by interning subjects using predefined map of subjects or just by utilising intern.Bytes

Use Case:

Subscriptions

Proposed Change:

import "github.com/josharian/intern"
subj := intern.Bytes(nc.ps.ma.subject)

or some predefined map

subj := nc.subjects[string(nc.ps.ma.subject)]

Who Benefits From The Change(s)?

Users of subscriptions

@moredure moredure added the feature New feature request label Sep 11, 2021
@derekcollison
Copy link
Member

We either copy or send to a socket any message (with subject) that is given to us, so internal to the library not sure this would help too much, but could be used above the NATS lib.

@moredure
Copy link
Contributor Author

moredure commented Sep 14, 2021

I am talking about processMsg in Subscribe* methods where copy of string(subjectBytes) instantiated for each processed message

@derekcollison
Copy link
Member

Do we have any measurable results?

@moredure
Copy link
Contributor Author

moredure commented Sep 14, 2021

I also started reusing Msg container utilising sync.Pool (since my application using fixed size messages) as a result gotten rid of allocations for subscriptions in my use case. I will check if there is any benchmarks I will be able to run them on processMsg.

@derekcollison
Copy link
Member

If it has a definitive impact I am all for that for sure. Thanks!

@moredure
Copy link
Contributor Author

moredure commented Sep 15, 2021

@derekcollison What about this #824 ?

@moredure moredure reopened this Sep 15, 2021
@moredure
Copy link
Contributor Author

moredure commented Sep 16, 2021

Regarding message container reuse I am using smth like this and totally ok with it, because of fixed message sizes, have no idea how to adapt it to public use, maybe we need some ranges of messages size and achive messages depending on their size in different buckets, or utilize smth known for it. Also maybe some adapter factory in options to allow decide whether the pool .Get() should be used. or default New().(*Msg).

var msgPool = &sync.Pool{
	New: func() interface{} {
		return new(Msg)
	},
}

func (m *Msg) Release() {
	m.Subject = ""
	m.Reply = ""
	m.Header = nil
	m.Data = m.Data[:0]
	m.Sub = nil
	m.next = nil
	m.barrier = nil
	m.ackd = 0
	msgPool.Put(m)
}

m := msgPool.Get().(*Msg)

if !nc.ps.msgCopied {
	if cap(m.Data) < len(data) {
		m.Data = m.Data[:cap(m.Data)]
		m.Data = append(m.Data, make([]byte, len(data) - cap(m.Data))...)
		copy(m.Data, data)
	} else {
		m.Data = m.Data[:len(data)]
		copy(m.Data, data)
	}
} else {
	m.Data = data
}

for msg := range subChan {
     process(msg)
     msg.Release()
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature request
Projects
None yet
Development

No branches or pull requests

2 participants