Kaynağa Gözat

客户端gc优化,去掉TimerComponent跟KService中的两个gc

tanghai 7 yıl önce
ebeveyn
işleme
39d22ce1ce

+ 159 - 0
Server/Model/Base/MultiMap.cs

@@ -0,0 +1,159 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace ETModel
+{
+	public class MultiMap<T, K>
+	{
+		private readonly SortedDictionary<T, List<K>> dictionary = new SortedDictionary<T, List<K>>();
+
+		// 重用list
+		private readonly Queue<List<K>> queue = new Queue<List<K>>();
+
+		public SortedDictionary<T, List<K>> GetDictionary()
+		{
+			return this.dictionary;
+		}
+
+		public void Add(T t, K k)
+		{
+			List<K> list;
+			this.dictionary.TryGetValue(t, out list);
+			if (list == null)
+			{
+				list = this.FetchList();
+			}
+			list.Add(k);
+			this.dictionary[t] = list;
+		}
+
+		public KeyValuePair<T, List<K>> First()
+		{
+			return this.dictionary.First();
+		}
+
+		public T FirstKey()
+		{
+			return this.dictionary.Keys.First();
+		}
+
+		public int Count
+		{
+			get
+			{
+				return this.dictionary.Count;
+			}
+		}
+
+		private List<K> FetchList()
+		{
+			if (this.queue.Count > 0)
+			{
+				List<K> list = this.queue.Dequeue();
+				list.Clear();
+				return list;
+			}
+			return new List<K>();
+		}
+
+		private void RecycleList(List<K> list)
+		{
+			// 防止暴涨
+			if (this.queue.Count > 100)
+			{
+				return;
+			}
+			list.Clear();
+			this.queue.Enqueue(list);
+		}
+
+		public bool Remove(T t, K k)
+		{
+			List<K> list;
+			this.dictionary.TryGetValue(t, out list);
+			if (list == null)
+			{
+				return false;
+			}
+			if (!list.Remove(k))
+			{
+				return false;
+			}
+			if (list.Count == 0)
+			{
+				this.RecycleList(list);
+				this.dictionary.Remove(t);
+			}
+			return true;
+		}
+
+		public bool Remove(T t)
+		{
+			List<K> list = null;
+			this.dictionary.TryGetValue(t, out list);
+			if (list != null)
+			{
+				this.RecycleList(list);
+			}
+			return this.dictionary.Remove(t);
+		}
+
+		/// <summary>
+		/// 不返回内部的list,copy一份出来
+		/// </summary>
+		/// <param name="t"></param>
+		/// <returns></returns>
+		public K[] GetAll(T t)
+		{
+			List<K> list;
+			this.dictionary.TryGetValue(t, out list);
+			if (list == null)
+			{
+				return new K[0];
+			}
+			return list.ToArray();
+		}
+
+		/// <summary>
+		/// 返回内部的list
+		/// </summary>
+		/// <param name="t"></param>
+		/// <returns></returns>
+		public List<K> this[T t]
+		{
+			get
+			{
+				List<K> list;
+				this.dictionary.TryGetValue(t, out list);
+				return list;
+			}
+		}
+
+		public K GetOne(T t)
+		{
+			List<K> list;
+			this.dictionary.TryGetValue(t, out list);
+			if (list != null && list.Count > 0)
+			{
+				return list[0];
+			}
+			return default(K);
+		}
+
+		public bool Contains(T t, K k)
+		{
+			List<K> list;
+			this.dictionary.TryGetValue(t, out list);
+			if (list == null)
+			{
+				return false;
+			}
+			return list.Contains(k);
+		}
+
+		public bool ContainsKey(T t)
+		{
+			return this.dictionary.ContainsKey(t);
+		}
+	}
+}

+ 1 - 1
Server/Model/Component/BenchmarkComponent.cs

