성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
[정성태] 만드실 수 있습니다. 단지, Unity 엔진 내의 스크립트와 W...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# - Span<T>와 Memory<T></h1> <p> Span<T>에 대해서는 전에,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# 7.2 - Span<T> ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/11534'>https://www.sysnet.pe.kr/2/0/11534</a> C# - System.Span<T> 성능 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/11535'>https://www.sysnet.pe.kr/2/0/11535</a> </pre> <br /> 소개한 적이 있으니, 이번엔 Memory<T>를<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Memory<T> Struct ; <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.memory-1'>https://learn.microsoft.com/en-us/dotnet/api/system.memory-1</a> </pre> <br /> 추가해 설명하겠습니다. 우선 성능을 볼 텐데, (최소 지원 버전인) .NET Framework 4.5 + Nuget System.Memory 4.5.2로 구성해,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Diagnostics; namespace ConsoleApp1 { class Program { static void Main(string[] args) { Action<int, string, Action<byte[]>, byte[]> action = (loopCount, title, work, arg) => { Stopwatch st = new Stopwatch(); st.Start(); Random rand = new Random(Environment.TickCount); for (int i = 0; i < loopCount; i++) { work(arg); } st.Stop(); Console.WriteLine(title + " : " + st.ElapsedMilliseconds); }; byte[] buf = new byte[1]; action(1, "touch-JIT", ForLoop, buf); action(1, "touch-JIT", MemoryLoop, buf); action(1, "touch-JIT", PtrLoop, buf); Console.WriteLine(); buf = new byte[10000]; action(100000, "ForLoop", ForLoop, buf); <span style='color: blue; font-weight: bold'>action(100000, "MemoryLoop", MemoryLoop, buf);</span> action(100000, "PtrLoop", PtrLoop, buf); } static void ForLoop(byte[] buffer) { int sum = 0; for (int i = 0; i < buffer.Length; i++) { sum += buffer[i]; } } static void MemoryLoop(byte[] buffer) { Memory<byte> memory = buffer; int sum = 0; for (int i = 0; i < memory.Length; i++) { sum += <span style='color: blue; font-weight: bold'>memory.Span[i];</span> } } static unsafe void PtrLoop(byte[] buffer) { int sum = 0; fixed (byte* ptr = buffer) { for (int i = 0; i < buffer.Length; i++) { sum += *(ptr + i); } } } } } </pre> <br /> 실행하면 이런 결과를 얻습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // .NET 4.5 + Release ForLoop : 708 <span style='color: blue; font-weight: bold'>MemoryLoop : 6822</span> PtrLoop : 569 // .NET Core 2.1 + Release ForLoop : 597 <span style='color: blue; font-weight: bold'>MemoryLoop : 6044</span> PtrLoop : 466 </pre> <br /> 보다시피 Memory<T>의 성능은 일반적인 배열과 비교해 약 10배 정도 느립니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 하지만, 그렇다고 해서 Memory<T>에 대해 크게 실망할 필요는 없습니다. 왜냐하면, 사실 Memory<T>.Span 속성은 Span<T> 타입인데 이를 가볍게 캐시만 해서 사용하는 코드로 바꾸면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > static int MemorySpanLoop(byte[] buffer) { Memory<byte> memory = buffer; <span style='color: blue; font-weight: bold'>Span<byte> span = memory.Span;</span> int sum = 0; for (int i = 0; i < span.Length; i++) { sum += span[i]; } return sum; } </pre> <br /> 이번엔 다음과 같은 결과를 확인할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // .NET 4.5 + Release ForLoop : 623 MemoryLoop : 6095 <span style='color: blue; font-weight: bold'>MemorySpanLoop : 907</span> PtrLoop : 434 // .NET Core 2.1 + Release ForLoop : 540 MemoryLoop : 10770 <span style='color: blue; font-weight: bold'>MemorySpanLoop : 440</span> PtrLoop : 428 </pre> <br /> 거의 <a target='tab' href='https://www.sysnet.pe.kr/2/0/11535'>Span<T></a>와 다름없는 속도입니다.<br /> <br /> (결과에서 유추해 보면, 관리 포인터로 인한 혜택은 (907 - 440) 정도의 속도 차이만 나고, 그 외의 성능 손실은 Memory<T>.Span 속성이 <a target='tab' href='https://www.sysnet.pe.kr/2/0/1545'>단순히 내부의 변수 하나를 반환하는 것이 아닌, 복잡한 코드를 포함하고 있기 때문에 그것 자체의 메서드 처리</a>가 문제였을 것입니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그나저나, Memory<T> 타입과 Span<T> 타입의 차이점이 뭘까요? "<a target='tab' href='https://www.sysnet.pe.kr/2/0/11534'>C# 7.2 - Span<T></a>" 글에서 Span은 "ref struct"이기 때문에 스택에만 생성할 수 있다고 했습니다. 즉, 다른 타입의 필드로 Span<T>를 정의할 수 없습니다. 반면, Memory<T>는 그냥 struct이기 때문에 관리 힙에도 위치할 수 있으므로 Span<T>와 같은 제약이 없습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > class MyType { // 컴파일 오류 // Error CS8345 Field or auto-implemented property cannot be of type 'Span<byte>' unless it is an instance member of a ref struct public <span style='color: blue; font-weight: bold'>Span<byte></span> ByteBuffer; // 사용 가능 public <span style='color: blue; font-weight: bold'>Memory<byte></span> MemoryBuffer; } </pre> <br /> 따라서, 사용 원칙은 간단합니다. 1) 평소에는 성능을 위해 Span<T>를 사용하고, 2) 간혹 해당 버퍼를 다른 타입의 필드로 들고 있어야 할 때 Memory<T>를 사용하다가, 3) 다시 그것을 접근해야 할 때는 Span<T>로 캐시해 사용하는 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > { byte[] buffer = new byte[1000]; MyType type = new MyType(); <span style='color: blue; font-weight: bold'>type.MemoryBuffer = buffer</span>; // 필드에 들고 있어야 할 때는 Memory<T>로. // 그 필드를 다시 사용해야 할 때는 Span<T>로. <span style='color: blue; font-weight: bold'>Span<byte> fastBuf = type.MemoryBuffer.Span;</span> for (int i = 0; i < fastBuf.Length; i ++) { // ... } } </pre> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1696&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1124
(왼쪽의 숫자를 입력해야 합니다.)