Skip to content

Commit 473f592

Browse files
committed
c#에서 네트워크로 받은 데이터를 메모리풀로 관리하는 아이디어
1 parent 768129a commit 473f592

File tree

8 files changed

+218
-0
lines changed

8 files changed

+218
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# 멀티스레드에서의 패킷 분배 전략
2+
3+
## 전제
4+
- 네트워크 처리는 비동기 IO
5+
- 패킷 처리(클라이언트 요청)는 워커스레드
6+
- 패킷 데이터는 세션 별로 할당 되어 있다
7+
- 프로그래밍 언어는 C#
8+
9+
10+
## PacketQueue, 패키 처리 완료 통보
11+
12+
13+
14+
## PacketList, 패키 처리 완료 통보
15+
- 미리 capacity(D)를 설정한다
16+
- 현재 사용 중인 패킷 데이터 개수(C)
17+
- 현재 사용 인덱스(A)가 가리키는 패킷 데이터를 사용한다
18+
- (C)가 (D)와 같으면 빈 패킷 데이터가 없다는 뜻이다
19+
- 현재 사용량 크기(B)
20+
- 패킷 처리 스레드로 보내면 (B)에 `+1` 킨다.
21+
- 통보쪽에서는 알리면 (B)에 `-1` 시킨다.
22+
23+
- 낙관적처리: 통보 없이(빈 데이터가 있는지 조사하지 않고) 로테이션
24+
25+
26+
## 링버퍼, 패키 처리 완료 통보
27+
28+
- 통보쪽에서는 패킷 크기만 알려주면 Int64 타입의 변수에 누적시킨다.
29+
30+
- 낙관적처리: 통보 없이 로테이션
31+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace TestProject;
8+
9+
//GC 최적화를 한다면 외부에서 큰 버퍼를 만들고, 버퍼의 일부를 여기에 할당해서
10+
// 큰 메모리 조각을 나누어서 사용하는 것이 더 좋을 수 있다.
11+
// 이 버퍼는 프로그램 종료까지 사용될테니 처음부터 GC 2세대로 보내거나 pin 하는 것이 더 좋다
12+
public class PacketData
13+
{
14+
public Int32 SessionId { get; private set; }
15+
public Byte[] Data { get; private set; }
16+
17+
public void Init(Int32 dataSize)
18+
{
19+
Data = new Byte[dataSize];
20+
}
21+
22+
public void SetData(Int32 sessionId, Byte[] data)
23+
{
24+
this.SessionId = sessionId;
25+
Buffer.BlockCopy(data, 0, Data, 0, data.Length);
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace TestProject;
8+
9+
public class PacketList
10+
{
11+
Int64 _capacity = 0; // 좀 넉넉한 크기여야 한다
12+
Int64 _useCount = 0;
13+
14+
List<PacketData> _packets = new List<PacketData>();
15+
16+
17+
void Init(Int64 capacity, int maxPacketSize)
18+
{
19+
_capacity = capacity;
20+
21+
for(var i = 0; i < _capacity; i++)
22+
{
23+
var packet = new PacketData();
24+
packet.Init(maxPacketSize);
25+
26+
_packets.Add(packet);
27+
}
28+
}
29+
30+
// 1개의 스레드에서만 호출한다고 가정한다
31+
PacketData? Alloc()
32+
{
33+
if(Interlocked.Read(ref _useCount) == _capacity)
34+
{
35+
return null;
36+
}
37+
38+
var index = _capacity % _useCount;
39+
var packet = _packets[(int)index];
40+
41+
++_useCount;
42+
return packet;
43+
}
44+
45+
void Free(PacketData packet)
46+
{
47+
Interlocked.Decrement(ref _useCount);
48+
}
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System;
2+
using System.Collections.Concurrent;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
8+
namespace TestProject
9+
{
10+
public class PacketQuerue
11+
{
12+
ConcurrentBag<PacketData> packetDatas = new ConcurrentBag<PacketData>();
13+
14+
PacketData Alloc()
15+
{
16+
if(packetDatas.TryTake(out var packetData))
17+
{
18+
return packetData;
19+
}
20+
21+
return null;
22+
}
23+
24+
void Free(PacketData packetData)
25+
{
26+
packetDatas.Add(packetData);
27+
}
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace TestProject;
8+
9+
public class PacketRingBuffer
10+
{
11+
byte[] _buffer;
12+
13+
14+
void Init(Int32 capacity)
15+
{
16+
_buffer = new byte[capacity];
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
8+
<IsPackable>false</IsPackable>
9+
<IsTestProject>true</IsTestProject>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<PackageReference Include="coverlet.collector" Version="6.0.0" />
14+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
15+
<PackageReference Include="NUnit" Version="3.14.0" />
16+
<PackageReference Include="NUnit.Analyzers" Version="3.9.0" />
17+
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
18+
</ItemGroup>
19+
20+
<ItemGroup>
21+
<Using Include="NUnit.Framework" />
22+
</ItemGroup>
23+
24+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.9.34701.34
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProject", "TestProject.csproj", "{18752DA1-D12F-441C-B60A-AFE4989117D1}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{18752DA1-D12F-441C-B60A-AFE4989117D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{18752DA1-D12F-441C-B60A-AFE4989117D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{18752DA1-D12F-441C-B60A-AFE4989117D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{18752DA1-D12F-441C-B60A-AFE4989117D1}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {9F58F0E0-0AC5-4B4A-8A4B-5A3891CDE6BA}
24+
EndGlobalSection
25+
EndGlobal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace TestProject;
2+
3+
public class Tests
4+
{
5+
[SetUp]
6+
public void Setup()
7+
{
8+
}
9+
10+
[Test]
11+
public void Test1()
12+
{
13+
Assert.Pass();
14+
}
15+
}

0 commit comments

Comments
 (0)