@@ -24,7 +24,7 @@ namespace ETModel
 			try
 			{
 				NetOuterComponent networkComponent = Game.Scene.GetComponent<NetOuterComponent>();
-				for (int i = 0; i < 100; i++)
+				for (int i = 0; i < 1000; i++)
 				{
 					this.TestAsync(networkComponent, ipEndPoint, i);
 				}

+ 0 - 1
Server/Model/Server.Model.csproj

@@ -40,7 +40,6 @@
     <Compile Include="..\..\Unity\Assets\Scripts\Base\Helper\StringHelper.cs" Link="Base\Helper\StringHelper.cs" />
     <Compile Include="..\..\Unity\Assets\Scripts\Base\Helper\TimeHelper.cs" Link="Base\Helper\TimeHelper.cs" />
     <Compile Include="..\..\Unity\Assets\Scripts\Base\Helper\ZipHelper.cs" Link="Base\Helper\ZipHelper.cs" />
-    <Compile Include="..\..\Unity\Assets\Scripts\Base\MultiMap.cs" Link="Base\MultiMap.cs" />
     <Compile Include="..\..\Unity\Assets\Scripts\Base\Object\Component.cs" Link="Base\Object\Component.cs" />
     <Compile Include="..\..\Unity\Assets\Scripts\Base\Object\ComponentFactory.cs" Link="Base\Object\ComponentFactory.cs" />
     <Compile Include="..\..\Unity\Assets\Scripts\Base\Object\ComponentWithId.cs" Link="Base\Object\ComponentWithId.cs" />

+ 8 - 2
Unity/Assets/Scripts/Base/MultiMap.cs

@@ -5,12 +5,13 @@ namespace ETModel
 {
 	public class MultiMap<T, K>
 	{
-		private readonly SortedDictionary<T, List<K>> dictionary = new SortedDictionary<T, List<K>>();
+		// 客户端用SortedList,因为unity用SortedDictionary在取firstkey的时候有gc,再者客户端的插入删除并不多
+		private readonly SortedList<T, List<K>> dictionary = new SortedList<T, List<K>>();
 
 		// 重用list
 		private readonly Queue<List<K>> queue = new Queue<List<K>>();
 
-		public SortedDictionary<T, List<K>> GetDictionary()
+		public SortedList<T, List<K>> GetDictionary()
 		{
 			return this.dictionary;
 		}
@@ -32,6 +33,11 @@ namespace ETModel
 			return this.dictionary.First();
 		}
 
+		public T FirstKey()
+		{
+			return this.dictionary.Keys[0];
+		}
+
 		public int Count
 		{
 			get

+ 23 - 20
Unity/Assets/Scripts/Component/TimerComponent.cs

@@ -33,35 +33,38 @@ namespace ETModel
 
 		public void Update()
 		{
+			if (this.timeId.Count == 0)
+			{
+				return;
+			}
+
 			long timeNow = TimeHelper.Now();
 
-			while (true)
+			timeOutId.Clear();
+
+			while (this.timeId.Count > 0)
 			{
-				if (this.timeId.Count <= 0)
+				long k = this.timeId.FirstKey();
+				if (k > timeNow)
 				{
-					return;
+					break;
 				}
-				var kv = this.timeId.First();
-				if (kv.Key > timeNow)
+				foreach (long ll in this.timeId[k])
 				{
-					break;
+					this.timeOutId.Add(ll);
 				}
+				this.timeId.Remove(k);
+			}
 
-				timeOutId.Clear();
-				timeOutId.AddRange(kv.Value);
-
-				this.timeId.Remove(kv.Key);
-				
-				foreach (long id in timeOutId)
+			foreach (long k in this.timeOutId)
+			{
+				Timer timer;
+				if (!this.timers.TryGetValue(k, out timer))
 				{
-					Timer timer;
-					if (!this.timers.TryGetValue(id, out timer))
-					{
-						continue;
-					}
-					this.timers.Remove(id);
-					timer.tcs.SetResult(true);
-				}				
+					continue;
+				}
+				this.timers.Remove(k);
+				timer.tcs.SetResult(true);
 			}
 		}
 

+ 14 - 40
Unity/Assets/Scripts/Module/FrameSync/ClientFrameComponent.cs

@@ -9,14 +9,16 @@ namespace ETModel
         public FrameMessage FrameMessage;
     }
     
-    [ObjectSystem]
-    public class ClientFrameComponentStartSystem : StartSystem<ClientFrameComponent>
-    {
-	    public override void Start(ClientFrameComponent t)
-	    {
-		    t.Start();
-	    }
-    }
+	[ObjectSystem]
+	public class ClientFrameComponentUpdateSystem : UpdateSystem<ClientFrameComponent>
+	{
+		public override void Update(ClientFrameComponent self)
+		{
+			self.Update();
+		}
+	}
+
+
 	public class ClientFrameComponent: Component
     {
         public int Frame;
@@ -24,47 +26,19 @@ namespace ETModel
         public Queue<SessionFrameMessage> Queue = new Queue<SessionFrameMessage>();
 
         public int count = 1;
-        
-        public int waitTime;
 
-        public const int maxWaitTime = 100;
+	    public int waitTime = 100;
 
-        public void Start()
-        {
-            UpdateAsync();
-        }
+        public const int maxWaitTime = 100;
 
         public void Add(Session session, FrameMessage frameMessage)
         {
             this.Queue.Enqueue(new SessionFrameMessage() {Session = session, FrameMessage = frameMessage});
         }
 
-        public async void UpdateAsync()
-        {
-	        try
-	        {
-		        TimerComponent timerComponent = Game.Scene.GetComponent<TimerComponent>();
-		        while (true)
-		        {
-			        await timerComponent.WaitAsync(waitTime);
-
-			        if (this.IsDisposed)
-			        {
-				        return;
-			        }
-
-			        this.UpdateFrame();
-		        }
-			}
-	        catch (Exception e)
-	        {
-		        Log.Error(e);
-	        }
-        }
-
-        private void UpdateFrame()
+        public void Update()
         {
-            if (this.Queue.Count == 0)
+			if (this.Queue.Count == 0)
             {
                 return;
             }

+ 37 - 21
Unity/Assets/Scripts/Module/Message/Network/KCP/KService.cs

@@ -32,7 +32,9 @@ namespace ETModel
 		private readonly HashSet<long> updateChannels = new HashSet<long>();
 
 		// 下次时间更新的channel
-		private readonly MultiMap<long, long> timerMap = new MultiMap<long, long>();
+		private readonly MultiMap<long, long> timerId = new MultiMap<long, long>();
+
+		private readonly List<long> timeOutId = new List<long>();
 
 		public KService(IPEndPoint ipEndPoint)
 		{
@@ -240,7 +242,7 @@ namespace ETModel
 
 		public void AddToNextTimeUpdate(long time, long id)
 		{
-			this.timerMap.Add(time, id);
+			this.timerId.Add(time, id);
 		}
 
 		public override AChannel GetChannel(long id)
@@ -280,26 +282,8 @@ namespace ETModel
 
 		public override void Update()
 		{
-			this.TimeNow = (uint)TimeHelper.Now();
+			this.TimerOut();
 
-			while (true)
-			{
-				if (this.timerMap.Count <= 0)
-				{
-					break;
-				}
-				var kv = this.timerMap.First();
-				if (kv.Key > TimeNow)
-				{
-					break;
-				}
-				List<long> timeOutId = kv.Value;
-				foreach (long id in timeOutId)
-				{
-					this.updateChannels.Add(id);
-				}
-				this.timerMap.Remove(kv.Key);
-			}
 			foreach (long id in updateChannels)
 			{
 				KChannel kChannel;
@@ -325,5 +309,37 @@ namespace ETModel
 				this.idChannels.Remove(id);
 			}
 		}
+
+		// 计算到期需要update的channel
+		private void TimerOut()
+		{
+			if (this.timerId.Count == 0)
+			{
+				return;
+			}
+
+			this.TimeNow = (uint)TimeHelper.ClientNow();
+
+			timeOutId.Clear();
+
+			while (this.timerId.Count > 0)
+			{
+				long k = this.timerId.FirstKey();
+				if (k > this.TimeNow)
+				{
+					break;
+				}
+				foreach (long ll in this.timerId[k])
+				{
+					this.timeOutId.Add(ll);
+				}
+				this.timerId.Remove(k);
+			}
+
+			foreach (long k in this.timeOutId)
+			{
+				this.updateChannels.Add(k);
+			}
+		}
 	}
 }