Преглед изворни кода

Integrate Hashtable into Multicaster, where @mwarning found heaviest std::map() overhead.

Adam Ierymenko пре 10 година
родитељ
комит
b11ffc9635
5 измењених фајлова са 117 додато и 57 уклоњено
  1. 68 29
      node/Hashtable.hpp
  2. 2 0
      node/MAC.hpp
  3. 2 0
      node/MulticastGroup.hpp
  4. 30 26
      node/Multicaster.cpp
  5. 15 2
      node/Multicaster.hpp

+ 68 - 29
node/Hashtable.hpp

@@ -19,7 +19,6 @@
  *
  * ZeroTier may be used and distributed under the terms of the GPLv3, which
  * are available at: http://www.gnu.org/licenses/gpl-3.0.html
- *
  */
 
 #ifndef ZT_HASHTABLE_HPP
@@ -39,9 +38,6 @@ namespace ZeroTier {
  * This is not a drop-in replacement for STL containers, and has several
  * limitations. It's designed to be small and fast for use in the
  * ZeroTier core.
- *
- * Pairs of values can also be used as a key. In this case the first and
- * second element of the pair's hash codes are XORed.
  */
 template<typename K,typename V>
 class Hashtable
@@ -49,12 +45,11 @@ class Hashtable
 private:
 	struct _Bucket
 	{
-		_Bucket(const K &k,const V &v) :
-			k(k),
-			v(v) {}
-		_Bucket *next;
+		_Bucket(const K &k,const V &v) : k(k),v(v) {}
+		_Bucket(const K &k) : k(k),v() {}
 		K k;
 		V v;
+		_Bucket *next;
 	};
 
 public:
@@ -188,35 +183,56 @@ public:
 	/**
 	 * @param k Key
 	 * @param v Value
+	 * @return Reference to value in table
 	 */
-	inline void set(const K &k,const V &v)
+	inline V &set(const K &k,const V &v)
 	{
-		if (_s >= _bc) {
-			const unsigned long nc = _bc * 2;
-			_Bucket **nt = reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * nc));
-			if (nt) {
-				for(unsigned long i=0;i<nc;++i)
-					nt[i] = (_Bucket *)0;
-				for(unsigned long i=0;i<_bc;++i) {
-					_Bucket *b = _t[i];
-					while (b) {
-						_Bucket *const nb = b->next;
-						const unsigned long nidx = _hc(b->k) % nc;
-						b->next = nt[nidx];
-						nt[nidx] = b;
-						b = nb;
-					}
-				}
-				::free(_t);
-				_t = nt;
-				_bc = nc;
+		const unsigned long bidx = _hc(k) % _bc;
+
+		_Bucket *b = _t[bidx];
+		while (b) {
+			if (b->k == k) {
+				b->v = v;
+				return b->v;
 			}
+			b = b->next;
 		}
+
+		if (_s >= _bc)
+			_grow();
+
+		b = new _Bucket(k,v);
+		b->next = _t[bidx];
+		_t[bidx] = b;
+		++_s;
+
+		return b->v;
+	}
+
+	/**
+	 * @param k Key
+	 * @return Value, possibly newly created
+	 */
+	inline V &operator[](const K &k)
+	{
 		const unsigned long bidx = _hc(k) % _bc;
-		_Bucket *const b = new _Bucket(k,v);
+
+		_Bucket *b = _t[bidx];
+		while (b) {
+			if (b->k == k)
+				return b->v;
+			b = b->next;
+		}
+
+		if (_s >= _bc)
+			_grow();
+
+		b = new _Bucket(k);
 		b->next = _t[bidx];
 		_t[bidx] = b;
 		++_s;
+
+		return b->v;
 	}
 
 	/**
@@ -242,6 +258,29 @@ private:
 		return (unsigned long)((i ^ (i >> 32)) * 2654435761ULL);
 	}
 
+	inline void _grow()
+	{
+		const unsigned long nc = _bc * 2;
+		_Bucket **nt = reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * nc));
+		if (nt) {
+			for(unsigned long i=0;i<nc;++i)
+				nt[i] = (_Bucket *)0;
+			for(unsigned long i=0;i<_bc;++i) {
+				_Bucket *b = _t[i];
+				while (b) {
+					_Bucket *const nb = b->next;
+					const unsigned long nidx = _hc(b->k) % nc;
+					b->next = nt[nidx];
+					nt[nidx] = b;
+					b = nb;
+				}
+			}
+			::free(_t);
+			_t = nt;
+			_bc = nc;
+		}
+	}
+
 	_Bucket **_t;
 	unsigned long _bc;
 	unsigned long _s;

+ 2 - 0
node/MAC.hpp

@@ -242,6 +242,8 @@ public:
 	 */
 	inline unsigned int size() const throw() { return 6; }
 
+	inline unsigned long hashCode() const throw() { return (unsigned long)_m; }
+
 	inline MAC &operator=(const MAC &m)
 		throw()
 	{

+ 2 - 0
node/MulticastGroup.hpp

@@ -141,6 +141,8 @@ public:
 	 */
 	inline uint32_t adi() const throw() { return _adi; }
 
+	inline unsigned long hashCode() const throw() { return (_mac.hashCode() ^ (unsigned long)_adi); }
+
 	inline bool operator==(const MulticastGroup &g) const throw() { return ((_mac == g._mac)&&(_adi == g._adi)); }
 	inline bool operator!=(const MulticastGroup &g) const throw() { return ((_mac != g._mac)||(_adi != g._adi)); }
 	inline bool operator<(const MulticastGroup &g) const throw()

+ 30 - 26
node/Multicaster.cpp

@@ -41,7 +41,9 @@
 namespace ZeroTier {
 
 Multicaster::Multicaster(const RuntimeEnvironment *renv) :
-	RR(renv)
+	RR(renv),
+	_groups(1024),
+	_groups_m()
 {
 }
 
@@ -54,7 +56,7 @@ void Multicaster::addMultiple(uint64_t now,uint64_t nwid,const MulticastGroup &m
 	const unsigned char *p = (const unsigned char *)addresses;
 	const unsigned char *e = p + (5 * count);
 	Mutex::Lock _l(_groups_m);
-	MulticastGroupStatus &gs = _groups[std::pair<uint64_t,MulticastGroup>(nwid,mg)];
+	MulticastGroupStatus &gs = _groups[Multicaster::Key(nwid,mg)];
 	while (p != e) {
 		_add(now,nwid,mg,gs,Address(p,5));
 		p += 5;
@@ -64,11 +66,11 @@ void Multicaster::addMultiple(uint64_t now,uint64_t nwid,const MulticastGroup &m
 void Multicaster::remove(uint64_t nwid,const MulticastGroup &mg,const Address &member)
 {
 	Mutex::Lock _l(_groups_m);
-	std::map< std::pair<uint64_t,MulticastGroup>,MulticastGroupStatus >::iterator g(_groups.find(std::pair<uint64_t,MulticastGroup>(nwid,mg)));
-	if (g != _groups.end()) {
-		for(std::vector<MulticastGroupMember>::iterator m(g->second.members.begin());m!=g->second.members.end();++m) {
+	MulticastGroupStatus *s = _groups.get(Multicaster::Key(nwid,mg));
+	if (s) {
+		for(std::vector<MulticastGroupMember>::iterator m(s->members.begin());m!=s->members.end();++m) {
 			if (m->address == member) {
-				g->second.members.erase(m);
+				s->members.erase(m);
 				break;
 			}
 		}
@@ -102,18 +104,18 @@ unsigned int Multicaster::gather(const Address &queryingPeer,uint64_t nwid,const
 
 	Mutex::Lock _l(_groups_m);
 
-	std::map< std::pair<uint64_t,MulticastGroup>,MulticastGroupStatus >::const_iterator gs(_groups.find(std::pair<uint64_t,MulticastGroup>(nwid,mg)));
-	if ((gs != _groups.end())&&(!gs->second.members.empty())) {
-		totalKnown += (unsigned int)gs->second.members.size();
+	const MulticastGroupStatus *s = _groups.get(Multicaster::Key(nwid,mg));
+	if ((s)&&(!s->members.empty())) {
+		totalKnown += (unsigned int)s->members.size();
 
 		// Members are returned in random order so that repeated gather queries
 		// will return different subsets of a large multicast group.
 		k = 0;
-		while ((added < limit)&&(k < gs->second.members.size())&&((appendTo.size() + ZT_ADDRESS_LENGTH) <= ZT_UDP_DEFAULT_PAYLOAD_MTU)) {
+		while ((added < limit)&&(k < s->members.size())&&((appendTo.size() + ZT_ADDRESS_LENGTH) <= ZT_UDP_DEFAULT_PAYLOAD_MTU)) {
 			rptr = (unsigned int)RR->node->prng();
 
 restart_member_scan:
-			a = gs->second.members[rptr % (unsigned int)gs->second.members.size()].address.toInt();
+			a = s->members[rptr % (unsigned int)s->members.size()].address.toInt();
 			for(i=0;i<k;++i) {
 				if (picked[i] == a) {
 					++rptr;
@@ -146,10 +148,10 @@ std::vector<Address> Multicaster::getMembers(uint64_t nwid,const MulticastGroup
 {
 	std::vector<Address> ls;
 	Mutex::Lock _l(_groups_m);
-	std::map< std::pair<uint64_t,MulticastGroup>,MulticastGroupStatus >::const_iterator gs(_groups.find(std::pair<uint64_t,MulticastGroup>(nwid,mg)));
-	if (gs == _groups.end())
+	const MulticastGroupStatus *s = _groups.get(Multicaster::Key(nwid,mg));
+	if (!s)
 		return ls;
-	for(std::vector<MulticastGroupMember>::const_reverse_iterator m(gs->second.members.rbegin());m!=gs->second.members.rend();++m) {
+	for(std::vector<MulticastGroupMember>::const_reverse_iterator m(s->members.rbegin());m!=s->members.rend();++m) {
 		ls.push_back(m->address);
 		if (ls.size() >= limit)
 			break;
@@ -173,7 +175,7 @@ void Multicaster::send(
 	unsigned long *indexes = idxbuf;
 
 	Mutex::Lock _l(_groups_m);
-	MulticastGroupStatus &gs = _groups[std::pair<uint64_t,MulticastGroup>(nwid,mg)];
+	MulticastGroupStatus &gs = _groups[Multicaster::Key(nwid,mg)];
 
 	if (!gs.members.empty()) {
 		// Allocate a memory buffer if group is monstrous
@@ -291,18 +293,22 @@ void Multicaster::send(
 void Multicaster::clean(uint64_t now)
 {
 	Mutex::Lock _l(_groups_m);
-	for(std::map< std::pair<uint64_t,MulticastGroup>,MulticastGroupStatus >::iterator mm(_groups.begin());mm!=_groups.end();) {
-		for(std::list<OutboundMulticast>::iterator tx(mm->second.txQueue.begin());tx!=mm->second.txQueue.end();) {
+
+	Multicaster::Key *k = (Multicaster::Key *)0;
+	MulticastGroupStatus *s = (MulticastGroupStatus *)0;
+	Hashtable<Multicaster::Key,MulticastGroupStatus>::Iterator mm(_groups);
+	while (mm.next(k,s)) {
+		for(std::list<OutboundMulticast>::iterator tx(s->txQueue.begin());tx!=s->txQueue.end();) {
 			if ((tx->expired(now))||(tx->atLimit()))
-				mm->second.txQueue.erase(tx++);
+				s->txQueue.erase(tx++);
 			else ++tx;
 		}
 
 		unsigned long count = 0;
 		{
-			std::vector<MulticastGroupMember>::iterator reader(mm->second.members.begin());
+			std::vector<MulticastGroupMember>::iterator reader(s->members.begin());
 			std::vector<MulticastGroupMember>::iterator writer(reader);
-			while (reader != mm->second.members.end()) {
+			while (reader != s->members.end()) {
 				if ((now - reader->timestamp) < ZT_MULTICAST_LIKE_EXPIRE) {
 					*writer = *reader;
 					++writer;
@@ -313,13 +319,11 @@ void Multicaster::clean(uint64_t now)
 		}
 
 		if (count) {
-			mm->second.members.resize(count);
-			++mm;
-		} else if (mm->second.txQueue.empty()) {
-			_groups.erase(mm++);
+			s->members.resize(count);
+		} else if (s->txQueue.empty()) {
+			_groups.erase(*k);
 		} else {
-			mm->second.members.clear();
-			++mm;
+			s->members.clear();
 		}
 	}
 }

+ 15 - 2
node/Multicaster.hpp

@@ -36,6 +36,7 @@
 #include <list>
 
 #include "Constants.hpp"
+#include "Hashtable.hpp"
 #include "Address.hpp"
 #include "MAC.hpp"
 #include "MulticastGroup.hpp"
@@ -56,6 +57,18 @@ class Packet;
 class Multicaster : NonCopyable
 {
 private:
+	struct Key
+	{
+		Key() : nwid(0),mg() {}
+		Key(uint64_t n,const MulticastGroup &g) : nwid(n),mg(g) {}
+
+		uint64_t nwid;
+		MulticastGroup mg;
+
+		inline bool operator==(const Key &k) const throw() { return ((nwid == k.nwid)&&(mg == k.mg)); }
+		inline unsigned long hashCode() const throw() { return (mg.hashCode() ^ (unsigned long)(nwid ^ (nwid >> 32))); }
+	};
+
 	struct MulticastGroupMember
 	{
 		MulticastGroupMember() {}
@@ -89,7 +102,7 @@ public:
 	inline void add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,const Address &member)
 	{
 		Mutex::Lock _l(_groups_m);
-		_add(now,nwid,mg,_groups[std::pair<uint64_t,MulticastGroup>(nwid,mg)],member);
+		_add(now,nwid,mg,_groups[Multicaster::Key(nwid,mg)],member);
 	}
 
 	/**
@@ -181,7 +194,7 @@ private:
 	void _add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member);
 
 	const RuntimeEnvironment *RR;
-	std::map< std::pair<uint64_t,MulticastGroup>,MulticastGroupStatus > _groups;
+	Hashtable<Multicaster::Key,MulticastGroupStatus> _groups;
 	Mutex _groups_m;
 };