diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index bb29ddf..9d4b4c6 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -26,12 +26,12 @@ jobs: create_nuget: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 # Get all history to allow automatic versioning - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v0 + uses: gittools/actions/gitversion/setup@v1 with: versionSpec: '5.x' includePrerelease: true @@ -39,10 +39,10 @@ jobs: - name: Determine Version id: gitversion - uses: gittools/actions/gitversion/execute@v0 + uses: gittools/actions/gitversion/execute@v1 - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 - run: > dotnet pack @@ -53,7 +53,7 @@ jobs: /p:PackageVersion=${{ steps.gitversion.outputs.semVer }} --output ${{ env.NuGetDirectory }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: nuget if-no-files-found: error @@ -65,11 +65,13 @@ jobs: run_test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 + - name: Build + run: dotnet build --configuration Release -v q - name: Run tests - run: dotnet test --configuration Release + run: dotnet test --configuration Release --no-build --verbosity normal --logger GitHubActions deploy: # Publish only when creating a GitHub Release @@ -79,13 +81,13 @@ jobs: runs-on: ubuntu-latest needs: [ create_nuget, run_test ] steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: nuget path: ${{ env.NuGetDirectory }} - name: Setup .NET Core - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 # Publish all NuGet packages to NuGet.org # Use --skip-duplicate to prevent errors if a package with the same version already exists. diff --git a/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization-report-github.md b/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization-report-github.md new file mode 100644 index 0000000..643cd0b --- /dev/null +++ b/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization-report-github.md @@ -0,0 +1,17 @@ +``` ini + +BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.3086/22H2/2022Update) +Intel Core i9-9900K CPU 3.60GHz (Coffee Lake), 1 CPU, 16 logical and 8 physical cores +.NET SDK=7.0.302 + [Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 + Job-RMFIHR : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 + +Runtime=.NET 7.0 Toolchain=net70 + +``` +| Method | Mean | Error | StdDev | Code Size | Allocated | +|----------------------------------------- |---------:|----------:|----------:|----------:|----------:| +| MemoryMarshal_Cast | 6.517 ns | 0.1357 ns | 0.1270 ns | 395 B | - | +| Unsafe_WriteUnaligned | 4.462 ns | 0.1186 ns | 0.1543 ns | 145 B | - | +| Unsafe_WriteUnaligned_With_Optimizations | 2.547 ns | 0.0678 ns | 0.0634 ns | 116 B | - | +| Unsafe_As_Struct | 6.453 ns | 0.1559 ns | 0.1382 ns | 220 B | - | diff --git a/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializeArray.SerializeByte-asm.md b/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializeArray.SerializeByte-asm.md new file mode 100644 index 0000000..2cd582a --- /dev/null +++ b/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializeArray.SerializeByte-asm.md @@ -0,0 +1,1880 @@ +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.SpanCopyTo() + push rdi + push rsi + push rbp + push rbx + sub rsp,28 + mov rsi,rcx + mov rcx,[rsi+10] + test rcx,rcx + je short M00_L02 + lea rdx,[rcx+10] + mov edi,[rcx+8] +M00_L00: + mov rcx,[rsi+8] + test rcx,rcx + je short M00_L03 + lea rbx,[rcx+10] + mov ebp,[rcx+8] +M00_L01: + mov rcx,rbx + cmp edi,ebp + ja short M00_L04 + mov r8d,edi + call qword ptr [7FFEC33699F0]; System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr) + mov rax,[rsi+10] + mov eax,[rax+8] + add rsp,28 + pop rbx + pop rbp + pop rsi + pop rdi + ret +M00_L02: + xor edx,edx + xor edi,edi + jmp short M00_L00 +M00_L03: + xor ebx,ebx + xor ebp,ebp + jmp short M00_L01 +M00_L04: + call qword ptr [7FFEC359B4B0] + int 3 +; Total bytes of code 94 +``` +```assembly +; System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr) + vzeroupper + mov rax,rcx + sub rax,rdx + cmp rax,r8 + jae short M01_L01 +M01_L00: + cmp rcx,rdx + je near ptr M01_L09 + jmp near ptr M01_L11 +M01_L01: + mov rax,rdx + sub rax,rcx + cmp rax,r8 + jb short M01_L00 + lea rax,[rdx+r8] + lea r9,[rcx+r8] + cmp r8,10 + jbe short M01_L04 + cmp r8,40 + ja near ptr M01_L07 +M01_L02: + vmovupd xmm0,[rdx] + vmovupd [rcx],xmm0 + cmp r8,20 + jbe short M01_L03 + vmovupd xmm0,[rdx+10] + vmovupd [rcx+10],xmm0 + cmp r8,30 + jbe short M01_L03 + vmovupd xmm0,[rdx+20] + vmovupd [rcx+20],xmm0 +M01_L03: + vmovupd xmm0,[rax-10] + vmovupd [r9-10],xmm0 + jmp short M01_L09 +M01_L04: + test r8b,18 + je short M01_L05 + mov r8,[rdx] + mov [rcx],r8 + mov rdx,[rax-8] + mov [r9-8],rdx + jmp short M01_L09 +M01_L05: + test r8b,4 + je short M01_L06 + mov r8d,[rdx] + mov [rcx],r8d + mov edx,[rax-4] + mov [r9-4],edx + jmp short M01_L09 +M01_L06: + test r8,r8 + je short M01_L09 + movzx edx,byte ptr [rdx] + mov [rcx],dl + test r8b,2 + je short M01_L09 + movsx r8,word ptr [rax-2] + mov [r9-2],r8w + jmp short M01_L09 +M01_L07: + cmp r8,800 + ja short M01_L11 + mov r10,r8 + shr r10,6 +M01_L08: + vmovdqu ymm0,ymmword ptr [rdx] + vmovdqu ymmword ptr [rcx],ymm0 + vmovdqu ymm0,ymmword ptr [rdx+20] + vmovdqu ymmword ptr [rcx+20],ymm0 + add rcx,40 + add rdx,40 + dec r10 + je short M01_L10 + jmp short M01_L08 +M01_L09: + ret +M01_L10: + and r8,3F + cmp r8,10 + ja near ptr M01_L02 + vmovupd xmm0,[rax-10] + vmovupd [r9-10],xmm0 + jmp short M01_L09 +M01_L11: + jmp qword ptr [7FFEC3369A08]; System.Buffer._Memmove(Byte ByRef, Byte ByRef, UIntPtr) +; Total bytes of code 270 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyUsingLargerPrimitives() + push rdi + push rsi + sub rsp,48 + xor eax,eax + mov [rsp+28],rax + vxorps xmm4,xmm4,xmm4 + vmovdqa xmmword ptr [rsp+30],xmm4 + mov [rsp+40],rax + mov rdx,[rcx+10] + test rdx,rdx + je short M00_L02 + lea rax,[rdx+10] + mov r8d,[rdx+8] +M00_L00: + mov rcx,[rcx+8] + test rcx,rcx + je short M00_L03 + lea rsi,[rcx+10] + mov edi,[rcx+8] +M00_L01: + mov [rsp+38],rax + mov [rsp+40],r8d + mov [rsp+28],rsi + mov [rsp+30],edi + lea rcx,[rsp+38] + lea rdx,[rsp+28] + call qword ptr [7FFEC35D7948]; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyBytes(System.ReadOnlySpan`1, System.Span`1) + nop + add rsp,48 + pop rsi + pop rdi + ret +M00_L02: + xor eax,eax + xor r8d,r8d + jmp short M00_L00 +M00_L03: + xor esi,esi + xor edi,edi + jmp short M00_L01 +; Total bytes of code 117 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyBytes(System.ReadOnlySpan`1, System.Span`1) + mov rax,[rcx] + mov ecx,[rcx+8] + mov rdx,[rdx] + xor r8d,r8d + lea r9d,[rcx-8] + movsxd r9,r9d + test r9,r9 + jl short M01_L01 + nop dword ptr [rax+rax] +M01_L00: + mov r10d,r8d + mov r11,[rax+r10] + mov [rdx+r10],r11 + add r8d,8 + mov r10d,r8d + cmp r10,r9 + jle short M01_L00 +M01_L01: + mov r9d,r8d + lea r10d,[rcx-4] + movsxd r10,r10d + cmp r9,r10 + jg short M01_L02 + mov r10d,[rax+r9] + mov [rdx+r9],r10d + add r8d,4 +M01_L02: + mov r9d,r8d + lea r10d,[rcx-2] + movsxd r10,r10d + cmp r9,r10 + jg short M01_L03 + movzx r10d,word ptr [rax+r9] + mov [rdx+r9],r10w + add r8d,2 +M01_L03: + mov r9d,r8d + movsxd rcx,ecx + cmp r9,rcx + jge short M01_L04 + movzx eax,byte ptr [rax+r9] + mov [rdx+r9],al + inc r8d +M01_L04: + mov eax,r8d + ret +; Total bytes of code 138 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.SpanCopyTo() + push rdi + push rsi + push rbp + push rbx + sub rsp,28 + mov rsi,rcx + mov rcx,[rsi+10] + test rcx,rcx + je short M00_L02 + lea rdx,[rcx+10] + mov edi,[rcx+8] +M00_L00: + mov rcx,[rsi+8] + test rcx,rcx + je short M00_L03 + lea rbx,[rcx+10] + mov ebp,[rcx+8] +M00_L01: + mov rcx,rbx + cmp edi,ebp + ja short M00_L04 + mov r8d,edi + call qword ptr [7FFEC33999F0]; System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr) + mov rax,[rsi+10] + mov eax,[rax+8] + add rsp,28 + pop rbx + pop rbp + pop rsi + pop rdi + ret +M00_L02: + xor edx,edx + xor edi,edi + jmp short M00_L00 +M00_L03: + xor ebx,ebx + xor ebp,ebp + jmp short M00_L01 +M00_L04: + call qword ptr [7FFEC35CB4B0] + int 3 +; Total bytes of code 94 +``` +```assembly +; System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr) + vzeroupper + mov rax,rcx + sub rax,rdx + cmp rax,r8 + jae short M01_L01 +M01_L00: + cmp rcx,rdx + je near ptr M01_L09 + jmp near ptr M01_L11 +M01_L01: + mov rax,rdx + sub rax,rcx + cmp rax,r8 + jb short M01_L00 + lea rax,[rdx+r8] + lea r9,[rcx+r8] + cmp r8,10 + jbe short M01_L04 + cmp r8,40 + ja near ptr M01_L07 +M01_L02: + vmovupd xmm0,[rdx] + vmovupd [rcx],xmm0 + cmp r8,20 + jbe short M01_L03 + vmovupd xmm0,[rdx+10] + vmovupd [rcx+10],xmm0 + cmp r8,30 + jbe short M01_L03 + vmovupd xmm0,[rdx+20] + vmovupd [rcx+20],xmm0 +M01_L03: + vmovupd xmm0,[rax-10] + vmovupd [r9-10],xmm0 + jmp short M01_L09 +M01_L04: + test r8b,18 + je short M01_L05 + mov r8,[rdx] + mov [rcx],r8 + mov rdx,[rax-8] + mov [r9-8],rdx + jmp short M01_L09 +M01_L05: + test r8b,4 + je short M01_L06 + mov r8d,[rdx] + mov [rcx],r8d + mov edx,[rax-4] + mov [r9-4],edx + jmp short M01_L09 +M01_L06: + test r8,r8 + je short M01_L09 + movzx edx,byte ptr [rdx] + mov [rcx],dl + test r8b,2 + je short M01_L09 + movsx r8,word ptr [rax-2] + mov [r9-2],r8w + jmp short M01_L09 +M01_L07: + cmp r8,800 + ja short M01_L11 + mov r10,r8 + shr r10,6 +M01_L08: + vmovdqu ymm0,ymmword ptr [rdx] + vmovdqu ymmword ptr [rcx],ymm0 + vmovdqu ymm0,ymmword ptr [rdx+20] + vmovdqu ymmword ptr [rcx+20],ymm0 + add rcx,40 + add rdx,40 + dec r10 + je short M01_L10 + jmp short M01_L08 +M01_L09: + ret +M01_L10: + and r8,3F + cmp r8,10 + ja near ptr M01_L02 + vmovupd xmm0,[rax-10] + vmovupd [r9-10],xmm0 + jmp short M01_L09 +M01_L11: + jmp qword ptr [7FFEC3399A08]; System.Buffer._Memmove(Byte ByRef, Byte ByRef, UIntPtr) +; Total bytes of code 270 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyUsingLargerPrimitives() + push rdi + push rsi + sub rsp,48 + xor eax,eax + mov [rsp+28],rax + vxorps xmm4,xmm4,xmm4 + vmovdqa xmmword ptr [rsp+30],xmm4 + mov [rsp+40],rax + mov rdx,[rcx+10] + test rdx,rdx + je short M00_L02 + lea rax,[rdx+10] + mov r8d,[rdx+8] +M00_L00: + mov rcx,[rcx+8] + test rcx,rcx + je short M00_L03 + lea rsi,[rcx+10] + mov edi,[rcx+8] +M00_L01: + mov [rsp+38],rax + mov [rsp+40],r8d + mov [rsp+28],rsi + mov [rsp+30],edi + lea rcx,[rsp+38] + lea rdx,[rsp+28] + call qword ptr [7FFEC35D7948]; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyBytes(System.ReadOnlySpan`1, System.Span`1) + nop + add rsp,48 + pop rsi + pop rdi + ret +M00_L02: + xor eax,eax + xor r8d,r8d + jmp short M00_L00 +M00_L03: + xor esi,esi + xor edi,edi + jmp short M00_L01 +; Total bytes of code 117 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyBytes(System.ReadOnlySpan`1, System.Span`1) + mov rax,[rcx] + mov ecx,[rcx+8] + mov rdx,[rdx] + xor r8d,r8d + lea r9d,[rcx-8] + movsxd r9,r9d + test r9,r9 + jl short M01_L01 + nop dword ptr [rax+rax] +M01_L00: + mov r10d,r8d + mov r11,[rax+r10] + mov [rdx+r10],r11 + add r8d,8 + mov r10d,r8d + cmp r10,r9 + jle short M01_L00 +M01_L01: + mov r9d,r8d + lea r10d,[rcx-4] + movsxd r10,r10d + cmp r9,r10 + jg short M01_L02 + mov r10d,[rax+r9] + mov [rdx+r9],r10d + add r8d,4 +M01_L02: + mov r9d,r8d + lea r10d,[rcx-2] + movsxd r10,r10d + cmp r9,r10 + jg short M01_L03 + movzx r10d,word ptr [rax+r9] + mov [rdx+r9],r10w + add r8d,2 +M01_L03: + mov r9d,r8d + movsxd rcx,ecx + cmp r9,rcx + jge short M01_L04 + movzx eax,byte ptr [rax+r9] + mov [rdx+r9],al + inc r8d +M01_L04: + mov eax,r8d + ret +; Total bytes of code 138 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.SpanCopyTo() + push rdi + push rsi + push rbp + push rbx + sub rsp,28 + mov rsi,rcx + mov rcx,[rsi+10] + test rcx,rcx + je short M00_L02 + lea rdx,[rcx+10] + mov edi,[rcx+8] +M00_L00: + mov rcx,[rsi+8] + test rcx,rcx + je short M00_L03 + lea rbx,[rcx+10] + mov ebp,[rcx+8] +M00_L01: + mov rcx,rbx + cmp edi,ebp + ja short M00_L04 + mov r8d,edi + call qword ptr [7FFEC33899F0]; System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr) + mov rax,[rsi+10] + mov eax,[rax+8] + add rsp,28 + pop rbx + pop rbp + pop rsi + pop rdi + ret +M00_L02: + xor edx,edx + xor edi,edi + jmp short M00_L00 +M00_L03: + xor ebx,ebx + xor ebp,ebp + jmp short M00_L01 +M00_L04: + call qword ptr [7FFEC35BB4B0] + int 3 +; Total bytes of code 94 +``` +```assembly +; System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr) + vzeroupper + mov rax,rcx + sub rax,rdx + cmp rax,r8 + jae short M01_L01 +M01_L00: + cmp rcx,rdx + je near ptr M01_L09 + jmp near ptr M01_L11 +M01_L01: + mov rax,rdx + sub rax,rcx + cmp rax,r8 + jb short M01_L00 + lea rax,[rdx+r8] + lea r9,[rcx+r8] + cmp r8,10 + jbe short M01_L04 + cmp r8,40 + ja near ptr M01_L07 +M01_L02: + vmovupd xmm0,[rdx] + vmovupd [rcx],xmm0 + cmp r8,20 + jbe short M01_L03 + vmovupd xmm0,[rdx+10] + vmovupd [rcx+10],xmm0 + cmp r8,30 + jbe short M01_L03 + vmovupd xmm0,[rdx+20] + vmovupd [rcx+20],xmm0 +M01_L03: + vmovupd xmm0,[rax-10] + vmovupd [r9-10],xmm0 + jmp short M01_L09 +M01_L04: + test r8b,18 + je short M01_L05 + mov r8,[rdx] + mov [rcx],r8 + mov rdx,[rax-8] + mov [r9-8],rdx + jmp short M01_L09 +M01_L05: + test r8b,4 + je short M01_L06 + mov r8d,[rdx] + mov [rcx],r8d + mov edx,[rax-4] + mov [r9-4],edx + jmp short M01_L09 +M01_L06: + test r8,r8 + je short M01_L09 + movzx edx,byte ptr [rdx] + mov [rcx],dl + test r8b,2 + je short M01_L09 + movsx r8,word ptr [rax-2] + mov [r9-2],r8w + jmp short M01_L09 +M01_L07: + cmp r8,800 + ja short M01_L11 + mov r10,r8 + shr r10,6 +M01_L08: + vmovdqu ymm0,ymmword ptr [rdx] + vmovdqu ymmword ptr [rcx],ymm0 + vmovdqu ymm0,ymmword ptr [rdx+20] + vmovdqu ymmword ptr [rcx+20],ymm0 + add rcx,40 + add rdx,40 + dec r10 + je short M01_L10 + jmp short M01_L08 +M01_L09: + ret +M01_L10: + and r8,3F + cmp r8,10 + ja near ptr M01_L02 + vmovupd xmm0,[rax-10] + vmovupd [r9-10],xmm0 + jmp short M01_L09 +M01_L11: + jmp qword ptr [7FFEC3389A08]; System.Buffer._Memmove(Byte ByRef, Byte ByRef, UIntPtr) +; Total bytes of code 270 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyUsingLargerPrimitives() + push rdi + push rsi + sub rsp,48 + xor eax,eax + mov [rsp+28],rax + vxorps xmm4,xmm4,xmm4 + vmovdqa xmmword ptr [rsp+30],xmm4 + mov [rsp+40],rax + mov rdx,[rcx+10] + test rdx,rdx + je short M00_L02 + lea rax,[rdx+10] + mov r8d,[rdx+8] +M00_L00: + mov rcx,[rcx+8] + test rcx,rcx + je short M00_L03 + lea rsi,[rcx+10] + mov edi,[rcx+8] +M00_L01: + mov [rsp+38],rax + mov [rsp+40],r8d + mov [rsp+28],rsi + mov [rsp+30],edi + lea rcx,[rsp+38] + lea rdx,[rsp+28] + call qword ptr [7FFEC35E7948]; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyBytes(System.ReadOnlySpan`1, System.Span`1) + nop + add rsp,48 + pop rsi + pop rdi + ret +M00_L02: + xor eax,eax + xor r8d,r8d + jmp short M00_L00 +M00_L03: + xor esi,esi + xor edi,edi + jmp short M00_L01 +; Total bytes of code 117 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyBytes(System.ReadOnlySpan`1, System.Span`1) + mov rax,[rcx] + mov ecx,[rcx+8] + mov rdx,[rdx] + xor r8d,r8d + lea r9d,[rcx-8] + movsxd r9,r9d + test r9,r9 + jl short M01_L01 + nop dword ptr [rax+rax] +M01_L00: + mov r10d,r8d + mov r11,[rax+r10] + mov [rdx+r10],r11 + add r8d,8 + mov r10d,r8d + cmp r10,r9 + jle short M01_L00 +M01_L01: + mov r9d,r8d + lea r10d,[rcx-4] + movsxd r10,r10d + cmp r9,r10 + jg short M01_L02 + mov r10d,[rax+r9] + mov [rdx+r9],r10d + add r8d,4 +M01_L02: + mov r9d,r8d + lea r10d,[rcx-2] + movsxd r10,r10d + cmp r9,r10 + jg short M01_L03 + movzx r10d,word ptr [rax+r9] + mov [rdx+r9],r10w + add r8d,2 +M01_L03: + mov r9d,r8d + movsxd rcx,ecx + cmp r9,rcx + jge short M01_L04 + movzx eax,byte ptr [rax+r9] + mov [rdx+r9],al + inc r8d +M01_L04: + mov eax,r8d + ret +; Total bytes of code 138 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.SpanCopyTo() + push rdi + push rsi + push rbp + push rbx + sub rsp,28 + mov rsi,rcx + mov rcx,[rsi+10] + test rcx,rcx + je short M00_L02 + lea rdx,[rcx+10] + mov edi,[rcx+8] +M00_L00: + mov rcx,[rsi+8] + test rcx,rcx + je short M00_L03 + lea rbx,[rcx+10] + mov ebp,[rcx+8] +M00_L01: + mov rcx,rbx + cmp edi,ebp + ja short M00_L04 + mov r8d,edi + call qword ptr [7FFEC33699F0]; System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr) + mov rax,[rsi+10] + mov eax,[rax+8] + add rsp,28 + pop rbx + pop rbp + pop rsi + pop rdi + ret +M00_L02: + xor edx,edx + xor edi,edi + jmp short M00_L00 +M00_L03: + xor ebx,ebx + xor ebp,ebp + jmp short M00_L01 +M00_L04: + call qword ptr [7FFEC359B4B0] + int 3 +; Total bytes of code 94 +``` +```assembly +; System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr) + vzeroupper + mov rax,rcx + sub rax,rdx + cmp rax,r8 + jae short M01_L01 +M01_L00: + cmp rcx,rdx + je near ptr M01_L09 + jmp near ptr M01_L11 +M01_L01: + mov rax,rdx + sub rax,rcx + cmp rax,r8 + jb short M01_L00 + lea rax,[rdx+r8] + lea r9,[rcx+r8] + cmp r8,10 + jbe short M01_L04 + cmp r8,40 + ja near ptr M01_L07 +M01_L02: + vmovupd xmm0,[rdx] + vmovupd [rcx],xmm0 + cmp r8,20 + jbe short M01_L03 + vmovupd xmm0,[rdx+10] + vmovupd [rcx+10],xmm0 + cmp r8,30 + jbe short M01_L03 + vmovupd xmm0,[rdx+20] + vmovupd [rcx+20],xmm0 +M01_L03: + vmovupd xmm0,[rax-10] + vmovupd [r9-10],xmm0 + jmp short M01_L09 +M01_L04: + test r8b,18 + je short M01_L05 + mov r8,[rdx] + mov [rcx],r8 + mov rdx,[rax-8] + mov [r9-8],rdx + jmp short M01_L09 +M01_L05: + test r8b,4 + je short M01_L06 + mov r8d,[rdx] + mov [rcx],r8d + mov edx,[rax-4] + mov [r9-4],edx + jmp short M01_L09 +M01_L06: + test r8,r8 + je short M01_L09 + movzx edx,byte ptr [rdx] + mov [rcx],dl + test r8b,2 + je short M01_L09 + movsx r8,word ptr [rax-2] + mov [r9-2],r8w + jmp short M01_L09 +M01_L07: + cmp r8,800 + ja short M01_L11 + mov r10,r8 + shr r10,6 +M01_L08: + vmovdqu ymm0,ymmword ptr [rdx] + vmovdqu ymmword ptr [rcx],ymm0 + vmovdqu ymm0,ymmword ptr [rdx+20] + vmovdqu ymmword ptr [rcx+20],ymm0 + add rcx,40 + add rdx,40 + dec r10 + je short M01_L10 + jmp short M01_L08 +M01_L09: + ret +M01_L10: + and r8,3F + cmp r8,10 + ja near ptr M01_L02 + vmovupd xmm0,[rax-10] + vmovupd [r9-10],xmm0 + jmp short M01_L09 +M01_L11: + jmp qword ptr [7FFEC3369A08]; System.Buffer._Memmove(Byte ByRef, Byte ByRef, UIntPtr) +; Total bytes of code 270 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyUsingLargerPrimitives() + push rdi + push rsi + sub rsp,48 + xor eax,eax + mov [rsp+28],rax + vxorps xmm4,xmm4,xmm4 + vmovdqa xmmword ptr [rsp+30],xmm4 + mov [rsp+40],rax + mov rdx,[rcx+10] + test rdx,rdx + je short M00_L02 + lea rax,[rdx+10] + mov r8d,[rdx+8] +M00_L00: + mov rcx,[rcx+8] + test rcx,rcx + je short M00_L03 + lea rsi,[rcx+10] + mov edi,[rcx+8] +M00_L01: + mov [rsp+38],rax + mov [rsp+40],r8d + mov [rsp+28],rsi + mov [rsp+30],edi + lea rcx,[rsp+38] + lea rdx,[rsp+28] + call qword ptr [7FFEC35F7948]; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyBytes(System.ReadOnlySpan`1, System.Span`1) + nop + add rsp,48 + pop rsi + pop rdi + ret +M00_L02: + xor eax,eax + xor r8d,r8d + jmp short M00_L00 +M00_L03: + xor esi,esi + xor edi,edi + jmp short M00_L01 +; Total bytes of code 117 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyBytes(System.ReadOnlySpan`1, System.Span`1) + mov rax,[rcx] + mov ecx,[rcx+8] + mov rdx,[rdx] + xor r8d,r8d + lea r9d,[rcx-8] + movsxd r9,r9d + test r9,r9 + jl short M01_L01 + nop dword ptr [rax+rax] +M01_L00: + mov r10d,r8d + mov r11,[rax+r10] + mov [rdx+r10],r11 + add r8d,8 + mov r10d,r8d + cmp r10,r9 + jle short M01_L00 +M01_L01: + mov r9d,r8d + lea r10d,[rcx-4] + movsxd r10,r10d + cmp r9,r10 + jg short M01_L02 + mov r10d,[rax+r9] + mov [rdx+r9],r10d + add r8d,4 +M01_L02: + mov r9d,r8d + lea r10d,[rcx-2] + movsxd r10,r10d + cmp r9,r10 + jg short M01_L03 + movzx r10d,word ptr [rax+r9] + mov [rdx+r9],r10w + add r8d,2 +M01_L03: + mov r9d,r8d + movsxd rcx,ecx + cmp r9,rcx + jge short M01_L04 + movzx eax,byte ptr [rax+r9] + mov [rdx+r9],al + inc r8d +M01_L04: + mov eax,r8d + ret +; Total bytes of code 138 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.SpanCopyTo() + push rdi + push rsi + push rbp + push rbx + sub rsp,28 + mov rsi,rcx + mov rcx,[rsi+10] + test rcx,rcx + je short M00_L02 + lea rdx,[rcx+10] + mov edi,[rcx+8] +M00_L00: + mov rcx,[rsi+8] + test rcx,rcx + je short M00_L03 + lea rbx,[rcx+10] + mov ebp,[rcx+8] +M00_L01: + mov rcx,rbx + cmp edi,ebp + ja short M00_L04 + mov r8d,edi + call qword ptr [7FFEC33799F0]; System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr) + mov rax,[rsi+10] + mov eax,[rax+8] + add rsp,28 + pop rbx + pop rbp + pop rsi + pop rdi + ret +M00_L02: + xor edx,edx + xor edi,edi + jmp short M00_L00 +M00_L03: + xor ebx,ebx + xor ebp,ebp + jmp short M00_L01 +M00_L04: + call qword ptr [7FFEC35AB4B0] + int 3 +; Total bytes of code 94 +``` +```assembly +; System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr) + vzeroupper + mov rax,rcx + sub rax,rdx + cmp rax,r8 + jae short M01_L01 +M01_L00: + cmp rcx,rdx + je near ptr M01_L09 + jmp near ptr M01_L11 +M01_L01: + mov rax,rdx + sub rax,rcx + cmp rax,r8 + jb short M01_L00 + lea rax,[rdx+r8] + lea r9,[rcx+r8] + cmp r8,10 + jbe short M01_L04 + cmp r8,40 + ja near ptr M01_L07 +M01_L02: + vmovupd xmm0,[rdx] + vmovupd [rcx],xmm0 + cmp r8,20 + jbe short M01_L03 + vmovupd xmm0,[rdx+10] + vmovupd [rcx+10],xmm0 + cmp r8,30 + jbe short M01_L03 + vmovupd xmm0,[rdx+20] + vmovupd [rcx+20],xmm0 +M01_L03: + vmovupd xmm0,[rax-10] + vmovupd [r9-10],xmm0 + jmp short M01_L09 +M01_L04: + test r8b,18 + je short M01_L05 + mov r8,[rdx] + mov [rcx],r8 + mov rdx,[rax-8] + mov [r9-8],rdx + jmp short M01_L09 +M01_L05: + test r8b,4 + je short M01_L06 + mov r8d,[rdx] + mov [rcx],r8d + mov edx,[rax-4] + mov [r9-4],edx + jmp short M01_L09 +M01_L06: + test r8,r8 + je short M01_L09 + movzx edx,byte ptr [rdx] + mov [rcx],dl + test r8b,2 + je short M01_L09 + movsx r8,word ptr [rax-2] + mov [r9-2],r8w + jmp short M01_L09 +M01_L07: + cmp r8,800 + ja short M01_L11 + mov r10,r8 + shr r10,6 +M01_L08: + vmovdqu ymm0,ymmword ptr [rdx] + vmovdqu ymmword ptr [rcx],ymm0 + vmovdqu ymm0,ymmword ptr [rdx+20] + vmovdqu ymmword ptr [rcx+20],ymm0 + add rcx,40 + add rdx,40 + dec r10 + je short M01_L10 + jmp short M01_L08 +M01_L09: + ret +M01_L10: + and r8,3F + cmp r8,10 + ja near ptr M01_L02 + vmovupd xmm0,[rax-10] + vmovupd [r9-10],xmm0 + jmp short M01_L09 +M01_L11: + jmp qword ptr [7FFEC3379A08]; System.Buffer._Memmove(Byte ByRef, Byte ByRef, UIntPtr) +; Total bytes of code 270 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyUsingLargerPrimitives() + push rdi + push rsi + sub rsp,48 + xor eax,eax + mov [rsp+28],rax + vxorps xmm4,xmm4,xmm4 + vmovdqa xmmword ptr [rsp+30],xmm4 + mov [rsp+40],rax + mov rdx,[rcx+10] + test rdx,rdx + je short M00_L02 + lea rax,[rdx+10] + mov r8d,[rdx+8] +M00_L00: + mov rcx,[rcx+8] + test rcx,rcx + je short M00_L03 + lea rsi,[rcx+10] + mov edi,[rcx+8] +M00_L01: + mov [rsp+38],rax + mov [rsp+40],r8d + mov [rsp+28],rsi + mov [rsp+30],edi + lea rcx,[rsp+38] + lea rdx,[rsp+28] + call qword ptr [7FFEC35F7948]; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyBytes(System.ReadOnlySpan`1, System.Span`1) + nop + add rsp,48 + pop rsi + pop rdi + ret +M00_L02: + xor eax,eax + xor r8d,r8d + jmp short M00_L00 +M00_L03: + xor esi,esi + xor edi,edi + jmp short M00_L01 +; Total bytes of code 117 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyBytes(System.ReadOnlySpan`1, System.Span`1) + mov rax,[rcx] + mov ecx,[rcx+8] + mov rdx,[rdx] + xor r8d,r8d + lea r9d,[rcx-8] + movsxd r9,r9d + test r9,r9 + jl short M01_L01 + nop dword ptr [rax+rax] +M01_L00: + mov r10d,r8d + mov r11,[rax+r10] + mov [rdx+r10],r11 + add r8d,8 + mov r10d,r8d + cmp r10,r9 + jle short M01_L00 +M01_L01: + mov r9d,r8d + lea r10d,[rcx-4] + movsxd r10,r10d + cmp r9,r10 + jg short M01_L02 + mov r10d,[rax+r9] + mov [rdx+r9],r10d + add r8d,4 +M01_L02: + mov r9d,r8d + lea r10d,[rcx-2] + movsxd r10,r10d + cmp r9,r10 + jg short M01_L03 + movzx r10d,word ptr [rax+r9] + mov [rdx+r9],r10w + add r8d,2 +M01_L03: + mov r9d,r8d + movsxd rcx,ecx + cmp r9,rcx + jge short M01_L04 + movzx eax,byte ptr [rax+r9] + mov [rdx+r9],al + inc r8d +M01_L04: + mov eax,r8d + ret +; Total bytes of code 138 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.SpanCopyTo() + push rdi + push rsi + push rbp + push rbx + sub rsp,28 + mov rsi,rcx + mov rcx,[rsi+10] + test rcx,rcx + je short M00_L02 + lea rdx,[rcx+10] + mov edi,[rcx+8] +M00_L00: + mov rcx,[rsi+8] + test rcx,rcx + je short M00_L03 + lea rbx,[rcx+10] + mov ebp,[rcx+8] +M00_L01: + mov rcx,rbx + cmp edi,ebp + ja short M00_L04 + mov r8d,edi + call qword ptr [7FFEC33799F0]; System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr) + mov rax,[rsi+10] + mov eax,[rax+8] + add rsp,28 + pop rbx + pop rbp + pop rsi + pop rdi + ret +M00_L02: + xor edx,edx + xor edi,edi + jmp short M00_L00 +M00_L03: + xor ebx,ebx + xor ebp,ebp + jmp short M00_L01 +M00_L04: + call qword ptr [7FFEC35AB4B0] + int 3 +; Total bytes of code 94 +``` +```assembly +; System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr) + vzeroupper + mov rax,rcx + sub rax,rdx + cmp rax,r8 + jae short M01_L01 +M01_L00: + cmp rcx,rdx + je near ptr M01_L09 + jmp near ptr M01_L11 +M01_L01: + mov rax,rdx + sub rax,rcx + cmp rax,r8 + jb short M01_L00 + lea rax,[rdx+r8] + lea r9,[rcx+r8] + cmp r8,10 + jbe short M01_L04 + cmp r8,40 + ja near ptr M01_L07 +M01_L02: + vmovupd xmm0,[rdx] + vmovupd [rcx],xmm0 + cmp r8,20 + jbe short M01_L03 + vmovupd xmm0,[rdx+10] + vmovupd [rcx+10],xmm0 + cmp r8,30 + jbe short M01_L03 + vmovupd xmm0,[rdx+20] + vmovupd [rcx+20],xmm0 +M01_L03: + vmovupd xmm0,[rax-10] + vmovupd [r9-10],xmm0 + jmp short M01_L09 +M01_L04: + test r8b,18 + je short M01_L05 + mov r8,[rdx] + mov [rcx],r8 + mov rdx,[rax-8] + mov [r9-8],rdx + jmp short M01_L09 +M01_L05: + test r8b,4 + je short M01_L06 + mov r8d,[rdx] + mov [rcx],r8d + mov edx,[rax-4] + mov [r9-4],edx + jmp short M01_L09 +M01_L06: + test r8,r8 + je short M01_L09 + movzx edx,byte ptr [rdx] + mov [rcx],dl + test r8b,2 + je short M01_L09 + movsx r8,word ptr [rax-2] + mov [r9-2],r8w + jmp short M01_L09 +M01_L07: + cmp r8,800 + ja short M01_L11 + mov r10,r8 + shr r10,6 +M01_L08: + vmovdqu ymm0,ymmword ptr [rdx] + vmovdqu ymmword ptr [rcx],ymm0 + vmovdqu ymm0,ymmword ptr [rdx+20] + vmovdqu ymmword ptr [rcx+20],ymm0 + add rcx,40 + add rdx,40 + dec r10 + je short M01_L10 + jmp short M01_L08 +M01_L09: + ret +M01_L10: + and r8,3F + cmp r8,10 + ja near ptr M01_L02 + vmovupd xmm0,[rax-10] + vmovupd [r9-10],xmm0 + jmp short M01_L09 +M01_L11: + jmp qword ptr [7FFEC3379A08]; System.Buffer._Memmove(Byte ByRef, Byte ByRef, UIntPtr) +; Total bytes of code 270 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyUsingLargerPrimitives() + push rdi + push rsi + sub rsp,48 + xor eax,eax + mov [rsp+28],rax + vxorps xmm4,xmm4,xmm4 + vmovdqa xmmword ptr [rsp+30],xmm4 + mov [rsp+40],rax + mov rdx,[rcx+10] + test rdx,rdx + je short M00_L02 + lea rax,[rdx+10] + mov r8d,[rdx+8] +M00_L00: + mov rcx,[rcx+8] + test rcx,rcx + je short M00_L03 + lea rsi,[rcx+10] + mov edi,[rcx+8] +M00_L01: + mov [rsp+38],rax + mov [rsp+40],r8d + mov [rsp+28],rsi + mov [rsp+30],edi + lea rcx,[rsp+38] + lea rdx,[rsp+28] + call qword ptr [7FFEC35F7948]; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyBytes(System.ReadOnlySpan`1, System.Span`1) + nop + add rsp,48 + pop rsi + pop rdi + ret +M00_L02: + xor eax,eax + xor r8d,r8d + jmp short M00_L00 +M00_L03: + xor esi,esi + xor edi,edi + jmp short M00_L01 +; Total bytes of code 117 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyBytes(System.ReadOnlySpan`1, System.Span`1) + mov rax,[rcx] + mov ecx,[rcx+8] + mov rdx,[rdx] + xor r8d,r8d + lea r9d,[rcx-8] + movsxd r9,r9d + test r9,r9 + jl short M01_L01 + nop dword ptr [rax+rax] +M01_L00: + mov r10d,r8d + mov r11,[rax+r10] + mov [rdx+r10],r11 + add r8d,8 + mov r10d,r8d + cmp r10,r9 + jle short M01_L00 +M01_L01: + mov r9d,r8d + lea r10d,[rcx-4] + movsxd r10,r10d + cmp r9,r10 + jg short M01_L02 + mov r10d,[rax+r9] + mov [rdx+r9],r10d + add r8d,4 +M01_L02: + mov r9d,r8d + lea r10d,[rcx-2] + movsxd r10,r10d + cmp r9,r10 + jg short M01_L03 + movzx r10d,word ptr [rax+r9] + mov [rdx+r9],r10w + add r8d,2 +M01_L03: + mov r9d,r8d + movsxd rcx,ecx + cmp r9,rcx + jge short M01_L04 + movzx eax,byte ptr [rax+r9] + mov [rdx+r9],al + inc r8d +M01_L04: + mov eax,r8d + ret +; Total bytes of code 138 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.SpanCopyTo() + push rdi + push rsi + push rbp + push rbx + sub rsp,28 + mov rsi,rcx + mov rcx,[rsi+10] + test rcx,rcx + je short M00_L02 + lea rdx,[rcx+10] + mov edi,[rcx+8] +M00_L00: + mov rcx,[rsi+8] + test rcx,rcx + je short M00_L03 + lea rbx,[rcx+10] + mov ebp,[rcx+8] +M00_L01: + mov rcx,rbx + cmp edi,ebp + ja short M00_L04 + mov r8d,edi + call qword ptr [7FFEC33699F0]; System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr) + mov rax,[rsi+10] + mov eax,[rax+8] + add rsp,28 + pop rbx + pop rbp + pop rsi + pop rdi + ret +M00_L02: + xor edx,edx + xor edi,edi + jmp short M00_L00 +M00_L03: + xor ebx,ebx + xor ebp,ebp + jmp short M00_L01 +M00_L04: + call qword ptr [7FFEC359B4B0] + int 3 +; Total bytes of code 94 +``` +```assembly +; System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr) + vzeroupper + mov rax,rcx + sub rax,rdx + cmp rax,r8 + jae short M01_L01 +M01_L00: + cmp rcx,rdx + je near ptr M01_L09 + jmp near ptr M01_L11 +M01_L01: + mov rax,rdx + sub rax,rcx + cmp rax,r8 + jb short M01_L00 + lea rax,[rdx+r8] + lea r9,[rcx+r8] + cmp r8,10 + jbe short M01_L04 + cmp r8,40 + ja near ptr M01_L07 +M01_L02: + vmovupd xmm0,[rdx] + vmovupd [rcx],xmm0 + cmp r8,20 + jbe short M01_L03 + vmovupd xmm0,[rdx+10] + vmovupd [rcx+10],xmm0 + cmp r8,30 + jbe short M01_L03 + vmovupd xmm0,[rdx+20] + vmovupd [rcx+20],xmm0 +M01_L03: + vmovupd xmm0,[rax-10] + vmovupd [r9-10],xmm0 + jmp short M01_L09 +M01_L04: + test r8b,18 + je short M01_L05 + mov r8,[rdx] + mov [rcx],r8 + mov rdx,[rax-8] + mov [r9-8],rdx + jmp short M01_L09 +M01_L05: + test r8b,4 + je short M01_L06 + mov r8d,[rdx] + mov [rcx],r8d + mov edx,[rax-4] + mov [r9-4],edx + jmp short M01_L09 +M01_L06: + test r8,r8 + je short M01_L09 + movzx edx,byte ptr [rdx] + mov [rcx],dl + test r8b,2 + je short M01_L09 + movsx r8,word ptr [rax-2] + mov [r9-2],r8w + jmp short M01_L09 +M01_L07: + cmp r8,800 + ja short M01_L11 + mov r10,r8 + shr r10,6 +M01_L08: + vmovdqu ymm0,ymmword ptr [rdx] + vmovdqu ymmword ptr [rcx],ymm0 + vmovdqu ymm0,ymmword ptr [rdx+20] + vmovdqu ymmword ptr [rcx+20],ymm0 + add rcx,40 + add rdx,40 + dec r10 + je short M01_L10 + jmp short M01_L08 +M01_L09: + ret +M01_L10: + and r8,3F + cmp r8,10 + ja near ptr M01_L02 + vmovupd xmm0,[rax-10] + vmovupd [r9-10],xmm0 + jmp short M01_L09 +M01_L11: + jmp qword ptr [7FFEC3369A08]; System.Buffer._Memmove(Byte ByRef, Byte ByRef, UIntPtr) +; Total bytes of code 270 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyUsingLargerPrimitives() + push rdi + push rsi + sub rsp,48 + xor eax,eax + mov [rsp+28],rax + vxorps xmm4,xmm4,xmm4 + vmovdqa xmmword ptr [rsp+30],xmm4 + mov [rsp+40],rax + mov rdx,[rcx+10] + test rdx,rdx + je short M00_L02 + lea rax,[rdx+10] + mov r8d,[rdx+8] +M00_L00: + mov rcx,[rcx+8] + test rcx,rcx + je short M00_L03 + lea rsi,[rcx+10] + mov edi,[rcx+8] +M00_L01: + mov [rsp+38],rax + mov [rsp+40],r8d + mov [rsp+28],rsi + mov [rsp+30],edi + lea rcx,[rsp+38] + lea rdx,[rsp+28] + call qword ptr [7FFEC35D7948]; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyBytes(System.ReadOnlySpan`1, System.Span`1) + nop + add rsp,48 + pop rsi + pop rdi + ret +M00_L02: + xor eax,eax + xor r8d,r8d + jmp short M00_L00 +M00_L03: + xor esi,esi + xor edi,edi + jmp short M00_L01 +; Total bytes of code 117 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyBytes(System.ReadOnlySpan`1, System.Span`1) + mov rax,[rcx] + mov ecx,[rcx+8] + mov rdx,[rdx] + xor r8d,r8d + lea r9d,[rcx-8] + movsxd r9,r9d + test r9,r9 + jl short M01_L01 + nop dword ptr [rax+rax] +M01_L00: + mov r10d,r8d + mov r11,[rax+r10] + mov [rdx+r10],r11 + add r8d,8 + mov r10d,r8d + cmp r10,r9 + jle short M01_L00 +M01_L01: + mov r9d,r8d + lea r10d,[rcx-4] + movsxd r10,r10d + cmp r9,r10 + jg short M01_L02 + mov r10d,[rax+r9] + mov [rdx+r9],r10d + add r8d,4 +M01_L02: + mov r9d,r8d + lea r10d,[rcx-2] + movsxd r10,r10d + cmp r9,r10 + jg short M01_L03 + movzx r10d,word ptr [rax+r9] + mov [rdx+r9],r10w + add r8d,2 +M01_L03: + mov r9d,r8d + movsxd rcx,ecx + cmp r9,rcx + jge short M01_L04 + movzx eax,byte ptr [rax+r9] + mov [rdx+r9],al + inc r8d +M01_L04: + mov eax,r8d + ret +; Total bytes of code 138 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.SpanCopyTo() + push rdi + push rsi + push rbp + push rbx + sub rsp,28 + mov rsi,rcx + mov rcx,[rsi+10] + test rcx,rcx + je short M00_L02 + lea rdx,[rcx+10] + mov edi,[rcx+8] +M00_L00: + mov rcx,[rsi+8] + test rcx,rcx + je short M00_L03 + lea rbx,[rcx+10] + mov ebp,[rcx+8] +M00_L01: + mov rcx,rbx + cmp edi,ebp + ja short M00_L04 + mov r8d,edi + call qword ptr [7FFEC33999F0]; System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr) + mov rax,[rsi+10] + mov eax,[rax+8] + add rsp,28 + pop rbx + pop rbp + pop rsi + pop rdi + ret +M00_L02: + xor edx,edx + xor edi,edi + jmp short M00_L00 +M00_L03: + xor ebx,ebx + xor ebp,ebp + jmp short M00_L01 +M00_L04: + call qword ptr [7FFEC35CB4B0] + int 3 +; Total bytes of code 94 +``` +```assembly +; System.Buffer.Memmove(Byte ByRef, Byte ByRef, UIntPtr) + vzeroupper + mov rax,rcx + sub rax,rdx + cmp rax,r8 + jae short M01_L01 +M01_L00: + cmp rcx,rdx + je near ptr M01_L09 + jmp near ptr M01_L11 +M01_L01: + mov rax,rdx + sub rax,rcx + cmp rax,r8 + jb short M01_L00 + lea rax,[rdx+r8] + lea r9,[rcx+r8] + cmp r8,10 + jbe short M01_L04 + cmp r8,40 + ja near ptr M01_L07 +M01_L02: + vmovupd xmm0,[rdx] + vmovupd [rcx],xmm0 + cmp r8,20 + jbe short M01_L03 + vmovupd xmm0,[rdx+10] + vmovupd [rcx+10],xmm0 + cmp r8,30 + jbe short M01_L03 + vmovupd xmm0,[rdx+20] + vmovupd [rcx+20],xmm0 +M01_L03: + vmovupd xmm0,[rax-10] + vmovupd [r9-10],xmm0 + jmp short M01_L09 +M01_L04: + test r8b,18 + je short M01_L05 + mov r8,[rdx] + mov [rcx],r8 + mov rdx,[rax-8] + mov [r9-8],rdx + jmp short M01_L09 +M01_L05: + test r8b,4 + je short M01_L06 + mov r8d,[rdx] + mov [rcx],r8d + mov edx,[rax-4] + mov [r9-4],edx + jmp short M01_L09 +M01_L06: + test r8,r8 + je short M01_L09 + movzx edx,byte ptr [rdx] + mov [rcx],dl + test r8b,2 + je short M01_L09 + movsx r8,word ptr [rax-2] + mov [r9-2],r8w + jmp short M01_L09 +M01_L07: + cmp r8,800 + ja short M01_L11 + mov r10,r8 + shr r10,6 +M01_L08: + vmovdqu ymm0,ymmword ptr [rdx] + vmovdqu ymmword ptr [rcx],ymm0 + vmovdqu ymm0,ymmword ptr [rdx+20] + vmovdqu ymmword ptr [rcx+20],ymm0 + add rcx,40 + add rdx,40 + dec r10 + je short M01_L10 + jmp short M01_L08 +M01_L09: + ret +M01_L10: + and r8,3F + cmp r8,10 + ja near ptr M01_L02 + vmovupd xmm0,[rax-10] + vmovupd [r9-10],xmm0 + jmp short M01_L09 +M01_L11: + jmp qword ptr [7FFEC3399A08]; System.Buffer._Memmove(Byte ByRef, Byte ByRef, UIntPtr) +; Total bytes of code 270 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyUsingLargerPrimitives() + push rdi + push rsi + sub rsp,48 + xor eax,eax + mov [rsp+28],rax + vxorps xmm4,xmm4,xmm4 + vmovdqa xmmword ptr [rsp+30],xmm4 + mov [rsp+40],rax + mov rdx,[rcx+10] + test rdx,rdx + je short M00_L02 + lea rax,[rdx+10] + mov r8d,[rdx+8] +M00_L00: + mov rcx,[rcx+8] + test rcx,rcx + je short M00_L03 + lea rsi,[rcx+10] + mov edi,[rcx+8] +M00_L01: + mov [rsp+38],rax + mov [rsp+40],r8d + mov [rsp+28],rsi + mov [rsp+30],edi + lea rcx,[rsp+38] + lea rdx,[rsp+28] + call qword ptr [7FFEC35F7948]; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyBytes(System.ReadOnlySpan`1, System.Span`1) + nop + add rsp,48 + pop rsi + pop rdi + ret +M00_L02: + xor eax,eax + xor r8d,r8d + jmp short M00_L00 +M00_L03: + xor esi,esi + xor edi,edi + jmp short M00_L01 +; Total bytes of code 117 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeByte.CopyBytes(System.ReadOnlySpan`1, System.Span`1) + mov rax,[rcx] + mov ecx,[rcx+8] + mov rdx,[rdx] + xor r8d,r8d + lea r9d,[rcx-8] + movsxd r9,r9d + test r9,r9 + jl short M01_L01 + nop dword ptr [rax+rax] +M01_L00: + mov r10d,r8d + mov r11,[rax+r10] + mov [rdx+r10],r11 + add r8d,8 + mov r10d,r8d + cmp r10,r9 + jle short M01_L00 +M01_L01: + mov r9d,r8d + lea r10d,[rcx-4] + movsxd r10,r10d + cmp r9,r10 + jg short M01_L02 + mov r10d,[rax+r9] + mov [rdx+r9],r10d + add r8d,4 +M01_L02: + mov r9d,r8d + lea r10d,[rcx-2] + movsxd r10,r10d + cmp r9,r10 + jg short M01_L03 + movzx r10d,word ptr [rax+r9] + mov [rdx+r9],r10w + add r8d,2 +M01_L03: + mov r9d,r8d + movsxd rcx,ecx + cmp r9,rcx + jge short M01_L04 + movzx eax,byte ptr [rax+r9] + mov [rdx+r9],al + inc r8d +M01_L04: + mov eax,r8d + ret +; Total bytes of code 138 +``` + diff --git a/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializeArray.SerializeByte-report-github.md b/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializeArray.SerializeByte-report-github.md new file mode 100644 index 0000000..6d8cce3 --- /dev/null +++ b/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializeArray.SerializeByte-report-github.md @@ -0,0 +1,29 @@ +``` ini + +BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.3448/22H2/2022Update) +Intel Core i9-9900K CPU 3.60GHz (Coffee Lake), 1 CPU, 16 logical and 8 physical cores +.NET SDK=7.0.401 + [Host] : .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 + Job-YLNZRQ : .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 + +IterationTime=150.0000 ms WarmupCount=2 + +``` +| Method | Value | Mean | Error | StdDev | Median | Ratio | RatioSD | Completed Work Items | Lock Contentions | Code Size | Allocated | Alloc Ratio | +|-------------------------- |--------- |---------:|----------:|----------:|---------:|------:|--------:|---------------------:|-----------------:|----------:|----------:|------------:| +| **SpanCopyTo** | **Byte[16]** | **3.201 ns** | **0.1285 ns** | **0.3729 ns** | **3.139 ns** | **1.00** | **0.00** | **-** | **-** | **364 B** | **-** | **NA** | +| CopyUsingLargerPrimitives | Byte[16] | 4.236 ns | 0.1211 ns | 0.1849 ns | 4.232 ns | 1.26 | 0.13 | - | - | 255 B | - | NA | +| **SpanCopyTo** | **Byte[1]** | **4.482 ns** | **0.1734 ns** | **0.4947 ns** | **4.365 ns** | **1.42** | **0.24** | **-** | **-** | **364 B** | **-** | **NA** | +| CopyUsingLargerPrimitives | Byte[1] | 3.955 ns | 0.1599 ns | 0.4613 ns | 3.865 ns | 1.25 | 0.19 | - | - | 255 B | - | NA | +| **SpanCopyTo** | **Byte[24]** | **3.049 ns** | **0.1035 ns** | **0.2868 ns** | **2.963 ns** | **0.96** | **0.13** | **-** | **-** | **364 B** | **-** | **NA** | +| CopyUsingLargerPrimitives | Byte[24] | 5.251 ns | 0.1449 ns | 0.3025 ns | 5.172 ns | 1.58 | 0.16 | - | - | 255 B | - | NA | +| **SpanCopyTo** | **Byte[2]** | **4.087 ns** | **0.1235 ns** | **0.2813 ns** | **4.065 ns** | **1.23** | **0.15** | **-** | **-** | **364 B** | **-** | **NA** | +| CopyUsingLargerPrimitives | Byte[2] | 3.627 ns | 0.1125 ns | 0.2606 ns | 3.598 ns | 1.10 | 0.14 | - | - | 255 B | - | NA | +| **SpanCopyTo** | **Byte[32]** | **2.588 ns** | **0.0952 ns** | **0.2475 ns** | **2.511 ns** | **0.80** | **0.10** | **-** | **-** | **364 B** | **-** | **NA** | +| CopyUsingLargerPrimitives | Byte[32] | 5.079 ns | 0.1422 ns | 0.3434 ns | 4.968 ns | 1.55 | 0.19 | - | - | 255 B | - | NA | +| **SpanCopyTo** | **Byte[4]** | **2.896 ns** | **0.0944 ns** | **0.2013 ns** | **2.885 ns** | **0.87** | **0.09** | **-** | **-** | **364 B** | **-** | **NA** | +| CopyUsingLargerPrimitives | Byte[4] | 3.210 ns | 0.1042 ns | 0.2414 ns | 3.157 ns | 0.97 | 0.11 | - | - | 255 B | - | NA | +| **SpanCopyTo** | **Byte[64]** | **3.387 ns** | **0.1047 ns** | **0.1598 ns** | **3.373 ns** | **1.01** | **0.10** | **-** | **-** | **364 B** | **-** | **NA** | +| CopyUsingLargerPrimitives | Byte[64] | 6.639 ns | 0.1769 ns | 0.2480 ns | 6.567 ns | 1.98 | 0.19 | - | - | 255 B | - | NA | +| **SpanCopyTo** | **Byte[8]** | **2.715 ns** | **0.0883 ns** | **0.1400 ns** | **2.742 ns** | **0.81** | **0.09** | **-** | **-** | **364 B** | **-** | **NA** | +| CopyUsingLargerPrimitives | Byte[8] | 3.476 ns | 0.1054 ns | 0.2401 ns | 3.465 ns | 1.04 | 0.11 | - | - | 255 B | - | NA | diff --git a/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializeArray.SerializeUInt16-asm.md b/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializeArray.SerializeUInt16-asm.md new file mode 100644 index 0000000..b0a63ad --- /dev/null +++ b/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializeArray.SerializeUInt16-asm.md @@ -0,0 +1,2640 @@ +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.SerializeOneByOne() + push rdi + push rsi + sub rsp,38 + xor eax,eax + mov [rsp+28],rax + mov rsi,rcx + mov rcx,[rsi+8] + test rcx,rcx + je short M00_L03 + lea rdi,[rcx+10] + mov ecx,[rcx+8] +M00_L00: + mov rcx,rdi + mov rdx,[rsi+10] + test rdx,rdx + jne short M00_L01 + xor eax,eax + xor r8d,r8d + jmp short M00_L02 +M00_L01: + lea rax,[rdx+10] + mov r8d,[rdx+8] +M00_L02: + mov [rsp+28],rax + mov [rsp+30],r8d + lea rdx,[rsp+28] + call qword ptr [7FFEC35E7AB0]; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + mov rax,[rsi+10] + mov eax,[rax+8] + add eax,eax + add rsp,38 + pop rsi + pop rdi + ret +M00_L03: + xor edi,edi + jmp short M00_L00 +; Total bytes of code 100 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + xor eax,eax + mov r8,[rdx] + mov edx,[rdx+8] + xor r9d,r9d + test edx,edx + jle short M01_L01 + nop +M01_L00: + mov r10d,r9d + movzx r10d,word ptr [r8+r10*2] + mov r11d,eax + movbe [rcx+r11],r10w + add eax,2 + inc r9d + cmp r9d,edx + jl short M01_L00 +M01_L01: + ret +; Total bytes of code 46 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeReadUnaligned() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + xor eax,eax + xor r9d,r9d + cmp dword ptr [r8+8],0 + jle short M00_L03 + nop dword ptr [rax] + nop dword ptr [rax] +M00_L02: + mov r8d,eax + movzx r11d,word ptr [r10+r8] + movbe [rdx+r8],r11w + add eax,2 + inc r9d + mov r8,[rcx+10] + cmp [r8+8],r9d + jg short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 143 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeIsAddressLessThan() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov eax,[r8+8] + add eax,eax + mov r8d,eax + add r8,r10 + cmp r10,r8 + jae short M00_L03 +M00_L02: + movzx r9d,word ptr [r10] + movbe [rdx],r9w + add r10,2 + add rdx,2 + cmp r10,r8 + jb short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 126 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingCopyAndAlign() + sub rsp,28 + mov rdx,[rcx+10] + mov rax,rdx + test rax,rax + je short M00_L05 + lea r8,[rax+10] + mov r9d,[rax+8] +M00_L00: + mov eax,r9d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov rax,[rcx+8] + test rax,rax + je short M00_L06 + lea r9,[rax+10] + mov eax,[rax+8] +M00_L01: + mov eax,[rdx+8] + add eax,eax + xor ecx,ecx + test eax,eax + jbe short M00_L03 +M00_L02: + mov edx,ecx + movzx r10d,word ptr [r8+rdx] + movbe [r9+rdx],r10w + add ecx,2 + cmp ecx,eax + jb short M00_L02 +M00_L03: + mov eax,ecx + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor r8d,r8d + xor r9d,r9d + jmp short M00_L00 +M00_L06: + xor r9d,r9d + jmp short M00_L01 +; Total bytes of code 111 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.SerializeOneByOne() + push rdi + push rsi + sub rsp,38 + xor eax,eax + mov [rsp+28],rax + mov rsi,rcx + mov rcx,[rsi+8] + test rcx,rcx + je short M00_L03 + lea rdi,[rcx+10] + mov ecx,[rcx+8] +M00_L00: + mov rcx,rdi + mov rdx,[rsi+10] + test rdx,rdx + jne short M00_L01 + xor eax,eax + xor r8d,r8d + jmp short M00_L02 +M00_L01: + lea rax,[rdx+10] + mov r8d,[rdx+8] +M00_L02: + mov [rsp+28],rax + mov [rsp+30],r8d + lea rdx,[rsp+28] + call qword ptr [7FFEC3617AB0]; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + mov rax,[rsi+10] + mov eax,[rax+8] + add eax,eax + add rsp,38 + pop rsi + pop rdi + ret +M00_L03: + xor edi,edi + jmp short M00_L00 +; Total bytes of code 100 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + xor eax,eax + mov r8,[rdx] + mov edx,[rdx+8] + xor r9d,r9d + test edx,edx + jle short M01_L01 + nop +M01_L00: + mov r10d,r9d + movzx r10d,word ptr [r8+r10*2] + mov r11d,eax + movbe [rcx+r11],r10w + add eax,2 + inc r9d + cmp r9d,edx + jl short M01_L00 +M01_L01: + ret +; Total bytes of code 46 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeReadUnaligned() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + xor eax,eax + xor r9d,r9d + cmp dword ptr [r8+8],0 + jle short M00_L03 + nop dword ptr [rax] + nop dword ptr [rax] +M00_L02: + mov r8d,eax + movzx r11d,word ptr [r10+r8] + movbe [rdx+r8],r11w + add eax,2 + inc r9d + mov r8,[rcx+10] + cmp [r8+8],r9d + jg short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 143 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeIsAddressLessThan() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov eax,[r8+8] + add eax,eax + mov r8d,eax + add r8,r10 + cmp r10,r8 + jae short M00_L03 +M00_L02: + movzx r9d,word ptr [r10] + movbe [rdx],r9w + add r10,2 + add rdx,2 + cmp r10,r8 + jb short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 126 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingCopyAndAlign() + sub rsp,28 + mov rdx,[rcx+10] + mov rax,rdx + test rax,rax + je short M00_L05 + lea r8,[rax+10] + mov r9d,[rax+8] +M00_L00: + mov eax,r9d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov rax,[rcx+8] + test rax,rax + je short M00_L06 + lea r9,[rax+10] + mov eax,[rax+8] +M00_L01: + mov eax,[rdx+8] + add eax,eax + xor ecx,ecx + test eax,eax + jbe short M00_L03 +M00_L02: + mov edx,ecx + movzx r10d,word ptr [r8+rdx] + movbe [r9+rdx],r10w + add ecx,2 + cmp ecx,eax + jb short M00_L02 +M00_L03: + mov eax,ecx + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor r8d,r8d + xor r9d,r9d + jmp short M00_L00 +M00_L06: + xor r9d,r9d + jmp short M00_L01 +; Total bytes of code 111 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.SerializeOneByOne() + push rdi + push rsi + sub rsp,38 + xor eax,eax + mov [rsp+28],rax + mov rsi,rcx + mov rcx,[rsi+8] + test rcx,rcx + je short M00_L03 + lea rdi,[rcx+10] + mov ecx,[rcx+8] +M00_L00: + mov rcx,rdi + mov rdx,[rsi+10] + test rdx,rdx + jne short M00_L01 + xor eax,eax + xor r8d,r8d + jmp short M00_L02 +M00_L01: + lea rax,[rdx+10] + mov r8d,[rdx+8] +M00_L02: + mov [rsp+28],rax + mov [rsp+30],r8d + lea rdx,[rsp+28] + call qword ptr [7FFEC35F7AB0]; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + mov rax,[rsi+10] + mov eax,[rax+8] + add eax,eax + add rsp,38 + pop rsi + pop rdi + ret +M00_L03: + xor edi,edi + jmp short M00_L00 +; Total bytes of code 100 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + xor eax,eax + mov r8,[rdx] + mov edx,[rdx+8] + xor r9d,r9d + test edx,edx + jle short M01_L01 + nop +M01_L00: + mov r10d,r9d + movzx r10d,word ptr [r8+r10*2] + mov r11d,eax + movbe [rcx+r11],r10w + add eax,2 + inc r9d + cmp r9d,edx + jl short M01_L00 +M01_L01: + ret +; Total bytes of code 46 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeReadUnaligned() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + xor eax,eax + xor r9d,r9d + cmp dword ptr [r8+8],0 + jle short M00_L03 + nop dword ptr [rax] + nop dword ptr [rax] +M00_L02: + mov r8d,eax + movzx r11d,word ptr [r10+r8] + movbe [rdx+r8],r11w + add eax,2 + inc r9d + mov r8,[rcx+10] + cmp [r8+8],r9d + jg short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 143 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeIsAddressLessThan() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov eax,[r8+8] + add eax,eax + mov r8d,eax + add r8,r10 + cmp r10,r8 + jae short M00_L03 +M00_L02: + movzx r9d,word ptr [r10] + movbe [rdx],r9w + add r10,2 + add rdx,2 + cmp r10,r8 + jb short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 126 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingCopyAndAlign() + sub rsp,28 + mov rdx,[rcx+10] + mov rax,rdx + test rax,rax + je short M00_L05 + lea r8,[rax+10] + mov r9d,[rax+8] +M00_L00: + mov eax,r9d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov rax,[rcx+8] + test rax,rax + je short M00_L06 + lea r9,[rax+10] + mov eax,[rax+8] +M00_L01: + mov eax,[rdx+8] + add eax,eax + xor ecx,ecx + test eax,eax + jbe short M00_L03 +M00_L02: + mov edx,ecx + movzx r10d,word ptr [r8+rdx] + movbe [r9+rdx],r10w + add ecx,2 + cmp ecx,eax + jb short M00_L02 +M00_L03: + mov eax,ecx + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor r8d,r8d + xor r9d,r9d + jmp short M00_L00 +M00_L06: + xor r9d,r9d + jmp short M00_L01 +; Total bytes of code 111 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.SerializeOneByOne() + push rdi + push rsi + sub rsp,38 + xor eax,eax + mov [rsp+28],rax + mov rsi,rcx + mov rcx,[rsi+8] + test rcx,rcx + je short M00_L03 + lea rdi,[rcx+10] + mov ecx,[rcx+8] +M00_L00: + mov rcx,rdi + mov rdx,[rsi+10] + test rdx,rdx + jne short M00_L01 + xor eax,eax + xor r8d,r8d + jmp short M00_L02 +M00_L01: + lea rax,[rdx+10] + mov r8d,[rdx+8] +M00_L02: + mov [rsp+28],rax + mov [rsp+30],r8d + lea rdx,[rsp+28] + call qword ptr [7FFEC35E7AB0]; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + mov rax,[rsi+10] + mov eax,[rax+8] + add eax,eax + add rsp,38 + pop rsi + pop rdi + ret +M00_L03: + xor edi,edi + jmp short M00_L00 +; Total bytes of code 100 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + xor eax,eax + mov r8,[rdx] + mov edx,[rdx+8] + xor r9d,r9d + test edx,edx + jle short M01_L01 + nop +M01_L00: + mov r10d,r9d + movzx r10d,word ptr [r8+r10*2] + mov r11d,eax + movbe [rcx+r11],r10w + add eax,2 + inc r9d + cmp r9d,edx + jl short M01_L00 +M01_L01: + ret +; Total bytes of code 46 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeReadUnaligned() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + xor eax,eax + xor r9d,r9d + cmp dword ptr [r8+8],0 + jle short M00_L03 + nop dword ptr [rax] + nop dword ptr [rax] +M00_L02: + mov r8d,eax + movzx r11d,word ptr [r10+r8] + movbe [rdx+r8],r11w + add eax,2 + inc r9d + mov r8,[rcx+10] + cmp [r8+8],r9d + jg short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 143 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeIsAddressLessThan() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov eax,[r8+8] + add eax,eax + mov r8d,eax + add r8,r10 + cmp r10,r8 + jae short M00_L03 +M00_L02: + movzx r9d,word ptr [r10] + movbe [rdx],r9w + add r10,2 + add rdx,2 + cmp r10,r8 + jb short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 126 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingCopyAndAlign() + sub rsp,28 + mov rdx,[rcx+10] + mov rax,rdx + test rax,rax + je short M00_L05 + lea r8,[rax+10] + mov r9d,[rax+8] +M00_L00: + mov eax,r9d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov rax,[rcx+8] + test rax,rax + je short M00_L06 + lea r9,[rax+10] + mov eax,[rax+8] +M00_L01: + mov eax,[rdx+8] + add eax,eax + xor ecx,ecx + test eax,eax + jbe short M00_L03 +M00_L02: + mov edx,ecx + movzx r10d,word ptr [r8+rdx] + movbe [r9+rdx],r10w + add ecx,2 + cmp ecx,eax + jb short M00_L02 +M00_L03: + mov eax,ecx + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor r8d,r8d + xor r9d,r9d + jmp short M00_L00 +M00_L06: + xor r9d,r9d + jmp short M00_L01 +; Total bytes of code 111 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.SerializeOneByOne() + push rdi + push rsi + sub rsp,38 + xor eax,eax + mov [rsp+28],rax + mov rsi,rcx + mov rcx,[rsi+8] + test rcx,rcx + je short M00_L03 + lea rdi,[rcx+10] + mov ecx,[rcx+8] +M00_L00: + mov rcx,rdi + mov rdx,[rsi+10] + test rdx,rdx + jne short M00_L01 + xor eax,eax + xor r8d,r8d + jmp short M00_L02 +M00_L01: + lea rax,[rdx+10] + mov r8d,[rdx+8] +M00_L02: + mov [rsp+28],rax + mov [rsp+30],r8d + lea rdx,[rsp+28] + call qword ptr [7FFEC35E7AB0]; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + mov rax,[rsi+10] + mov eax,[rax+8] + add eax,eax + add rsp,38 + pop rsi + pop rdi + ret +M00_L03: + xor edi,edi + jmp short M00_L00 +; Total bytes of code 100 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + xor eax,eax + mov r8,[rdx] + mov edx,[rdx+8] + xor r9d,r9d + test edx,edx + jle short M01_L01 + nop +M01_L00: + mov r10d,r9d + movzx r10d,word ptr [r8+r10*2] + mov r11d,eax + movbe [rcx+r11],r10w + add eax,2 + inc r9d + cmp r9d,edx + jl short M01_L00 +M01_L01: + ret +; Total bytes of code 46 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeReadUnaligned() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + xor eax,eax + xor r9d,r9d + cmp dword ptr [r8+8],0 + jle short M00_L03 + nop dword ptr [rax] + nop dword ptr [rax] +M00_L02: + mov r8d,eax + movzx r11d,word ptr [r10+r8] + movbe [rdx+r8],r11w + add eax,2 + inc r9d + mov r8,[rcx+10] + cmp [r8+8],r9d + jg short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 143 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeIsAddressLessThan() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov eax,[r8+8] + add eax,eax + mov r8d,eax + add r8,r10 + cmp r10,r8 + jae short M00_L03 +M00_L02: + movzx r9d,word ptr [r10] + movbe [rdx],r9w + add r10,2 + add rdx,2 + cmp r10,r8 + jb short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 126 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingCopyAndAlign() + sub rsp,28 + mov rdx,[rcx+10] + mov rax,rdx + test rax,rax + je short M00_L05 + lea r8,[rax+10] + mov r9d,[rax+8] +M00_L00: + mov eax,r9d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov rax,[rcx+8] + test rax,rax + je short M00_L06 + lea r9,[rax+10] + mov eax,[rax+8] +M00_L01: + mov eax,[rdx+8] + add eax,eax + xor ecx,ecx + test eax,eax + jbe short M00_L03 +M00_L02: + mov edx,ecx + movzx r10d,word ptr [r8+rdx] + movbe [r9+rdx],r10w + add ecx,2 + cmp ecx,eax + jb short M00_L02 +M00_L03: + mov eax,ecx + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor r8d,r8d + xor r9d,r9d + jmp short M00_L00 +M00_L06: + xor r9d,r9d + jmp short M00_L01 +; Total bytes of code 111 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.SerializeOneByOne() + push rdi + push rsi + sub rsp,38 + xor eax,eax + mov [rsp+28],rax + mov rsi,rcx + mov rcx,[rsi+8] + test rcx,rcx + je short M00_L03 + lea rdi,[rcx+10] + mov ecx,[rcx+8] +M00_L00: + mov rcx,rdi + mov rdx,[rsi+10] + test rdx,rdx + jne short M00_L01 + xor eax,eax + xor r8d,r8d + jmp short M00_L02 +M00_L01: + lea rax,[rdx+10] + mov r8d,[rdx+8] +M00_L02: + mov [rsp+28],rax + mov [rsp+30],r8d + lea rdx,[rsp+28] + call qword ptr [7FFEC35E7AB0]; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + mov rax,[rsi+10] + mov eax,[rax+8] + add eax,eax + add rsp,38 + pop rsi + pop rdi + ret +M00_L03: + xor edi,edi + jmp short M00_L00 +; Total bytes of code 100 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + xor eax,eax + mov r8,[rdx] + mov edx,[rdx+8] + xor r9d,r9d + test edx,edx + jle short M01_L01 + nop +M01_L00: + mov r10d,r9d + movzx r10d,word ptr [r8+r10*2] + mov r11d,eax + movbe [rcx+r11],r10w + add eax,2 + inc r9d + cmp r9d,edx + jl short M01_L00 +M01_L01: + ret +; Total bytes of code 46 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeReadUnaligned() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + xor eax,eax + xor r9d,r9d + cmp dword ptr [r8+8],0 + jle short M00_L03 + nop dword ptr [rax] + nop dword ptr [rax] +M00_L02: + mov r8d,eax + movzx r11d,word ptr [r10+r8] + movbe [rdx+r8],r11w + add eax,2 + inc r9d + mov r8,[rcx+10] + cmp [r8+8],r9d + jg short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 143 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeIsAddressLessThan() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov eax,[r8+8] + add eax,eax + mov r8d,eax + add r8,r10 + cmp r10,r8 + jae short M00_L03 +M00_L02: + movzx r9d,word ptr [r10] + movbe [rdx],r9w + add r10,2 + add rdx,2 + cmp r10,r8 + jb short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 126 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingCopyAndAlign() + sub rsp,28 + mov rdx,[rcx+10] + mov rax,rdx + test rax,rax + je short M00_L05 + lea r8,[rax+10] + mov r9d,[rax+8] +M00_L00: + mov eax,r9d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov rax,[rcx+8] + test rax,rax + je short M00_L06 + lea r9,[rax+10] + mov eax,[rax+8] +M00_L01: + mov eax,[rdx+8] + add eax,eax + xor ecx,ecx + test eax,eax + jbe short M00_L03 +M00_L02: + mov edx,ecx + movzx r10d,word ptr [r8+rdx] + movbe [r9+rdx],r10w + add ecx,2 + cmp ecx,eax + jb short M00_L02 +M00_L03: + mov eax,ecx + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor r8d,r8d + xor r9d,r9d + jmp short M00_L00 +M00_L06: + xor r9d,r9d + jmp short M00_L01 +; Total bytes of code 111 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.SerializeOneByOne() + push rdi + push rsi + sub rsp,38 + xor eax,eax + mov [rsp+28],rax + mov rsi,rcx + mov rcx,[rsi+8] + test rcx,rcx + je short M00_L03 + lea rdi,[rcx+10] + mov ecx,[rcx+8] +M00_L00: + mov rcx,rdi + mov rdx,[rsi+10] + test rdx,rdx + jne short M00_L01 + xor eax,eax + xor r8d,r8d + jmp short M00_L02 +M00_L01: + lea rax,[rdx+10] + mov r8d,[rdx+8] +M00_L02: + mov [rsp+28],rax + mov [rsp+30],r8d + lea rdx,[rsp+28] + call qword ptr [7FFEC3607AB0]; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + mov rax,[rsi+10] + mov eax,[rax+8] + add eax,eax + add rsp,38 + pop rsi + pop rdi + ret +M00_L03: + xor edi,edi + jmp short M00_L00 +; Total bytes of code 100 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + xor eax,eax + mov r8,[rdx] + mov edx,[rdx+8] + xor r9d,r9d + test edx,edx + jle short M01_L01 + nop +M01_L00: + mov r10d,r9d + movzx r10d,word ptr [r8+r10*2] + mov r11d,eax + movbe [rcx+r11],r10w + add eax,2 + inc r9d + cmp r9d,edx + jl short M01_L00 +M01_L01: + ret +; Total bytes of code 46 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeReadUnaligned() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + xor eax,eax + xor r9d,r9d + cmp dword ptr [r8+8],0 + jle short M00_L03 + nop dword ptr [rax] + nop dword ptr [rax] +M00_L02: + mov r8d,eax + movzx r11d,word ptr [r10+r8] + movbe [rdx+r8],r11w + add eax,2 + inc r9d + mov r8,[rcx+10] + cmp [r8+8],r9d + jg short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 143 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeIsAddressLessThan() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov eax,[r8+8] + add eax,eax + mov r8d,eax + add r8,r10 + cmp r10,r8 + jae short M00_L03 +M00_L02: + movzx r9d,word ptr [r10] + movbe [rdx],r9w + add r10,2 + add rdx,2 + cmp r10,r8 + jb short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 126 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingCopyAndAlign() + sub rsp,28 + mov rdx,[rcx+10] + mov rax,rdx + test rax,rax + je short M00_L05 + lea r8,[rax+10] + mov r9d,[rax+8] +M00_L00: + mov eax,r9d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov rax,[rcx+8] + test rax,rax + je short M00_L06 + lea r9,[rax+10] + mov eax,[rax+8] +M00_L01: + mov eax,[rdx+8] + add eax,eax + xor ecx,ecx + test eax,eax + jbe short M00_L03 +M00_L02: + mov edx,ecx + movzx r10d,word ptr [r8+rdx] + movbe [r9+rdx],r10w + add ecx,2 + cmp ecx,eax + jb short M00_L02 +M00_L03: + mov eax,ecx + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor r8d,r8d + xor r9d,r9d + jmp short M00_L00 +M00_L06: + xor r9d,r9d + jmp short M00_L01 +; Total bytes of code 111 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.SerializeOneByOne() + push rdi + push rsi + sub rsp,38 + xor eax,eax + mov [rsp+28],rax + mov rsi,rcx + mov rcx,[rsi+8] + test rcx,rcx + je short M00_L03 + lea rdi,[rcx+10] + mov ecx,[rcx+8] +M00_L00: + mov rcx,rdi + mov rdx,[rsi+10] + test rdx,rdx + jne short M00_L01 + xor eax,eax + xor r8d,r8d + jmp short M00_L02 +M00_L01: + lea rax,[rdx+10] + mov r8d,[rdx+8] +M00_L02: + mov [rsp+28],rax + mov [rsp+30],r8d + lea rdx,[rsp+28] + call qword ptr [7FFEC35F7AB0]; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + mov rax,[rsi+10] + mov eax,[rax+8] + add eax,eax + add rsp,38 + pop rsi + pop rdi + ret +M00_L03: + xor edi,edi + jmp short M00_L00 +; Total bytes of code 100 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + xor eax,eax + mov r8,[rdx] + mov edx,[rdx+8] + xor r9d,r9d + test edx,edx + jle short M01_L01 + nop +M01_L00: + mov r10d,r9d + movzx r10d,word ptr [r8+r10*2] + mov r11d,eax + movbe [rcx+r11],r10w + add eax,2 + inc r9d + cmp r9d,edx + jl short M01_L00 +M01_L01: + ret +; Total bytes of code 46 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeReadUnaligned() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + xor eax,eax + xor r9d,r9d + cmp dword ptr [r8+8],0 + jle short M00_L03 + nop dword ptr [rax] + nop dword ptr [rax] +M00_L02: + mov r8d,eax + movzx r11d,word ptr [r10+r8] + movbe [rdx+r8],r11w + add eax,2 + inc r9d + mov r8,[rcx+10] + cmp [r8+8],r9d + jg short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 143 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeIsAddressLessThan() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov eax,[r8+8] + add eax,eax + mov r8d,eax + add r8,r10 + cmp r10,r8 + jae short M00_L03 +M00_L02: + movzx r9d,word ptr [r10] + movbe [rdx],r9w + add r10,2 + add rdx,2 + cmp r10,r8 + jb short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 126 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingCopyAndAlign() + sub rsp,28 + mov rdx,[rcx+10] + mov rax,rdx + test rax,rax + je short M00_L05 + lea r8,[rax+10] + mov r9d,[rax+8] +M00_L00: + mov eax,r9d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov rax,[rcx+8] + test rax,rax + je short M00_L06 + lea r9,[rax+10] + mov eax,[rax+8] +M00_L01: + mov eax,[rdx+8] + add eax,eax + xor ecx,ecx + test eax,eax + jbe short M00_L03 +M00_L02: + mov edx,ecx + movzx r10d,word ptr [r8+rdx] + movbe [r9+rdx],r10w + add ecx,2 + cmp ecx,eax + jb short M00_L02 +M00_L03: + mov eax,ecx + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor r8d,r8d + xor r9d,r9d + jmp short M00_L00 +M00_L06: + xor r9d,r9d + jmp short M00_L01 +; Total bytes of code 111 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.SerializeOneByOne() + push rdi + push rsi + sub rsp,38 + xor eax,eax + mov [rsp+28],rax + mov rsi,rcx + mov rcx,[rsi+8] + test rcx,rcx + je short M00_L03 + lea rdi,[rcx+10] + mov ecx,[rcx+8] +M00_L00: + mov rcx,rdi + mov rdx,[rsi+10] + test rdx,rdx + jne short M00_L01 + xor eax,eax + xor r8d,r8d + jmp short M00_L02 +M00_L01: + lea rax,[rdx+10] + mov r8d,[rdx+8] +M00_L02: + mov [rsp+28],rax + mov [rsp+30],r8d + lea rdx,[rsp+28] + call qword ptr [7FFEC35F7AB0]; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + mov rax,[rsi+10] + mov eax,[rax+8] + add eax,eax + add rsp,38 + pop rsi + pop rdi + ret +M00_L03: + xor edi,edi + jmp short M00_L00 +; Total bytes of code 100 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + xor eax,eax + mov r8,[rdx] + mov edx,[rdx+8] + xor r9d,r9d + test edx,edx + jle short M01_L01 + nop +M01_L00: + mov r10d,r9d + movzx r10d,word ptr [r8+r10*2] + mov r11d,eax + movbe [rcx+r11],r10w + add eax,2 + inc r9d + cmp r9d,edx + jl short M01_L00 +M01_L01: + ret +; Total bytes of code 46 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeReadUnaligned() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + xor eax,eax + xor r9d,r9d + cmp dword ptr [r8+8],0 + jle short M00_L03 + nop dword ptr [rax] + nop dword ptr [rax] +M00_L02: + mov r8d,eax + movzx r11d,word ptr [r10+r8] + movbe [rdx+r8],r11w + add eax,2 + inc r9d + mov r8,[rcx+10] + cmp [r8+8],r9d + jg short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 143 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeIsAddressLessThan() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov eax,[r8+8] + add eax,eax + mov r8d,eax + add r8,r10 + cmp r10,r8 + jae short M00_L03 +M00_L02: + movzx r9d,word ptr [r10] + movbe [rdx],r9w + add r10,2 + add rdx,2 + cmp r10,r8 + jb short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 126 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingCopyAndAlign() + sub rsp,28 + mov rdx,[rcx+10] + mov rax,rdx + test rax,rax + je short M00_L05 + lea r8,[rax+10] + mov r9d,[rax+8] +M00_L00: + mov eax,r9d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov rax,[rcx+8] + test rax,rax + je short M00_L06 + lea r9,[rax+10] + mov eax,[rax+8] +M00_L01: + mov eax,[rdx+8] + add eax,eax + xor ecx,ecx + test eax,eax + jbe short M00_L03 +M00_L02: + mov edx,ecx + movzx r10d,word ptr [r8+rdx] + movbe [r9+rdx],r10w + add ecx,2 + cmp ecx,eax + jb short M00_L02 +M00_L03: + mov eax,ecx + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor r8d,r8d + xor r9d,r9d + jmp short M00_L00 +M00_L06: + xor r9d,r9d + jmp short M00_L01 +; Total bytes of code 111 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.SerializeOneByOne() + push rdi + push rsi + sub rsp,38 + xor eax,eax + mov [rsp+28],rax + mov rsi,rcx + mov rcx,[rsi+8] + test rcx,rcx + je short M00_L03 + lea rdi,[rcx+10] + mov ecx,[rcx+8] +M00_L00: + mov rcx,rdi + mov rdx,[rsi+10] + test rdx,rdx + jne short M00_L01 + xor eax,eax + xor r8d,r8d + jmp short M00_L02 +M00_L01: + lea rax,[rdx+10] + mov r8d,[rdx+8] +M00_L02: + mov [rsp+28],rax + mov [rsp+30],r8d + lea rdx,[rsp+28] + call qword ptr [7FFEC35F7AB0]; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + mov rax,[rsi+10] + mov eax,[rax+8] + add eax,eax + add rsp,38 + pop rsi + pop rdi + ret +M00_L03: + xor edi,edi + jmp short M00_L00 +; Total bytes of code 100 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + xor eax,eax + mov r8,[rdx] + mov edx,[rdx+8] + xor r9d,r9d + test edx,edx + jle short M01_L01 + nop +M01_L00: + mov r10d,r9d + movzx r10d,word ptr [r8+r10*2] + mov r11d,eax + movbe [rcx+r11],r10w + add eax,2 + inc r9d + cmp r9d,edx + jl short M01_L00 +M01_L01: + ret +; Total bytes of code 46 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeReadUnaligned() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + xor eax,eax + xor r9d,r9d + cmp dword ptr [r8+8],0 + jle short M00_L03 + nop dword ptr [rax] + nop dword ptr [rax] +M00_L02: + mov r8d,eax + movzx r11d,word ptr [r10+r8] + movbe [rdx+r8],r11w + add eax,2 + inc r9d + mov r8,[rcx+10] + cmp [r8+8],r9d + jg short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 143 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeIsAddressLessThan() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov eax,[r8+8] + add eax,eax + mov r8d,eax + add r8,r10 + cmp r10,r8 + jae short M00_L03 +M00_L02: + movzx r9d,word ptr [r10] + movbe [rdx],r9w + add r10,2 + add rdx,2 + cmp r10,r8 + jb short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 126 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingCopyAndAlign() + sub rsp,28 + mov rdx,[rcx+10] + mov rax,rdx + test rax,rax + je short M00_L05 + lea r8,[rax+10] + mov r9d,[rax+8] +M00_L00: + mov eax,r9d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov rax,[rcx+8] + test rax,rax + je short M00_L06 + lea r9,[rax+10] + mov eax,[rax+8] +M00_L01: + mov eax,[rdx+8] + add eax,eax + xor ecx,ecx + test eax,eax + jbe short M00_L03 +M00_L02: + mov edx,ecx + movzx r10d,word ptr [r8+rdx] + movbe [r9+rdx],r10w + add ecx,2 + cmp ecx,eax + jb short M00_L02 +M00_L03: + mov eax,ecx + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor r8d,r8d + xor r9d,r9d + jmp short M00_L00 +M00_L06: + xor r9d,r9d + jmp short M00_L01 +; Total bytes of code 111 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.SerializeOneByOne() + push rdi + push rsi + sub rsp,38 + xor eax,eax + mov [rsp+28],rax + mov rsi,rcx + mov rcx,[rsi+8] + test rcx,rcx + je short M00_L03 + lea rdi,[rcx+10] + mov ecx,[rcx+8] +M00_L00: + mov rcx,rdi + mov rdx,[rsi+10] + test rdx,rdx + jne short M00_L01 + xor eax,eax + xor r8d,r8d + jmp short M00_L02 +M00_L01: + lea rax,[rdx+10] + mov r8d,[rdx+8] +M00_L02: + mov [rsp+28],rax + mov [rsp+30],r8d + lea rdx,[rsp+28] + call qword ptr [7FFEC3607AB0]; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + mov rax,[rsi+10] + mov eax,[rax+8] + add eax,eax + add rsp,38 + pop rsi + pop rdi + ret +M00_L03: + xor edi,edi + jmp short M00_L00 +; Total bytes of code 100 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + xor eax,eax + mov r8,[rdx] + mov edx,[rdx+8] + xor r9d,r9d + test edx,edx + jle short M01_L01 + nop +M01_L00: + mov r10d,r9d + movzx r10d,word ptr [r8+r10*2] + mov r11d,eax + movbe [rcx+r11],r10w + add eax,2 + inc r9d + cmp r9d,edx + jl short M01_L00 +M01_L01: + ret +; Total bytes of code 46 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeReadUnaligned() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + xor eax,eax + xor r9d,r9d + cmp dword ptr [r8+8],0 + jle short M00_L03 + nop dword ptr [rax] + nop dword ptr [rax] +M00_L02: + mov r8d,eax + movzx r11d,word ptr [r10+r8] + movbe [rdx+r8],r11w + add eax,2 + inc r9d + mov r8,[rcx+10] + cmp [r8+8],r9d + jg short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 143 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeIsAddressLessThan() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov eax,[r8+8] + add eax,eax + mov r8d,eax + add r8,r10 + cmp r10,r8 + jae short M00_L03 +M00_L02: + movzx r9d,word ptr [r10] + movbe [rdx],r9w + add r10,2 + add rdx,2 + cmp r10,r8 + jb short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 126 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingCopyAndAlign() + sub rsp,28 + mov rdx,[rcx+10] + mov rax,rdx + test rax,rax + je short M00_L05 + lea r8,[rax+10] + mov r9d,[rax+8] +M00_L00: + mov eax,r9d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov rax,[rcx+8] + test rax,rax + je short M00_L06 + lea r9,[rax+10] + mov eax,[rax+8] +M00_L01: + mov eax,[rdx+8] + add eax,eax + xor ecx,ecx + test eax,eax + jbe short M00_L03 +M00_L02: + mov edx,ecx + movzx r10d,word ptr [r8+rdx] + movbe [r9+rdx],r10w + add ecx,2 + cmp ecx,eax + jb short M00_L02 +M00_L03: + mov eax,ecx + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor r8d,r8d + xor r9d,r9d + jmp short M00_L00 +M00_L06: + xor r9d,r9d + jmp short M00_L01 +; Total bytes of code 111 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.SerializeOneByOne() + push rdi + push rsi + sub rsp,38 + xor eax,eax + mov [rsp+28],rax + mov rsi,rcx + mov rcx,[rsi+8] + test rcx,rcx + je short M00_L03 + lea rdi,[rcx+10] + mov ecx,[rcx+8] +M00_L00: + mov rcx,rdi + mov rdx,[rsi+10] + test rdx,rdx + jne short M00_L01 + xor eax,eax + xor r8d,r8d + jmp short M00_L02 +M00_L01: + lea rax,[rdx+10] + mov r8d,[rdx+8] +M00_L02: + mov [rsp+28],rax + mov [rsp+30],r8d + lea rdx,[rsp+28] + call qword ptr [7FFEC3607AB0]; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + mov rax,[rsi+10] + mov eax,[rax+8] + add eax,eax + add rsp,38 + pop rsi + pop rdi + ret +M00_L03: + xor edi,edi + jmp short M00_L00 +; Total bytes of code 100 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray.WriteUInt16(Byte ByRef, System.ReadOnlySpan`1) + xor eax,eax + mov r8,[rdx] + mov edx,[rdx+8] + xor r9d,r9d + test edx,edx + jle short M01_L01 + nop +M01_L00: + mov r10d,r9d + movzx r10d,word ptr [r8+r10*2] + mov r11d,eax + movbe [rcx+r11],r10w + add eax,2 + inc r9d + cmp r9d,edx + jl short M01_L00 +M01_L01: + ret +; Total bytes of code 46 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeReadUnaligned() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + xor eax,eax + xor r9d,r9d + cmp dword ptr [r8+8],0 + jle short M00_L03 + nop dword ptr [rax] + nop dword ptr [rax] +M00_L02: + mov r8d,eax + movzx r11d,word ptr [r10+r8] + movbe [rdx+r8],r11w + add eax,2 + inc r9d + mov r8,[rcx+10] + cmp [r8+8],r9d + jg short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 143 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingUnsafeIsAddressLessThan() + sub rsp,28 + mov rax,[rcx+8] + test rax,rax + je short M00_L05 + lea rdx,[rax+10] + mov eax,[rax+8] + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + je short M00_L06 +M00_L00: + lea r10,[r9+10] + mov r11d,[r9+8] +M00_L01: + mov eax,r11d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov eax,[r8+8] + add eax,eax + mov r8d,eax + add r8,r10 + cmp r10,r8 + jae short M00_L03 +M00_L02: + movzx r9d,word ptr [r10] + movbe [rdx],r9w + add r10,2 + add rdx,2 + cmp r10,r8 + jb short M00_L02 +M00_L03: + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor edx,edx + mov r8,[rcx+10] + mov r9,r8 + test r9,r9 + jne short M00_L00 +M00_L06: + xor r10d,r10d + xor r11d,r11d + jmp short M00_L01 +; Total bytes of code 126 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeArray+SerializeUInt16.UsingCopyAndAlign() + sub rsp,28 + mov rdx,[rcx+10] + mov rax,rdx + test rax,rax + je short M00_L05 + lea r8,[rax+10] + mov r9d,[rax+8] +M00_L00: + mov eax,r9d + add rax,rax + cmp rax,7FFFFFFF + ja short M00_L04 + mov rax,[rcx+8] + test rax,rax + je short M00_L06 + lea r9,[rax+10] + mov eax,[rax+8] +M00_L01: + mov eax,[rdx+8] + add eax,eax + xor ecx,ecx + test eax,eax + jbe short M00_L03 +M00_L02: + mov edx,ecx + movzx r10d,word ptr [r8+rdx] + movbe [r9+rdx],r10w + add ecx,2 + cmp ecx,eax + jb short M00_L02 +M00_L03: + mov eax,ecx + add rsp,28 + ret +M00_L04: + call CORINFO_HELP_OVERFLOW +M00_L05: + xor r8d,r8d + xor r9d,r9d + jmp short M00_L00 +M00_L06: + xor r9d,r9d + jmp short M00_L01 +; Total bytes of code 111 +``` + diff --git a/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializeArray.SerializeUInt16-report-github.md b/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializeArray.SerializeUInt16-report-github.md new file mode 100644 index 0000000..ae67115 --- /dev/null +++ b/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializeArray.SerializeUInt16-report-github.md @@ -0,0 +1,61 @@ +``` ini + +BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.3448/22H2/2022Update) +Intel Core i9-9900K CPU 3.60GHz (Coffee Lake), 1 CPU, 16 logical and 8 physical cores +.NET SDK=7.0.401 + [Host] : .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 + Job-DPCBSB : .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 + +IterationTime=150.0000 ms WarmupCount=2 + +``` +| Method | Value | Mean | Error | StdDev | Median | Ratio | RatioSD | Completed Work Items | Lock Contentions | Code Size | Allocated | Alloc Ratio | +|----------------------------- |----------- |----------:|----------:|----------:|----------:|------:|--------:|---------------------:|-----------------:|----------:|----------:|------------:| +| **SerializeOneByOne** | **UInt16[10]** | **6.7676 ns** | **0.1555 ns** | **0.1379 ns** | **6.7697 ns** | **1.00** | **0.00** | **-** | **-** | **146 B** | **-** | **NA** | +| UsingUnsafeReadUnaligned | UInt16[10] | 6.7781 ns | 0.1524 ns | 0.1426 ns | 6.7463 ns | 1.00 | 0.03 | - | - | 143 B | - | NA | +| UsingUnsafeIsAddressLessThan | UInt16[10] | 5.3003 ns | 0.1409 ns | 0.3265 ns | 5.2767 ns | 0.79 | 0.06 | - | - | 126 B | - | NA | +| UsingCopyAndAlign | UInt16[10] | 5.1882 ns | 0.1385 ns | 0.3696 ns | 5.1348 ns | 0.78 | 0.05 | - | - | 111 B | - | NA | +| **SerializeOneByOne** | **UInt16[11]** | **7.3683 ns** | **0.1758 ns** | **0.1881 ns** | **7.4333 ns** | **1.09** | **0.04** | **-** | **-** | **146 B** | **-** | **NA** | +| UsingUnsafeReadUnaligned | UInt16[11] | 7.7785 ns | 0.1900 ns | 0.2901 ns | 7.7991 ns | 1.17 | 0.04 | - | - | 143 B | - | NA | +| UsingUnsafeIsAddressLessThan | UInt16[11] | 5.3097 ns | 0.1380 ns | 0.1291 ns | 5.3539 ns | 0.78 | 0.03 | - | - | 126 B | - | NA | +| UsingCopyAndAlign | UInt16[11] | 5.0787 ns | 0.1331 ns | 0.2187 ns | 5.1052 ns | 0.75 | 0.05 | - | - | 111 B | - | NA | +| **SerializeOneByOne** | **UInt16[12]** | **7.4508 ns** | **0.1809 ns** | **0.1692 ns** | **7.4458 ns** | **1.10** | **0.03** | **-** | **-** | **146 B** | **-** | **NA** | +| UsingUnsafeReadUnaligned | UInt16[12] | 7.5599 ns | 0.1877 ns | 0.3238 ns | 7.5065 ns | 1.11 | 0.06 | - | - | 143 B | - | NA | +| UsingUnsafeIsAddressLessThan | UInt16[12] | 5.6171 ns | 0.1465 ns | 0.2527 ns | 5.6522 ns | 0.83 | 0.03 | - | - | 126 B | - | NA | +| UsingCopyAndAlign | UInt16[12] | 5.5608 ns | 0.1420 ns | 0.1578 ns | 5.5577 ns | 0.82 | 0.03 | - | - | 111 B | - | NA | +| **SerializeOneByOne** | **UInt16[1]** | **1.8980 ns** | **0.0737 ns** | **0.1232 ns** | **1.8702 ns** | **0.29** | **0.02** | **-** | **-** | **146 B** | **-** | **NA** | +| UsingUnsafeReadUnaligned | UInt16[1] | 0.9585 ns | 0.0520 ns | 0.0779 ns | 0.9602 ns | 0.14 | 0.01 | - | - | 143 B | - | NA | +| UsingUnsafeIsAddressLessThan | UInt16[1] | 0.7194 ns | 0.0490 ns | 0.0638 ns | 0.7136 ns | 0.11 | 0.01 | - | - | 126 B | - | NA | +| UsingCopyAndAlign | UInt16[1] | 0.8293 ns | 0.0495 ns | 0.1269 ns | 0.7978 ns | 0.12 | 0.02 | - | - | 111 B | - | NA | +| **SerializeOneByOne** | **UInt16[2]** | **2.4908 ns** | **0.0801 ns** | **0.0749 ns** | **2.4860 ns** | **0.37** | **0.01** | **-** | **-** | **146 B** | **-** | **NA** | +| UsingUnsafeReadUnaligned | UInt16[2] | 1.4594 ns | 0.0609 ns | 0.0677 ns | 1.4766 ns | 0.22 | 0.01 | - | - | 143 B | - | NA | +| UsingUnsafeIsAddressLessThan | UInt16[2] | 1.2157 ns | 0.0515 ns | 0.0481 ns | 1.2196 ns | 0.18 | 0.01 | - | - | 126 B | - | NA | +| UsingCopyAndAlign | UInt16[2] | 1.4421 ns | 0.0613 ns | 0.0797 ns | 1.4363 ns | 0.21 | 0.01 | - | - | 111 B | - | NA | +| **SerializeOneByOne** | **UInt16[3]** | **3.2555 ns** | **0.0968 ns** | **0.1325 ns** | **3.2564 ns** | **0.48** | **0.02** | **-** | **-** | **146 B** | **-** | **NA** | +| UsingUnsafeReadUnaligned | UInt16[3] | 2.1776 ns | 0.0761 ns | 0.0876 ns | 2.1652 ns | 0.32 | 0.02 | - | - | 143 B | - | NA | +| UsingUnsafeIsAddressLessThan | UInt16[3] | 1.4623 ns | 0.0634 ns | 0.0623 ns | 1.4698 ns | 0.22 | 0.01 | - | - | 126 B | - | NA | +| UsingCopyAndAlign | UInt16[3] | 1.4795 ns | 0.0603 ns | 0.0564 ns | 1.4762 ns | 0.22 | 0.01 | - | - | 111 B | - | NA | +| **SerializeOneByOne** | **UInt16[4]** | **3.6758 ns** | **0.1063 ns** | **0.1455 ns** | **3.6887 ns** | **0.54** | **0.02** | **-** | **-** | **146 B** | **-** | **NA** | +| UsingUnsafeReadUnaligned | UInt16[4] | 2.7321 ns | 0.0778 ns | 0.0690 ns | 2.7248 ns | 0.40 | 0.01 | - | - | 143 B | - | NA | +| UsingUnsafeIsAddressLessThan | UInt16[4] | 1.9155 ns | 0.0722 ns | 0.1059 ns | 1.9169 ns | 0.28 | 0.02 | - | - | 126 B | - | NA | +| UsingCopyAndAlign | UInt16[4] | 1.9845 ns | 0.0709 ns | 0.0896 ns | 1.9916 ns | 0.29 | 0.01 | - | - | 111 B | - | NA | +| **SerializeOneByOne** | **UInt16[5]** | **4.6423 ns** | **0.1260 ns** | **0.3318 ns** | **4.5721 ns** | **0.66** | **0.03** | **-** | **-** | **146 B** | **-** | **NA** | +| UsingUnsafeReadUnaligned | UInt16[5] | 4.1888 ns | 0.0988 ns | 0.1098 ns | 4.1633 ns | 0.62 | 0.02 | - | - | 143 B | - | NA | +| UsingUnsafeIsAddressLessThan | UInt16[5] | 2.2390 ns | 0.0579 ns | 0.0541 ns | 2.2507 ns | 0.33 | 0.01 | - | - | 126 B | - | NA | +| UsingCopyAndAlign | UInt16[5] | 2.4711 ns | 0.0823 ns | 0.1256 ns | 2.4921 ns | 0.36 | 0.02 | - | - | 111 B | - | NA | +| **SerializeOneByOne** | **UInt16[6]** | **4.9294 ns** | **0.1281 ns** | **0.1711 ns** | **4.8601 ns** | **0.72** | **0.03** | **-** | **-** | **146 B** | **-** | **NA** | +| UsingUnsafeReadUnaligned | UInt16[6] | 4.9451 ns | 0.1270 ns | 0.1462 ns | 4.9462 ns | 0.72 | 0.03 | - | - | 143 B | - | NA | +| UsingUnsafeIsAddressLessThan | UInt16[6] | 2.9418 ns | 0.0906 ns | 0.1241 ns | 2.9539 ns | 0.43 | 0.02 | - | - | 126 B | - | NA | +| UsingCopyAndAlign | UInt16[6] | 2.8518 ns | 0.0906 ns | 0.1178 ns | 2.8391 ns | 0.42 | 0.02 | - | - | 111 B | - | NA | +| **SerializeOneByOne** | **UInt16[7]** | **5.6504 ns** | **0.1468 ns** | **0.2328 ns** | **5.6487 ns** | **0.83** | **0.05** | **-** | **-** | **146 B** | **-** | **NA** | +| UsingUnsafeReadUnaligned | UInt16[7] | 5.1182 ns | 0.1379 ns | 0.3486 ns | 5.0251 ns | 0.82 | 0.05 | - | - | 143 B | - | NA | +| UsingUnsafeIsAddressLessThan | UInt16[7] | 3.1176 ns | 0.0946 ns | 0.1127 ns | 3.1385 ns | 0.46 | 0.02 | - | - | 126 B | - | NA | +| UsingCopyAndAlign | UInt16[7] | 3.0415 ns | 0.0908 ns | 0.1614 ns | 3.0384 ns | 0.45 | 0.03 | - | - | 111 B | - | NA | +| **SerializeOneByOne** | **UInt16[8]** | **5.8796 ns** | **0.1415 ns** | **0.1514 ns** | **5.8546 ns** | **0.87** | **0.03** | **-** | **-** | **146 B** | **-** | **NA** | +| UsingUnsafeReadUnaligned | UInt16[8] | 5.8337 ns | 0.1530 ns | 0.1989 ns | 5.8520 ns | 0.87 | 0.03 | - | - | 143 B | - | NA | +| UsingUnsafeIsAddressLessThan | UInt16[8] | 3.5075 ns | 0.1050 ns | 0.1167 ns | 3.4913 ns | 0.51 | 0.01 | - | - | 126 B | - | NA | +| UsingCopyAndAlign | UInt16[8] | 3.6621 ns | 0.1054 ns | 0.1477 ns | 3.6645 ns | 0.54 | 0.03 | - | - | 111 B | - | NA | +| **SerializeOneByOne** | **UInt16[9]** | **6.2735 ns** | **0.1208 ns** | **0.1009 ns** | **6.2891 ns** | **0.93** | **0.03** | **-** | **-** | **146 B** | **-** | **NA** | +| UsingUnsafeReadUnaligned | UInt16[9] | 6.5691 ns | 0.1677 ns | 0.4051 ns | 6.4135 ns | 0.95 | 0.04 | - | - | 143 B | - | NA | +| UsingUnsafeIsAddressLessThan | UInt16[9] | 4.1082 ns | 0.1027 ns | 0.0961 ns | 4.1170 ns | 0.61 | 0.01 | - | - | 126 B | - | NA | +| UsingCopyAndAlign | UInt16[9] | 4.3313 ns | 0.1195 ns | 0.1423 ns | 4.3350 ns | 0.63 | 0.02 | - | - | 111 B | - | NA | diff --git a/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializeJobRequestHeader-asm.md b/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializeJobRequestHeader-asm.md new file mode 100644 index 0000000..5a1e945 --- /dev/null +++ b/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializeJobRequestHeader-asm.md @@ -0,0 +1,172 @@ +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeJobRequestHeader.WriteFieldsOneByOne() + mov rax,[rcx+8] + test rax,rax + je short M00_L01 + lea rdx,[rax+10] + mov eax,[rax+8] +M00_L00: + mov dword ptr [rdx],132 + mov word ptr [rdx+4],1 + mov word ptr [rdx+6],0A00 + mov word ptr [rdx+8],1400 + mov eax,0A + ret +M00_L01: + xor edx,edx + jmp short M00_L00 +; Total bytes of code 50 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeJobRequestHeader.WriteLongThenShort() + mov rax,[rcx+8] + test rax,rax + je short M00_L01 + lea rdx,[rax+10] + mov eax,[rax+8] +M00_L00: + mov rax,[7FFE6DDADED0] + or rax,0A + movbe [rdx],rax + mov word ptr [rdx+8],1400 + mov eax,0A + ret +M00_L01: + xor edx,edx + jmp short M00_L00 +; Total bytes of code 48 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeJobRequestHeader.WriteVector128() + vzeroupper + mov rax,[rcx+8] + test rax,rax + je short M00_L01 + lea rdx,[rax+10] + mov eax,[rax+8] +M00_L00: + vmovupd xmm0,[7FFE6DA76730] + vmovdqu xmmword ptr [rdx],xmm0 + mov eax,0A + ret +M00_L01: + xor edx,edx + jmp short M00_L00 +; Total bytes of code 41 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeJobRequestHeader.WriteArray() + sub rsp,28 + mov rcx,[rcx+8] + test rcx,rcx + je short M00_L01 + lea rax,[rcx+10] + mov ecx,[rcx+8] +M00_L00: + mov rcx,rax + mov edx,0A + mov r8d,14 + call qword ptr [7FFE6DD979A8]; Sally7.Benchmarks.Serialization.SerializeJobRequestHeader.WriteArray(Byte ByRef, Int32, Int32) + nop + add rsp,28 + ret +M00_L01: + xor eax,eax + jmp short M00_L00 +; Total bytes of code 50 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializeJobRequestHeader.WriteArray(Byte ByRef, Int32, Int32) + sub rsp,38 + xor eax,eax + mov [rsp+20],rax + mov [rsp+28],rax + mov rax,3B80E53A0CC5 + mov [rsp+30],rax + lea rax,[rsp+20] + mov word ptr [rax],3201 + mov word ptr [rax+2],0 + mov word ptr [rax+4],100 + mov [rax+6],dx + mov [rax+8],r8w + movzx edx,word ptr [rax] + movbe [rax],dx + movzx edx,word ptr [rax+4] + movbe [rax+4],dx + movzx edx,word ptr [rax+6] + movbe [rax+6],dx + movzx edx,word ptr [rax+8] + movbe [rax+8],dx + mov rdx,[rax] + mov [rcx],rdx + mov rdx,[rax+2] + mov [rcx+2],rdx + mov eax,0A + mov rcx,3B80E53A0CC5 + cmp [rsp+30],rcx + je short M01_L00 + call CORINFO_HELP_FAIL_FAST +M01_L00: + nop + add rsp,38 + ret +; Total bytes of code 147 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializeJobRequestHeader.WriteStruct() + sub rsp,28 + mov rcx,[rcx+8] + test rcx,rcx + je short M00_L01 + lea rax,[rcx+10] + mov ecx,[rcx+8] +M00_L00: + mov rcx,rax + mov edx,0A + mov r8d,14 + call qword ptr [7FFE6DDC79C0]; Sally7.Benchmarks.Serialization.SerializeJobRequestHeader.WriteStruct(Byte ByRef, Int32, Int32) + nop + add rsp,28 + ret +M00_L01: + xor eax,eax + jmp short M00_L00 +; Total bytes of code 50 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializeJobRequestHeader.WriteStruct(Byte ByRef, Int32, Int32) + sub rsp,18 + xor eax,eax + mov [rsp+8],rax + mov [rsp+0A],rax + mov byte ptr [rsp+8],32 + mov byte ptr [rsp+9],1 + mov word ptr [rsp+0A],0 + mov word ptr [rsp+0C],1 + movzx eax,dx + ror ax,8 + movzx eax,ax + mov [rsp+0E],ax + movzx eax,r8w + ror ax,8 + movzx eax,ax + mov [rsp+10],ax + mov rax,[rsp+8] + mov [rcx],rax + mov rax,[rsp+0A] + mov [rcx+2],rax + mov eax,0A + add rsp,18 + ret +; Total bytes of code 98 +``` + diff --git a/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializeJobRequestHeader-report-github.md b/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializeJobRequestHeader-report-github.md new file mode 100644 index 0000000..3e54aeb --- /dev/null +++ b/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializeJobRequestHeader-report-github.md @@ -0,0 +1,17 @@ +``` ini + +BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.3448/22H2/2022Update) +Intel Core i9-9900K CPU 3.60GHz (Coffee Lake), 1 CPU, 16 logical and 8 physical cores +.NET SDK=7.0.401 + [Host] : .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 + DefaultJob : .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 + + +``` +| Method | Mean | Error | StdDev | Median | Ratio | RatioSD | Completed Work Items | Lock Contentions | Code Size | Allocated | Alloc Ratio | +|-------------------- |----------:|----------:|----------:|----------:|------:|--------:|---------------------:|-----------------:|----------:|----------:|------------:| +| WriteFieldsOneByOne | 0.2237 ns | 0.0365 ns | 0.0929 ns | 0.2225 ns | 1.00 | 0.00 | - | - | 50 B | - | NA | +| WriteLongThenShort | 0.2529 ns | 0.0352 ns | 0.0329 ns | 0.2551 ns | 1.00 | 0.69 | - | - | 48 B | - | NA | +| WriteVector128 | 0.0072 ns | 0.0105 ns | 0.0166 ns | 0.0000 ns | 0.03 | 0.07 | - | - | 41 B | - | NA | +| WriteArray | 6.7946 ns | 0.1702 ns | 0.2442 ns | 6.8168 ns | 35.15 | 16.45 | - | - | 197 B | - | NA | +| WriteStruct | 5.7721 ns | 0.1242 ns | 0.1275 ns | 5.7772 ns | 24.70 | 14.39 | - | - | 148 B | - | NA | diff --git a/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializePrimitives-asm.md b/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializePrimitives-asm.md new file mode 100644 index 0000000..ca33a3e --- /dev/null +++ b/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializePrimitives-asm.md @@ -0,0 +1,181 @@ +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializePrimitives.WriteUInt64() + mov rax,[rcx+10] + test rax,rax + je short M00_L01 + lea rdx,[rax+10] + mov eax,[rax+8] +M00_L00: + mov rcx,[rcx+18] + movbe [rdx],rcx + ret +M00_L01: + xor edx,edx + jmp short M00_L00 +; Total bytes of code 30 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializePrimitives.WriteUInt64ArrayIncrementOffset() + push rdi + push rsi + sub rsp,38 + xor eax,eax + mov [rsp+28],rax + mov [rsp+50],rcx + mov rdx,[rcx+10] + test rdx,rdx + je short M00_L01 + lea rsi,[rdx+10] + mov edi,[rdx+8] +M00_L00: + mov [rsp+28],rsi + mov [rsp+30],edi + lea rcx,[rsp+28] + mov rax,[rsp+50] + mov rdx,[rax+8] + call qword ptr [7FFE6DEFB018]; Sally7.Benchmarks.Serialization.SerializePrimitives+Converters.AppendUInt64ArrayIncrementDestination(System.Span`1, UInt64[]) + nop + add rsp,38 + pop rsi + pop rdi + ret +M00_L01: + xor esi,esi + xor edi,edi + jmp short M00_L00 +; Total bytes of code 77 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializePrimitives+Converters.AppendUInt64ArrayIncrementDestination(System.Span`1, UInt64[]) + mov rax,[rcx] + xor ecx,ecx + mov r8d,[rdx+8] + test r8d,r8d + jle short M01_L01 + xchg ax,ax +M01_L00: + mov r9d,ecx + mov r9,[rdx+r9*8+10] + movbe [rax],r9 + add rax,8 + inc ecx + cmp r8d,ecx + jg short M01_L00 +M01_L01: + ret +; Total bytes of code 41 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializePrimitives.WriteUInt64ArrayCalculateOffset() + push rdi + push rsi + sub rsp,38 + xor eax,eax + mov [rsp+28],rax + mov [rsp+50],rcx + mov rdx,[rcx+10] + test rdx,rdx + je short M00_L01 + lea rsi,[rdx+10] + mov edi,[rdx+8] +M00_L00: + mov [rsp+28],rsi + mov [rsp+30],edi + lea rcx,[rsp+28] + mov rax,[rsp+50] + mov rdx,[rax+8] + call qword ptr [7FFE6DEEB030]; Sally7.Benchmarks.Serialization.SerializePrimitives+Converters.AppendUInt64ArrayCalculateOffset(System.Span`1, UInt64[]) + nop + add rsp,38 + pop rsi + pop rdi + ret +M00_L01: + xor esi,esi + xor edi,edi + jmp short M00_L00 +; Total bytes of code 77 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializePrimitives+Converters.AppendUInt64ArrayCalculateOffset(System.Span`1, UInt64[]) + mov rax,[rcx] + xor ecx,ecx + mov r8d,[rdx+8] + test r8d,r8d + je short M01_L01 + xchg ax,ax +M01_L00: + mov r9d,ecx + mov r9,[rdx+r9*8+10] + mov r10d,ecx + shl r10d,3 + movbe [rax+r10],r9 + inc ecx + cmp r8d,ecx + ja short M01_L00 +M01_L01: + ret +; Total bytes of code 45 +``` + +## .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.Serialization.SerializePrimitives.WriteUInt64ArrayStoreOffset() + push rdi + push rsi + sub rsp,38 + xor eax,eax + mov [rsp+28],rax + mov [rsp+50],rcx + mov rdx,[rcx+10] + test rdx,rdx + je short M00_L01 + lea rsi,[rdx+10] + mov edi,[rdx+8] +M00_L00: + mov [rsp+28],rsi + mov [rsp+30],edi + lea rcx,[rsp+28] + mov rax,[rsp+50] + mov rdx,[rax+8] + call qword ptr [7FFE6DEEB048]; Sally7.Benchmarks.Serialization.SerializePrimitives+Converters.AppendUInt64ArrayStoreOffset(System.Span`1, UInt64[]) + nop + add rsp,38 + pop rsi + pop rdi + ret +M00_L01: + xor esi,esi + xor edi,edi + jmp short M00_L00 +; Total bytes of code 77 +``` +```assembly +; Sally7.Benchmarks.Serialization.SerializePrimitives+Converters.AppendUInt64ArrayStoreOffset(System.Span`1, UInt64[]) + mov rax,[rcx] + xor ecx,ecx + xor r8d,r8d + mov r9d,[rdx+8] + test r9d,r9d + jle short M01_L01 + nop dword ptr [rax] + nop dword ptr [rax+rax] +M01_L00: + mov r10d,r8d + mov r10,[rdx+r10*8+10] + mov r11d,ecx + movbe [rax+r11],r10 + add ecx,8 + inc r8d + cmp r9d,r8d + jg short M01_L00 +M01_L01: + ret +; Total bytes of code 61 +``` + diff --git a/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializePrimitives-report-github.md b/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializePrimitives-report-github.md new file mode 100644 index 0000000..1656b7e --- /dev/null +++ b/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.Serialization.SerializePrimitives-report-github.md @@ -0,0 +1,16 @@ +``` ini + +BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.3448/22H2/2022Update) +Intel Core i9-9900K CPU 3.60GHz (Coffee Lake), 1 CPU, 16 logical and 8 physical cores +.NET SDK=7.0.401 + [Host] : .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 + DefaultJob : .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2 + + +``` +| Method | Mean | Error | StdDev | Code Size | Completed Work Items | Lock Contentions | Allocated | +|-------------------------------- |----------:|----------:|----------:|----------:|---------------------:|-----------------:|----------:| +| WriteUInt64 | 0.2361 ns | 0.0352 ns | 0.0376 ns | 30 B | - | - | - | +| WriteUInt64ArrayIncrementOffset | 2.2491 ns | 0.0758 ns | 0.1203 ns | 118 B | - | - | - | +| WriteUInt64ArrayCalculateOffset | 2.2776 ns | 0.0768 ns | 0.0944 ns | 122 B | - | - | - | +| WriteUInt64ArrayStoreOffset | 2.2588 ns | 0.0736 ns | 0.1079 ns | 138 B | - | - | - | diff --git a/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.SerializeJobRequestHeader-asm.md b/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.SerializeJobRequestHeader-asm.md new file mode 100644 index 0000000..c3c7fb1 --- /dev/null +++ b/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.SerializeJobRequestHeader-asm.md @@ -0,0 +1,152 @@ +## .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.SerializeJobRequestHeader.WriteFieldsOneByOne() + mov rax,[rcx+8] + test rax,rax + je short M00_L01 + lea rdx,[rax+10] + mov eax,[rax+8] +M00_L00: + mov dword ptr [rdx],132 + mov word ptr [rdx+4],1 + mov word ptr [rdx+6],0A00 + mov word ptr [rdx+8],1400 + mov eax,0A + ret +M00_L01: + xor edx,edx + jmp short M00_L00 +; Total bytes of code 50 +``` + +## .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.SerializeJobRequestHeader.WriteLongThenShort() + mov rax,[rcx+8] + test rax,rax + je short M00_L01 + lea rdx,[rax+10] + mov eax,[rax+8] +M00_L00: + mov rax,[7FFA08064A28] + or rax,0A + movbe [rdx],rax + mov word ptr [rdx+8],1400 + mov eax,0A + ret +M00_L01: + xor edx,edx + jmp short M00_L00 +; Total bytes of code 48 +``` + +## .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.SerializeJobRequestHeader.WriteArray() + sub rsp,28 + mov rcx,[rcx+8] + test rcx,rcx + je short M00_L01 + lea rax,[rcx+10] + mov ecx,[rcx+8] +M00_L00: + mov rcx,rax + mov edx,0A + mov r8d,14 + call qword ptr [7FFA08071708]; Sally7.Benchmarks.SerializeJobRequestHeader.WriteArray(Byte ByRef, Int32, Int32) + nop + add rsp,28 + ret +M00_L01: + xor eax,eax + jmp short M00_L00 +; Total bytes of code 50 +``` +```assembly +; Sally7.Benchmarks.SerializeJobRequestHeader.WriteArray(Byte ByRef, Int32, Int32) + sub rsp,38 + xor eax,eax + mov [rsp+20],rax + mov [rsp+28],rax + mov rax,81801D716DE9 + mov [rsp+30],rax + lea rax,[rsp+20] + mov word ptr [rax],3201 + mov word ptr [rax+2],0 + mov word ptr [rax+4],100 + mov [rax+6],dx + mov [rax+8],r8w + movzx edx,word ptr [rax] + movbe [rax],dx + movzx edx,word ptr [rax+4] + movbe [rax+4],dx + movzx edx,word ptr [rax+6] + movbe [rax+6],dx + movzx edx,word ptr [rax+8] + movbe [rax+8],dx + mov rdx,[rax] + mov [rcx],rdx + mov rdx,[rax+2] + mov [rcx+2],rdx + mov eax,0A + mov rcx,81801D716DE9 + cmp [rsp+30],rcx + je short M01_L00 + call CORINFO_HELP_FAIL_FAST +M01_L00: + nop + add rsp,38 + ret +; Total bytes of code 147 +``` + +## .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 +```assembly +; Sally7.Benchmarks.SerializeJobRequestHeader.WriteStruct() + sub rsp,28 + mov rcx,[rcx+8] + test rcx,rcx + je short M00_L01 + lea rax,[rcx+10] + mov ecx,[rcx+8] +M00_L00: + mov rcx,rax + mov edx,0A + mov r8d,14 + call qword ptr [7FFA08081720]; Sally7.Benchmarks.SerializeJobRequestHeader.WriteStruct(Byte ByRef, Int32, Int32) + nop + add rsp,28 + ret +M00_L01: + xor eax,eax + jmp short M00_L00 +; Total bytes of code 50 +``` +```assembly +; Sally7.Benchmarks.SerializeJobRequestHeader.WriteStruct(Byte ByRef, Int32, Int32) + sub rsp,18 + xor eax,eax + mov [rsp+8],rax + mov [rsp+0A],rax + mov byte ptr [rsp+8],32 + mov byte ptr [rsp+9],1 + mov word ptr [rsp+0A],0 + mov word ptr [rsp+0C],1 + movzx eax,dx + ror ax,8 + movzx eax,ax + mov [rsp+0E],ax + movzx eax,r8w + ror ax,8 + movzx eax,ax + mov [rsp+10],ax + mov rax,[rsp+8] + mov [rcx],rax + mov rax,[rsp+0A] + mov [rcx+2],rax + mov eax,0A + add rsp,18 + ret +; Total bytes of code 98 +``` + diff --git a/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.SerializeJobRequestHeader-report-github.md b/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.SerializeJobRequestHeader-report-github.md new file mode 100644 index 0000000..816a2b2 --- /dev/null +++ b/BenchmarkDotNet.Artifacts/results/Sally7.Benchmarks.SerializeJobRequestHeader-report-github.md @@ -0,0 +1,17 @@ +``` ini + +BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.3086/22H2/2022Update) +Intel Core i9-9900K CPU 3.60GHz (Coffee Lake), 1 CPU, 16 logical and 8 physical cores +.NET SDK=7.0.302 + [Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 + Job-XAKHXL : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 + +Runtime=.NET 7.0 Toolchain=net70 IterationTime=120.0000 ms + +``` +| Method | Mean | Error | StdDev | Ratio | RatioSD | Code Size | Allocated | Alloc Ratio | +|-------------------- |----------:|----------:|----------:|------:|--------:|----------:|----------:|------------:| +| WriteFieldsOneByOne | 0.3152 ns | 0.0245 ns | 0.0217 ns | 1.00 | 0.00 | 50 B | - | NA | +| WriteLongThenShort | 0.0194 ns | 0.0167 ns | 0.0156 ns | 0.07 | 0.05 | 48 B | - | NA | +| WriteArray | 7.9353 ns | 0.1871 ns | 0.1837 ns | 25.22 | 2.22 | 197 B | - | NA | +| WriteStruct | 6.0876 ns | 0.1112 ns | 0.1040 ns | 19.42 | 1.43 | 148 B | - | NA | diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..dd9c333 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +https://github.com/mycroes/Sally7/issues. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/Sally7.Benchmarks/BigEndianArrayToIntConversion.cs b/Sally7.Benchmarks/BigEndianArrayToIntConversion.cs index 3378148..e8f183f 100644 --- a/Sally7.Benchmarks/BigEndianArrayToIntConversion.cs +++ b/Sally7.Benchmarks/BigEndianArrayToIntConversion.cs @@ -6,24 +6,24 @@ public class BigEndianArrayToIntConversion { public BigEndianArrayToIntConversion() { - byteValue = new byte[0]; + _byteValue = new byte[0]; } [Params(1, 2, 3)] public int Value { get; set; } - private byte[] byteValue; + private byte[] _byteValue; [GlobalSetup] public void GlobalSetupData() { - byteValue = new[] {(byte) (Value >> 24), (byte) (Value >> 16), (byte) (Value >> 8), (byte) Value}; + _byteValue = new[] {(byte) (Value >> 24), (byte) (Value >> 16), (byte) (Value >> 8), (byte) Value}; } [Benchmark(Baseline = true)] public int Manual() { - return byteValue[0] << 24 | byteValue[1] << 16 | byteValue[2] << 8 | byteValue[3]; + return _byteValue[0] << 24 | _byteValue[1] << 16 | _byteValue[2] << 8 | _byteValue[3]; } } } \ No newline at end of file diff --git a/Sally7.Benchmarks/BigEndianShortConversion.cs b/Sally7.Benchmarks/BigEndianShortConversion.cs new file mode 100644 index 0000000..5ea3033 --- /dev/null +++ b/Sally7.Benchmarks/BigEndianShortConversion.cs @@ -0,0 +1,64 @@ +using System; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using BenchmarkDotNet.Attributes; + +namespace Sally7.Benchmarks; + +public class BigEndianShortConversion +{ + private readonly int _intValue = ushort.MaxValue; + + private readonly byte _byteValue = byte.MaxValue; + + private readonly ushort _ushortValue = ushort.MaxValue; + + [Benchmark] + public BigEndianShort Manual_From_Byte() + { + return new BigEndianShort { High = 0, Low = _byteValue }; + } + + [Benchmark] + public BigEndianShort Manual_From_UShort() + { + return new BigEndianShort { High = (byte)(_ushortValue >> 8), Low = (byte)_ushortValue }; + } + + [Benchmark] + public BigEndianShort Manual_From_Int() + { + return new BigEndianShort { High = (byte)(_intValue >> 8), Low = (byte)_intValue }; + } + + [Benchmark] + public BigEndianShort Unsafe_As_And_BinaryPrimitives_ReverseEndianness_From_Byte() + { + var value = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness((ushort)_byteValue) : _byteValue; + return Unsafe.As(ref value); + } + + [Benchmark] + public BigEndianShort Unsafe_As_And_BinaryPrimitives_ReverseEndianness_From_UShort() + { + var value = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(_ushortValue) : _ushortValue; + return Unsafe.As(ref value); + } + + [Benchmark] + public BigEndianShort Unsafe_As_And_BinaryPrimitives_ReverseEndianness_From_Int() + { + var value = BitConverter.IsLittleEndian + ? BinaryPrimitives.ReverseEndianness((ushort)_intValue) + : (ushort)_intValue; + return Unsafe.As(ref value); + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct BigEndianShort + { + public byte High; + public byte Low; + } +} \ No newline at end of file diff --git a/Sally7.Benchmarks/ConverterLookup.cs b/Sally7.Benchmarks/ConverterLookup.cs index 2d87987..c619b7f 100644 --- a/Sally7.Benchmarks/ConverterLookup.cs +++ b/Sally7.Benchmarks/ConverterLookup.cs @@ -8,12 +8,12 @@ namespace Sally7.Benchmarks [ShortRunJob] public class ConverterLookup { - private readonly DataItemWithConverter withConverter = + private readonly DataItemWithConverter _withConverter = new DataItemWithConverter(b => b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]); - private readonly DataItemWithValue withValue = new DataItemWithValue(); + private readonly DataItemWithValue _withValue = new DataItemWithValue(); - private readonly Dictionary typeToConverterLookup = + private readonly Dictionary _typeToConverterLookup = new Dictionary { {typeof(bool), new Int32Converter()}, @@ -23,7 +23,7 @@ public class ConverterLookup {typeof(string), new Int32Converter()} }; - private readonly Dictionary metadataTokenToConverterLookup = + private readonly Dictionary _metadataTokenToConverterLookup = new Dictionary { {typeof(bool).MetadataToken, new Int32Converter()}, @@ -34,12 +34,12 @@ public class ConverterLookup }; // Type converters - private readonly IConverter boolConverter = new Int32Converter(); - private readonly IConverter shortConverter = new Int32Converter(); - private readonly IConverter floatConverter = new Int32Converter(); - private readonly IConverter stringConverter = new Int32Converter(); + private readonly IConverter _boolConverter = new Int32Converter(); + private readonly IConverter _shortConverter = new Int32Converter(); + private readonly IConverter _floatConverter = new Int32Converter(); + private readonly IConverter _stringConverter = new Int32Converter(); - private readonly Int32Converter intConverter = new Int32Converter(); + private readonly Int32Converter _intConverter = new Int32Converter(); public ConverterLookup() { @@ -66,57 +66,57 @@ public void SetupMessage() [Benchmark] public int UsingDataItemWithConverter() { - withConverter.ApplyValue(Message); - return withConverter.Value; + _withConverter.ApplyValue(Message); + return _withConverter.Value; } [Benchmark] public int WithTypeLookup() { - typeToConverterLookup[withValue.ValueType].Apply(Message, withValue); - return withValue.Value; + _typeToConverterLookup[_withValue.ValueType].Apply(Message, _withValue); + return _withValue.Value; } [Benchmark] public int WithMetadataTokenLookup() { - metadataTokenToConverterLookup[withValue.ValueType.MetadataToken].Apply(Message, withValue); - return withValue.Value; + _metadataTokenToConverterLookup[_withValue.ValueType.MetadataToken].Apply(Message, _withValue); + return _withValue.Value; } [Benchmark] public int WithIfElseLookup() { - IfElseLookup(withValue.ValueType).Apply(Message, withValue); - return withValue.Value; + IfElseLookup(_withValue.ValueType).Apply(Message, _withValue); + return _withValue.Value; } [Benchmark] public int WithConvertUsingIfElseLookup() { - ConvertUsingIfElseLookup(Message, withValue); - return withValue.Value; + ConvertUsingIfElseLookup(Message, _withValue); + return _withValue.Value; } [Benchmark] public int WithTypeSwitchConvert() { - TypeSwitchConvert(Message, withValue); - return withValue.Value; + TypeSwitchConvert(Message, _withValue); + return _withValue.Value; } private IConverter IfElseLookup(Type type) { if (type == typeof(bool)) - return boolConverter; + return _boolConverter; if (type == typeof(short)) - return shortConverter; + return _shortConverter; if (type == typeof(int)) - return intConverter; + return _intConverter; if (type == typeof(float)) - return floatConverter; + return _floatConverter; if (type == typeof(string)) - return stringConverter; + return _stringConverter; throw new ArgumentException($"Type {type} not supported."); } @@ -126,15 +126,15 @@ private void ConvertUsingIfElseLookup(byte[] message, IDataItem dataItem) var type = dataItem.ValueType; if (type == typeof(bool)) - boolConverter.Apply(message, dataItem); + _boolConverter.Apply(message, dataItem); else if (type == typeof(short)) - shortConverter.Apply(message, dataItem); + _shortConverter.Apply(message, dataItem); else if (type == typeof(int)) - intConverter.Apply(message, dataItem); + _intConverter.Apply(message, dataItem); else if (type == typeof(float)) - floatConverter.Apply(message, dataItem); + _floatConverter.Apply(message, dataItem); else if (type == typeof(string)) - stringConverter.Apply(message, dataItem); + _stringConverter.Apply(message, dataItem); else throw new ArgumentException($"Type {type} not supported."); } @@ -195,18 +195,18 @@ private class DataItemWithValue : IDataItem private class DataItemWithConverter { - private readonly Func converter; + private readonly Func _converter; public DataItemWithConverter(Func converter) { - this.converter = converter; + this._converter = converter; } public T? Value { get; set; } public void ApplyValue(byte[] message) { - Value = converter(message); + Value = _converter(message); } } } diff --git a/Sally7.Benchmarks/IntToBigEndianArrayConversion.cs b/Sally7.Benchmarks/IntToBigEndianArrayConversion.cs index 3f6c6d5..8cff5b2 100644 --- a/Sally7.Benchmarks/IntToBigEndianArrayConversion.cs +++ b/Sally7.Benchmarks/IntToBigEndianArrayConversion.cs @@ -6,7 +6,7 @@ namespace Sally7.Benchmarks { public class IntToBigEndianArrayConversion { - private int value = 1 | 2 << 8 | 3 << 16 | 4 << 24; + private int _value = 1 | 2 << 8 | 3 << 16 | 4 << 24; [Params(true, false)] public bool IsLittleEndian { get; set; } @@ -20,7 +20,7 @@ public void SetupLocalBitConverter() [Benchmark] public byte[] BitConverterGetBytesAndArrayReverse() { - var arr = BitConverter.GetBytes(value); + var arr = BitConverter.GetBytes(_value); if (LocalBitConverter.IsLittleEndian) Array.Reverse(arr); return arr; @@ -29,7 +29,7 @@ public byte[] BitConverterGetBytesAndArrayReverse() [Benchmark] public byte[] BitConverterGetBytes() { - var arr = BitConverter.GetBytes(value); + var arr = BitConverter.GetBytes(_value); return LocalBitConverter.IsLittleEndian ? new[] {arr[3], arr[2], arr[1], arr[0]} : arr; } @@ -37,13 +37,13 @@ public byte[] BitConverterGetBytes() [Benchmark] public byte[] Manual() { - return new[] {(byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) value}; + return new[] {(byte) (_value >> 24), (byte) (_value >> 16), (byte) (_value >> 8), (byte) _value}; } [Benchmark] public byte[] FromSpan() { - var span = new Span(new [] { value }); + var span = new Span(new [] { _value }); var byteSpan = MemoryMarshal.AsBytes(span); return LocalBitConverter.IsLittleEndian diff --git a/Sally7.Benchmarks/IntToFloatConversion.cs b/Sally7.Benchmarks/IntToFloatConversion.cs index fdc452a..fd99f22 100644 --- a/Sally7.Benchmarks/IntToFloatConversion.cs +++ b/Sally7.Benchmarks/IntToFloatConversion.cs @@ -17,18 +17,18 @@ private struct IntFloatUnion public IntToFloatConversion() { - byteValue = new byte[0]; + _byteValue = new byte[0]; } [Params(1, 2, 3)] public int Value { get; set; } - private byte[] byteValue; + private byte[] _byteValue; [GlobalSetup] public void SetByteValue() { - byteValue = BitConverter.GetBytes(Value); + _byteValue = BitConverter.GetBytes(Value); } [Benchmark] @@ -51,7 +51,7 @@ public float WithBitConverter() // endianness and as such this doesn't work without additional code // to check endianness and possibly reverse the data before passing // it to BitConverter. - return BitConverter.ToSingle(byteValue, 0); + return BitConverter.ToSingle(_byteValue, 0); } [Benchmark] diff --git a/Sally7.Benchmarks/Sally7.Benchmarks.csproj b/Sally7.Benchmarks/Sally7.Benchmarks.csproj index c2fb125..0704c08 100644 --- a/Sally7.Benchmarks/Sally7.Benchmarks.csproj +++ b/Sally7.Benchmarks/Sally7.Benchmarks.csproj @@ -1,7 +1,7 @@  Exe - net462 + net462;net48;net6;net7 latest true enable @@ -10,9 +10,9 @@ - - - + + + diff --git a/Sally7.Benchmarks/Serialization/SerializeArray.cs b/Sally7.Benchmarks/Serialization/SerializeArray.cs new file mode 100644 index 0000000..effc87d --- /dev/null +++ b/Sally7.Benchmarks/Serialization/SerializeArray.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using BenchmarkDotNet.Attributes; +using Sally7.Internal; + +namespace Sally7.Benchmarks.Serialization; + +public static class SerializeArray +{ + public class SerializeByte + { + private readonly byte[] _buffer = new byte[1024]; + + [ParamsSource(nameof(ValueParameters))] + public byte[] Value { get; set; } = Array.Empty(); + + public IEnumerable ValueParameters => + "1,2,4,8,16,24,32,64".Split(',').Select(int.Parse) + .Select(len => Enumerable.Range(1, len).Select(x => (byte)x).ToArray()); + + [Benchmark(Baseline = true)] + public int SpanCopyTo() + { + Value.AsSpan().CopyTo(_buffer.AsSpan()); + + return Value.Length; + } + + [Benchmark] + public int CopyUsingLargerPrimitives() + { + return CopyBytes(Value, _buffer); + } + + private static int CopyBytes(ReadOnlySpan input, Span output) + { + ref var destination = ref MemoryMarshal.GetReference(output); + ref var source = ref MemoryMarshal.GetReference(input); + + var offset = 0u; + while (offset <= input.Length - sizeof(ulong)) + { + var value = Unsafe.ReadUnaligned(ref source.GetOffset(offset)); + Unsafe.WriteUnaligned(ref destination.GetOffset(offset), value); + + offset += sizeof(ulong); + } + + if (offset <= input.Length - sizeof(uint)) + { + var value = Unsafe.ReadUnaligned(ref source.GetOffset(offset)); + Unsafe.WriteUnaligned(ref destination.GetOffset(offset), value); + + offset += sizeof(uint); + } + + if (offset <= input.Length - sizeof(ushort)) + { + var value = Unsafe.ReadUnaligned(ref source.GetOffset(offset)); + Unsafe.WriteUnaligned(ref destination.GetOffset(offset), value); + + offset += sizeof(ushort); + } + + if (offset < input.Length) + { + var value = Unsafe.ReadUnaligned(ref source.GetOffset(offset)); + Unsafe.WriteUnaligned(ref destination.GetOffset(offset), value); + + offset += sizeof(byte); + } + + return (int)offset; + } + } + + public class SerializeUInt16 + { + private readonly byte[] _buffer = new byte[1024]; + + [ParamsSource(nameof(ValueParameters))] + public ushort[] Value { get; set; } = Array.Empty(); + + public IEnumerable ValueParameters => + Enumerable.Range(1, 12).Select(l => Enumerable.Range(1, l).Select(x => (ushort)x).ToArray()); + + [Benchmark(Baseline = true)] + public int SerializeOneByOne() + { + ref var destination = ref MemoryMarshal.GetReference(_buffer.AsSpan()); + + WriteUInt16(ref destination, Value); + + return Value.Length * sizeof(ushort); + } + + [Benchmark] + public int UsingUnsafeReadUnaligned() + { + ref var destination = ref MemoryMarshal.GetReference(_buffer.AsSpan()); + ref var source = ref MemoryMarshal.GetReference(MemoryMarshal.Cast(Value.AsSpan())); + + var offset = 0u; + for (var i = 0; i < Value.Length; i++) + { + var value = Unsafe.ReadUnaligned(ref source.GetOffset(offset)); + NetworkOrderSerializer.WriteUInt16(ref destination.GetOffset(offset), value); + + offset += sizeof(ushort); + } + + return (int) offset; + } + + [Benchmark] + public int UsingUnsafeIsAddressLessThan() + { + ref var destination = ref MemoryMarshal.GetReference(_buffer.AsSpan()); + ref var source = ref MemoryMarshal.GetReference(MemoryMarshal.Cast(Value.AsSpan())); + + var size = Value.Length * sizeof(ushort); + ref var limit = ref source.GetOffset((uint)size); + + while (Unsafe.IsAddressLessThan(ref source, ref limit)) + { + var value = Unsafe.ReadUnaligned(ref source); + NetworkOrderSerializer.WriteUInt16(ref destination, value); + + source = ref source.GetOffset(sizeof(ushort)); + destination = ref destination.GetOffset(sizeof(ushort)); + } + + return size; + } + + [Benchmark] + public int UsingCopyAndAlign() + { + return CopyAndAlign16Bit(MemoryMarshal.Cast(Value.AsSpan()), _buffer.AsSpan(), Value.Length); + } + } + + private static int WriteUInt16(ref byte destination, ReadOnlySpan values) + { + var offset = 0; + foreach (var value in values) + { + NetworkOrderSerializer.WriteUInt16(ref destination.GetOffset((uint)offset), value); + offset += sizeof(ushort); + } + + return offset; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int CopyAndAlign16Bit(ReadOnlySpan input, Span output, int numberOfItems) + { + ref var destination = ref MemoryMarshal.GetReference(output); + ref var source = ref MemoryMarshal.GetReference(input); + + var limit = (uint) numberOfItems * sizeof(ushort); + + var offset = 0u; + while (offset < limit) + { + var value = Unsafe.ReadUnaligned(ref source.GetOffset(offset)); + NetworkOrderSerializer.WriteUInt16(ref destination.GetOffset(offset), value); + + offset += sizeof(ushort); + } + + return (int)offset; + } +} \ No newline at end of file diff --git a/Sally7.Benchmarks/Serialization/SerializeJobRequestHeader.cs b/Sally7.Benchmarks/Serialization/SerializeJobRequestHeader.cs new file mode 100644 index 0000000..fd1c0b5 --- /dev/null +++ b/Sally7.Benchmarks/Serialization/SerializeJobRequestHeader.cs @@ -0,0 +1,196 @@ +using System; +using System.Buffers.Binary; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.Intrinsics; +#endif +using BenchmarkDotNet.Attributes; +using Sally7.Internal; +using Sally7.Protocol.S7.Messages; + +namespace Sally7.Benchmarks.Serialization +{ + public class SerializeJobRequestHeader + { + private const int JobRequestHeader1 = 0x32 << 24 // Protocol ID + | (byte)MessageType.JobRequest << 16; // Message type + + private const ushort PduRef = 1 << 8; + + private static ulong JobRequestHeaderLong = 0x32L << 56 | (long)MessageType.JobRequest << 48 | PduRef << 16; + + private readonly byte[] _buffer = new byte[16]; + + [Benchmark(Baseline = true)] + public uint WriteFieldsOneByOne() + { + ref var start = ref MemoryMarshal.GetReference(_buffer.AsSpan()); + return WriteFieldsOneByOne(ref start, 10, 20); + } + + [Benchmark] + public uint WriteLongThenShort() + { + ref var start = ref MemoryMarshal.GetReference(_buffer.AsSpan()); + return WriteLongThenShort(ref start, 10, 20); + } + + #if NET7_0_OR_GREATER + [Benchmark] + public uint WriteVector128() + { + ref var start = ref MemoryMarshal.GetReference(_buffer.AsSpan()); + return WriteVector128(ref start, 10, 20); + } + #endif + + [Benchmark] + public uint WriteArray() + { + ref var start = ref MemoryMarshal.GetReference(_buffer.AsSpan()); + return WriteArray(ref start, 10, 20); + } + + [Benchmark] + public uint WriteStruct() + { + ref var start = ref MemoryMarshal.GetReference(_buffer.AsSpan()); + return WriteStruct(ref start, 10, 20); + } + + [GlobalCleanup] + public void VerifyBuffer() + { + byte[] expected = { 0x32, 1, 0, 0, 1, 0, 0, 10, 0, 20 }; + if (!_buffer.Take(10).SequenceEqual(expected)) + { + throw new Exception($""" + Buffer contents are invalid. + Expected: {BitConverter.ToString(expected)} + Received: {BitConverter.ToString(_buffer)} + """); + } + } + + public static uint WriteFieldsOneByOne(ref byte destination, int paramLength, int dataLength) + { + WriteUInt32(ref destination, JobRequestHeader1); + WriteUInt16(ref destination.GetOffset(4), PduRef); // Ignore PDU ref, leave to request executor. + WriteUInt16(ref destination.GetOffset(6), (ushort)paramLength); + WriteUInt16(ref destination.GetOffset(8), (ushort)dataLength); + + return 10; + } + + public static uint WriteLongThenShort(ref byte destination, int paramLength, int dataLength) + { + var header = JobRequestHeaderLong | (ushort)paramLength; + + Unsafe.WriteUnaligned(ref destination, + BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(header) : header); + WriteUInt16(ref destination.GetOffset(8), (ushort)dataLength); + + return 10; + } + + #if NET7_0_OR_GREATER + public static uint WriteVector128(ref byte destination, int paramLength, int dataLength) + { + var vec = Vector128.Create((byte)0x32, (byte)MessageType.JobRequest, 0, 0, 1, 0, (byte)(paramLength >> 8), + (byte)(paramLength), (byte)(dataLength >> 8), (byte)(dataLength), 0, 0, 0, 0, 0, 0); + vec.StoreUnsafe(ref destination); + + return 10; + } + #endif + + public static unsafe uint WriteArray(ref byte destination, int paramLength, int dataLength) + { + var request = stackalloc ushort[5]; + request[0] = 0x32_01; + request[1] = 0; + request[2] = PduRef; + request[3] = (ushort)paramLength; + request[4] = (ushort)dataLength; + + if (BitConverter.IsLittleEndian) + { + request[0] = BinaryPrimitives.ReverseEndianness(request[0]); + request[2] = BinaryPrimitives.ReverseEndianness(request[2]); + request[3] = BinaryPrimitives.ReverseEndianness(request[3]); + request[4] = BinaryPrimitives.ReverseEndianness(request[4]); + } + + Unsafe.CopyBlockUnaligned(ref destination, ref Unsafe.AsRef(request), 10); + + return 10; + } + + public static uint WriteStruct(ref byte destination, int paramLength, int dataLength) + { + var header = BitConverter.IsLittleEndian + ? new Header + { + ProtocolId = 0x32, + MessageType = (byte)MessageType.JobRequest, + Reserved = 0, + PduRef = BinaryPrimitives.ReverseEndianness(PduRef), + ParamLength = BinaryPrimitives.ReverseEndianness((ushort)paramLength), + DataLength = BinaryPrimitives.ReverseEndianness((ushort)dataLength) + } + : new Header + { + ProtocolId = 0x32, + MessageType = (byte)MessageType.JobRequest, + Reserved = 0, + PduRef = PduRef, + ParamLength = (ushort)paramLength, + DataLength = (ushort)dataLength + }; + + Unsafe.WriteUnaligned(ref destination, header); + + return 10; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct Header + { + public byte ProtocolId; + public byte MessageType; + public ushort Reserved; + public ushort PduRef; + public ushort ParamLength; + public ushort DataLength; + } + + public static uint WriteUInt16(ref byte destination, ushort value) + { + NetworkOrderSerializer.WriteUInt16(ref destination, value); + return sizeof(short); + } + + public static uint WriteUInt32(ref byte destination, uint value) + { + NetworkOrderSerializer.WriteUInt32(ref destination, value); + return sizeof(int); + } + + private static class NetworkOrderSerializer + { + public static void WriteUInt16(ref byte destination, ushort value) + { + Unsafe.WriteUnaligned(ref destination, + BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(value) : value); + } + + public static void WriteUInt32(ref byte destination, uint value) + { + Unsafe.WriteUnaligned(ref destination, + BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(value) : value); + } + } + } +} diff --git a/Sally7.Benchmarks/Serialization/SerializePrimitives.cs b/Sally7.Benchmarks/Serialization/SerializePrimitives.cs new file mode 100644 index 0000000..b54bde5 --- /dev/null +++ b/Sally7.Benchmarks/Serialization/SerializePrimitives.cs @@ -0,0 +1,79 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using BenchmarkDotNet.Attributes; +using Sally7.Internal; + +namespace Sally7.Benchmarks.Serialization +{ + public class SerializePrimitives + { + private readonly ulong _value = 0x0102030405060708; + private readonly ulong[] _arrayValue = { 0x0102030405060708, 0x0807060504030201 }; + private readonly byte[] _buffer = new byte[1024]; + + [Benchmark] + public void WriteUInt64() + { + Converters.AppendUInt64(_buffer, _value); + } + + [Benchmark] + public void WriteUInt64ArrayIncrementOffset() + { + Converters.AppendUInt64ArrayIncrementDestination(_buffer, _arrayValue); + } + + [Benchmark] + public void WriteUInt64ArrayCalculateOffset() + { + Converters.AppendUInt64ArrayCalculateOffset(_buffer, _arrayValue); + } + + [Benchmark] + public void WriteUInt64ArrayStoreOffset() + { + Converters.AppendUInt64ArrayStoreOffset(_buffer, _arrayValue); + } + + public static class Converters + { + public static void AppendUInt64(Span buffer, ulong value) + { + ref var destination = ref MemoryMarshal.GetReference(buffer); + NetworkOrderSerializer.WriteUInt64(ref destination, value); + } + + public static void AppendUInt64ArrayIncrementDestination(Span buffer, ulong[] value) + { + ref var destination = ref MemoryMarshal.GetReference(buffer); + for (var i = 0; i < value.Length; i++) + { + NetworkOrderSerializer.WriteUInt64(ref destination, value[i]); + destination = ref destination.GetOffset(sizeof(ulong)); + } + } + + public static void AppendUInt64ArrayCalculateOffset(Span buffer, ulong[] value) + { + ref var destination = ref MemoryMarshal.GetReference(buffer); + for (uint i = 0; i < value.Length; i++) + { + NetworkOrderSerializer.WriteUInt64(ref destination.GetOffset(sizeof(ulong) * i), value[i]); + } + } + + public static void AppendUInt64ArrayStoreOffset(Span buffer, ulong[] value) + { + ref var destination = ref MemoryMarshal.GetReference(buffer); + uint offset = 0; + for (var i = 0; i < value.Length; i++) + { + NetworkOrderSerializer.WriteUInt64(ref destination.GetOffset(offset), value[i]); + offset += sizeof(ulong); + } + } + } + } +} \ No newline at end of file diff --git a/Sally7.Benchmarks/Serialization/SerializeReadRequest.cs b/Sally7.Benchmarks/Serialization/SerializeReadRequest.cs new file mode 100644 index 0000000..08688d4 --- /dev/null +++ b/Sally7.Benchmarks/Serialization/SerializeReadRequest.cs @@ -0,0 +1,252 @@ +using System; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using BenchmarkDotNet.Attributes; +using Sally7.Protocol; +using Sally7.Protocol.S7; + +namespace Sally7.Benchmarks.Serialization; + +public class SerializeReadRequest +{ + private readonly byte[] _buffer = new byte[31]; + + [Benchmark] + public void MemoryMarshal_Cast() + { + var span = _buffer.AsSpan(); + + ref var tpkt = ref MemoryMarshal.Cast(span)[0]; + tpkt.Version = 3; + tpkt.Reserved = 0; + tpkt.Length = 31; + + ref var data = ref MemoryMarshal.Cast(span.Slice(4))[0]; + data.Length = 2; + data.DataIdentifier = 0b1111_0000; + data.PduNumberAndEot = 0b1_000_0000; + + ref var s7Header = ref MemoryMarshal.Cast(span.Slice(7))[0]; + s7Header.ProtocolId = 0x32; + s7Header.MessageType = MessageType.JobRequest; + s7Header.Reserved = 0; + s7Header.PduRef = 0x0101; + s7Header.ParamLength = 14; + s7Header.DataLength = 0; + + ref var rr = ref MemoryMarshal.Cast(span.Slice(17))[0]; + rr.FunctionCode = FunctionCode.Read; + rr.ItemCount = 1; + + ref var ri = ref MemoryMarshal.Cast(span.Slice(19))[0]; + ri.Spec = 0x12; + ri.Length = 10; + ri.SyntaxId = AddressingMode.S7Any; + ri.VariableType = VariableType.Byte; + ri.Count = 2; + ri.DbNumber = 2000; + ri.Area = Area.DataBlock; + ri.Address = new Address { High = 0, Mid = 0, Low = 20 }; + } + + [Benchmark] + public void Unsafe_WriteUnaligned() + { + var span = _buffer.AsSpan(); + ref var start = ref MemoryMarshal.GetReference(span); + // TPKT + Unsafe.WriteUnaligned(ref start, (byte)3); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 1), 0); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 2), + BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness((ushort)31) : (ushort)31); + + // Data + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 4), (byte)2); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 5), (byte)0b1111_0000); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 6), (byte)0b1_000_0000); + + // S7 header + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 7), (byte)0x32); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 8), (byte)MessageType.JobRequest); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 9), (ushort)0); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 11), (ushort)0x0101); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 13), + BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness((ushort)14) : (ushort)14); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 15), (ushort)0); + + // Read request + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 17), (byte)FunctionCode.Read); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 18), (byte)1); + + // Request item + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 19), (byte)0x12); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 20), (byte)10); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 21), (byte)AddressingMode.S7Any); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 22), (byte)VariableType.Byte); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 23), + BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness((ushort)2) : (ushort)2); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 25), + BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness((ushort)2000) : (ushort)2000); + // Hack: Big endian int uses last 3 bytes for lower range, so we can just put the address in as int + // and overwrite the first byte afterwards. + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 27), + BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(20u) : 20u); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 27), (byte)Area.DataBlock); + } + + [Benchmark] + public void Unsafe_WriteUnaligned_With_Optimizations() + { + const uint tpkt = 0x03_00_00_00; + const uint data = 2 << 24 | 0b1111_0000 << 16 | 0b1_000_0000; + const ulong jobRequestHeader = 0x32L << 56 | (ulong)MessageType.JobRequest << 48 | 0x0101 << 16; + + var span = _buffer.AsSpan(); + ref var start = ref MemoryMarshal.GetReference(span); + + // TPKT + var tpktWithLen = tpkt | 31; + Unsafe.WriteUnaligned(ref start, + BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(tpktWithLen) : tpktWithLen); + + // Data + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 4), + BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(data) : data); + + // S7 header + var jobRequestHeaderWithParamLength = jobRequestHeader | 14; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 7), + BitConverter.IsLittleEndian + ? BinaryPrimitives.ReverseEndianness(jobRequestHeaderWithParamLength) + : jobRequestHeaderWithParamLength); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 15), (ushort)0); + + // Read request + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 17), (byte)FunctionCode.Read); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 18), (byte)1); + + // Request item + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 19), (byte)0x12); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 20), (byte)10); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 21), (byte)AddressingMode.S7Any); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 22), (byte)VariableType.Byte); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 23), + BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness((ushort)2) : (ushort)2); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 25), + BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness((ushort)2000) : (ushort)2000); + // Hack: Big endian int uses last 3 bytes for lower range, so we can just put the address in as int + // and overwrite the first byte afterwards. + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 27), + BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(20u) : 20u); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref start, 27), (byte)Area.DataBlock); + } + + [Benchmark] + public void Unsafe_As_Struct() + { + var span = _buffer.AsSpan(); + ref var start = ref MemoryMarshal.GetReference(span); + ref var tpkt = ref Unsafe.As(ref start); + tpkt.Version = 3; + tpkt.Reserved = 0; + tpkt.Length = 31; + + ref var data = ref Unsafe.As(ref Unsafe.Add(ref start, 4)); + data.Length = 2; + data.DataIdentifier = 0b1111_0000; + data.PduNumberAndEot = 0b1_000_0000; + + ref var s7Header = ref Unsafe.As(ref Unsafe.Add(ref start, 7)); + s7Header.ProtocolId = 0x32; + s7Header.MessageType = MessageType.JobRequest; + s7Header.Reserved = 0; + s7Header.PduRef = 0x0101; + s7Header.ParamLength = 14; + s7Header.DataLength = 0; + + ref var rr = ref Unsafe.As(ref Unsafe.Add(ref start, 17)); + rr.FunctionCode = FunctionCode.Read; + rr.ItemCount = 1; + + ref var ri = ref Unsafe.As(ref Unsafe.Add(ref start, 19)); + ri.Spec = 0x12; + ri.Length = 10; + ri.SyntaxId = AddressingMode.S7Any; + ri.VariableType = VariableType.Byte; + ri.Count = 2; + ri.DbNumber = 2000; + ri.Area = Area.DataBlock; + ri.Address = new Address { High = 0, Mid = 0, Low = 20 }; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct Tpkt + { + public byte Version; + public byte Reserved; + public BigEndianShort Length; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct Data + { + public byte Length; + public byte DataIdentifier; + public byte PduNumberAndEot; + } + + private enum MessageType : byte + { + JobRequest = 0x01, + Ack = 0x02, + AckData = 0x03, + UserData = 0x07 + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct S7Header + { + public byte ProtocolId; + public MessageType MessageType; + public BigEndianShort Reserved; + public short PduRef; + public BigEndianShort ParamLength; + public BigEndianShort DataLength; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct ReadRequest + { + public FunctionCode FunctionCode; + public byte ItemCount; + } + + private enum FunctionCode : byte + { + Read = 0x04, + Write = 0x05, + CommunicationSetup = 0xf0 + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private struct RequestItem + { + public byte Spec; + public byte Length; + public AddressingMode SyntaxId; + public VariableType VariableType; + public BigEndianShort Count; + public BigEndianShort DbNumber; + public Area Area; + public Address Address; + } + + private enum AddressingMode : byte + { + S7Any = 0x10, // S7-Any pointer (regular addressing) memory+variable length+offset + DriveEs = 0xa2, // Drive-ES-Any seen on Drive ES Starter with routing over S7 + SubItem = 0xb0, // Special DB addressing for S400 (subitem read/write) + Symbolic = 0xb2 // S1200/S1500? Symbolic addressing mode + } +} \ No newline at end of file diff --git a/Sally7.Benchmarks/StructFromSpan.cs b/Sally7.Benchmarks/StructFromSpan.cs index 50e6509..dfabfb2 100644 --- a/Sally7.Benchmarks/StructFromSpan.cs +++ b/Sally7.Benchmarks/StructFromSpan.cs @@ -6,18 +6,18 @@ namespace Sally7.Benchmarks { public class StructFromSpan { - private byte[] buffer = new byte[5]; + private byte[] _buffer = new byte[5]; [Benchmark(Baseline = true)] public ref SomeStruct ByIndexReference() { - return ref MemoryMarshal.Cast(buffer.AsSpan())[0]; + return ref MemoryMarshal.Cast(_buffer.AsSpan())[0]; } [Benchmark] public ref SomeStruct ByGetReference() { - return ref MemoryMarshal.GetReference(MemoryMarshal.Cast(buffer.AsSpan())); + return ref MemoryMarshal.GetReference(MemoryMarshal.Cast(_buffer.AsSpan())); } } } \ No newline at end of file diff --git a/Sally7.Tests/ArrayConversionTests.cs b/Sally7.Tests/ArrayConversionTests.cs index 4911860..027c7b5 100644 --- a/Sally7.Tests/ArrayConversionTests.cs +++ b/Sally7.Tests/ArrayConversionTests.cs @@ -1,15 +1,39 @@ -using System; -using System.Linq; -using Sally7.ValueConversion; -using Xunit; +using Sally7.ValueConversion; namespace Sally7.Tests { public class ArrayConversionTests { + [Theory] + [InlineData(new byte[] { 1 })] + [InlineData(new byte[] { 1, byte.MaxValue })] + [InlineData(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 })] + public void ConvertToByteArray(byte[] bytes) + { + var converter = ConverterFactory.GetFromPlcConverter(bytes.Length); + var result = new byte[bytes.Length]; + converter(ref result, bytes, bytes.Length); + + Assert.Equal(bytes, result); + } + + [Theory] + [InlineData(new byte[] { 1 })] + [InlineData(new byte[] { 1, byte.MaxValue })] + [InlineData(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 })] + public void ConvertFromByteArray(byte[] bytes) + { + var converter = ConverterFactory.GetToPlcConverter(bytes.Length); + var output = new byte[bytes.Length]; + converter(bytes, bytes.Length, output); + + Assert.Equal(bytes, output); + } + [Theory] [InlineData(new short[] { 1 })] [InlineData(new short[] { 1, 1 << 8 })] + [InlineData(new short[] { 1, 2, 3, 4, 5, 6, 7, 8 })] public void ConvertToShortArray(short[] value) { var bytes = value.SelectMany(v => @@ -19,16 +43,37 @@ public void ConvertToShortArray(short[] value) return b; }).ToArray(); - var converter = ConverterFactory.GetFromPlcConverter(); + var converter = ConverterFactory.GetFromPlcConverter(value.Length); var result = new short[value.Length]; converter(ref result, bytes, value.Length); Assert.Equal(value, result); } + [Theory] + [InlineData(new short[] { 1 })] + [InlineData(new short[] { 1, 1 << 8 })] + [InlineData(new short[] { 1, 2, 3, 4, 5, 6, 7, 8 })] + public void ConvertFromShortArray(short[] value) + { + var bytes = value.SelectMany(v => + { + var b = BitConverter.GetBytes(v); + if (BitConverter.IsLittleEndian) Array.Reverse(b); + return b; + }).ToArray(); + + var converter = ConverterFactory.GetToPlcConverter(value.Length); + var output = new byte[bytes.Length]; + converter(value, value.Length, output); + + Assert.Equal(bytes, output); + } + [Theory] [InlineData(new int[] { 1 })] [InlineData(new int[] { 1, 1 << 8, 1 << 16, 1 << 24 })] + [InlineData(new int[] { 1, 2, 3, 4, 5, 6, 7, 8 })] public void ConvertToIntArray(int[] value) { var bytes = value.SelectMany(v => @@ -38,16 +83,37 @@ public void ConvertToIntArray(int[] value) return b; }).ToArray(); - var converter = ConverterFactory.GetFromPlcConverter(); + var converter = ConverterFactory.GetFromPlcConverter(value.Length); var result = new int[value.Length]; converter(ref result, bytes, value.Length); Assert.Equal(value, result); } + [Theory] + [InlineData(new int[] { 1 })] + [InlineData(new int[] { 1, 1 << 8 })] + [InlineData(new int[] { 1, 2, 3, 4, 5, 6, 7, 8 })] + public void ConvertFromIntArray(int[] value) + { + var bytes = value.SelectMany(v => + { + var b = BitConverter.GetBytes(v); + if (BitConverter.IsLittleEndian) Array.Reverse(b); + return b; + }).ToArray(); + + var converter = ConverterFactory.GetToPlcConverter(value.Length); + var output = new byte[bytes.Length]; + converter(value, value.Length, output); + + Assert.Equal(bytes, output); + } + [Theory] [InlineData(new long[] { 1 })] [InlineData(new long[] { 1, 1 << 8, 1 << 16, 1 << 24, 1 << 32, 1 << 40, 1 << 48, 1 << 56 })] + [InlineData(new long[] { 1, 2, 3, 4, 5, 6, 7, 8 })] public void ConvertToLongArray(long[] value) { var bytes = value.SelectMany(v => @@ -57,16 +123,37 @@ public void ConvertToLongArray(long[] value) return b; }).ToArray(); - var converter = ConverterFactory.GetFromPlcConverter(); + var converter = ConverterFactory.GetFromPlcConverter(value.Length); var result = new long[value.Length]; converter(ref result, bytes, value.Length); Assert.Equal(value, result); } + [Theory] + [InlineData(new long[] { 1 })] + [InlineData(new long[] { 1, 1 << 8 })] + [InlineData(new long[] { 1, 2, 3, 4, 5, 6, 7, 8 })] + public void ConvertFromLongArray(long[] value) + { + var bytes = value.SelectMany(v => + { + var b = BitConverter.GetBytes(v); + if (BitConverter.IsLittleEndian) Array.Reverse(b); + return b; + }).ToArray(); + + var converter = ConverterFactory.GetToPlcConverter(value.Length); + var output = new byte[bytes.Length]; + converter(value, value.Length, output); + + Assert.Equal(bytes, output); + } + [Theory] [InlineData(new float[] { 3.14f })] [InlineData(new float[] { 2.81f, 3.14f })] + [InlineData(new float[] { 1, 2, 3, 4, 5, 6, 7, 8 })] public void ConvertToFloatArray(float[] value) { var bytes = value.SelectMany(v => @@ -76,7 +163,7 @@ public void ConvertToFloatArray(float[] value) return b; }).ToArray(); - var converter = ConverterFactory.GetFromPlcConverter(); + var converter = ConverterFactory.GetFromPlcConverter(value.Length); var result = new float[value.Length]; converter(ref result, bytes, value.Length); @@ -86,6 +173,7 @@ public void ConvertToFloatArray(float[] value) [Theory] [InlineData(new double[] { 3.14 })] [InlineData(new double[] { 2.81, 3.14 })] + [InlineData(new double[] { 1, 2, 3, 4, 5, 6, 7, 8 })] public void ConvertToDoubleArray(double[] value) { var bytes = value.SelectMany(v => @@ -95,7 +183,7 @@ public void ConvertToDoubleArray(double[] value) return b; }).ToArray(); - var converter = ConverterFactory.GetFromPlcConverter(); + var converter = ConverterFactory.GetFromPlcConverter(value.Length); var result = new double[value.Length]; converter(ref result, bytes, value.Length); diff --git a/Sally7.Tests/FromPlcConverterTests.cs b/Sally7.Tests/FromPlcConverterTests.cs index 8db19b5..2c556e1 100644 --- a/Sally7.Tests/FromPlcConverterTests.cs +++ b/Sally7.Tests/FromPlcConverterTests.cs @@ -1,6 +1,4 @@ -using System; -using Sally7.ValueConversion; -using Xunit; +using Sally7.ValueConversion; namespace Sally7.Tests { @@ -21,7 +19,7 @@ public void ConvertToLong(long value) var bytes = BitConverter.GetBytes(value); if (BitConverter.IsLittleEndian) Array.Reverse(bytes); - var converter = ConverterFactory.GetFromPlcConverter(); + var converter = ConverterFactory.GetFromPlcConverter(1); long result = default; converter(ref result, bytes, sizeof(long)); @@ -41,7 +39,7 @@ public void ConvertToInt(int value) var bytes = BitConverter.GetBytes(value); if (BitConverter.IsLittleEndian) Array.Reverse(bytes); - var converter = ConverterFactory.GetFromPlcConverter(); + var converter = ConverterFactory.GetFromPlcConverter(1); int result = default; converter(ref result, bytes, sizeof(int)); @@ -59,7 +57,7 @@ public void ConvertToShort(short value) var bytes = BitConverter.GetBytes(value); if (BitConverter.IsLittleEndian) Array.Reverse(bytes); - var converter = ConverterFactory.GetFromPlcConverter(); + var converter = ConverterFactory.GetFromPlcConverter(1); short result = default; converter(ref result, bytes, sizeof(short)); @@ -77,7 +75,7 @@ public void ConvertToFloat(float value) var bytes = BitConverter.GetBytes(value); if (BitConverter.IsLittleEndian) Array.Reverse(bytes); - var converter = ConverterFactory.GetFromPlcConverter(); + var converter = ConverterFactory.GetFromPlcConverter(1); float result = default; converter(ref result, bytes, sizeof(float)); @@ -101,7 +99,7 @@ public void ConvertToEnumOfInt(EnumOfInt value) var bytes = BitConverter.GetBytes((int) value); if (BitConverter.IsLittleEndian) Array.Reverse(bytes); - var converter = ConverterFactory.GetFromPlcConverter(); + var converter = ConverterFactory.GetFromPlcConverter(1); EnumOfInt result = default; converter(ref result, bytes, sizeof(int)); @@ -144,8 +142,8 @@ public void ConvertToBoolArray_Roundtrips(int boolArraySize) bools[i] = random.Next() % 2 == 0; } - var toS7Converter = ConverterFactory.GetToPlcConverter(); - var fromS7Converter = ConverterFactory.GetFromPlcConverter(); + var toS7Converter = ConverterFactory.GetToPlcConverter(boolArraySize); + var fromS7Converter = ConverterFactory.GetFromPlcConverter(boolArraySize); int length = toS7Converter(bools, bools.Length, bytes); diff --git a/Sally7.Tests/Protocol/CommunicationSequence.cs b/Sally7.Tests/Protocol/CommunicationSequence.cs new file mode 100644 index 0000000..e5cc15a --- /dev/null +++ b/Sally7.Tests/Protocol/CommunicationSequence.cs @@ -0,0 +1,337 @@ +using System.Buffers; +using System.Net; +using System.Net.Sockets; +using Sally7.Protocol.Cotp; +using Sally7.Protocol.S7; +using Xunit.Abstractions; + +namespace Sally7.Tests.Protocol; + +internal class CommunicationSequence +{ + private readonly List<(byte[], byte[])> _sequence = new(); + + private readonly ITestOutputHelper _output; + + public CommunicationSequence(ITestOutputHelper output) + { + this._output = output; + } + + public CommunicationSequence AddCall(byte[] request, byte[] response) + { + _sequence.Add((request, response)); + + return this; + } + + public CommunicationSequence AddConnectRequest(PduSizeParameter.PduSize pduSize, Tsap sourceTsap, Tsap destinationTsap) + { + return AddCall(new byte[] + { + // TPKT + 3, // Version + 0, // Reserved + 0, 22, // Length + + // CR + 17, // Number of bytes following + 0b1110_0000, // CR / Credit + 0, 0, // Destination reference, unused + 0, 0, // Source reference, unused + 0, // Class / Option + + // PDU Size parameter + 0b1100_0000, // Parameter code + 1, // Parameter length + (byte) pduSize, // 1024 byte PDU (2 ^ 10) + + // Source TSAP + 0b1100_0001, // Parameter code + 2, // Parameter length + sourceTsap.Channel, // Channel + sourceTsap.Position, // Position + + // Destination TSAP + 0b1100_0010, // Parameter code + 2, // Parameter length + destinationTsap.Channel, // Channel + destinationTsap.Position, // Position + }, new byte[] + { + // TPKT + 3, // Version + 0, // Reserved + 0, 11, // Length + + // CC + 6, // Length + 0b1101_0000, // CC / Credit + 0, 0, // Destination reference + 0, 0, // Source reference + 0, // Class / Option + }); + } + + public CommunicationSequence AddCommunicationSetup() + { + return AddCall(new byte[] + { + // TPKT + 3, // Version + 0, // Reserved + 0, 25, // Length + + // Data header + 2, // Length + 0b1111_0000, // Data identifier + 0b1_000_0000, // PDU number and end of transmission + + // S7 header + 0x32, // Protocol ID + 0x01, // Message type job request + 0, 0, // Reserved + 1, 0, // PDU reference + 0, 8, // Parameter length (Communication Setup) + 0, 0, // Data length + + // Communication Setup + 0xf0, // Function code + 0, // Reserved + 0, 10, // Max AMQ caller + 0, 10, // Max AMQ callee + 3, 192, // PDU size (960) + }, new byte[] + { + // TPKT + 3, // Version + 0, // Reserved + 0, 27, // Length + + // Data header + 2, // Length + 0b1111_0000, // Data identifier + 0b1_000_0000, // PDU number and end of transmission + + // S7 header + 0x32, // Protocol ID + 0x03, // Message type ack data + 0, 0, // Reserved + 1, 0, // PDU reference + 0, 8, // Parameter length (Communication Setup) + 0, 0, // Data length + 0, // Error class + 0, // Error code + + // Communication Setup + 0xf0, // Function code + 0, // Reserved + 0, 3, // Max AMQ caller + 0, 3, // Max AMQ callee + 3, 192, // PDU size (960) + }); + } + + public CommunicationSequence AddRead(Area area, int dbNumber, int address, int length, TransportSize transportSize, + VariableType variableType, byte[] data) + { + var dataLength = 4 + data.Length; + + return AddCall(new byte[] + { + // TPKT + 3, // Version + 0, // Reserved + 0, 31, // Length + + // Data header + 2, // Length + 0b1111_0000, // Data identifier + 0b1_000_0000, // PDU number and end of transmission + + // S7 header + 0x32, // Protocol ID + 0x01, // Message type job request + 0, 0, // Reserved + 1, 1, // PDU reference + 0, 14, // Parameter length (Read request) + 0, 0, // Data length + + // Read request + 0x04, // Function code + 1, // Number of items + + // Request item + 0x12, // Spec + 10, // Length of request item + 0x10, // AddressingMode S7 any + (byte) variableType, // Variable type + (byte) (length >> 8 & 0xff), // Length, upper byte + (byte) (length & 0xff), // Length, lower byte + (byte) (dbNumber >> 8 & 0xff), // DB number, upper byte + (byte) (dbNumber & 0xff), // DB number, lower byte + (byte) area, + (byte) (address >> 16 & 0xff), // Address, upper byte + (byte) (address >> 8 & 0xff), // Address, middle byte + (byte) (address & 0xff), // Address, lower byte + }, new byte[] + { + // TPKT + 3, // Version + 0, // Reserved + 0, 27, // Length + + // Data header + 2, // Length + 0b1111_0000, // Data identifier + 0b1_000_0000, // PDU number and end of transmission + + // S7 header + 0x32, // Protocol ID + 0x03, // Message type ack data + 0, 0, // Reserved + 1, 1, // PDU reference + 0, 2, // Parameter length (Read request) + (byte) (dataLength >> 8 & 0xff), (byte) (dataLength & 0xff), // Data length + 0, // Error class + 0, // Error code + + // Read response + 0x04, // Function code + 1, // Number of items + + // DataItem + 0xff, // ErrorCode + (byte) transportSize, // Transport size + (byte) (data.Length >> 5 & 0xff), // Data length, upper byte, in bits + (byte) (data.Length << 3 & 0xff), // Data length, lower byte, in bits + }.Concat(data).ToArray()); + } + + public CommunicationSequence AddWrite(Area area, int dbNumber, int address, int length, TransportSize transportSize, + VariableType variableType, byte[] data) + { + var dataLength = 4 + data.Length; + + return AddCall(new byte[] + { + // TPKT + 3, // Version + 0, // Reserved + 0, 37, // Length + + // Data header + 2, // Length + 0b1111_0000, // Data identifier + 0b1_000_0000, // PDU number and end of transmission + + // S7 header + 0x32, // Protocol ID + 0x01, // Message type job request + 0, 0, // Reserved + 1, 1, // PDU reference + 0, 14, // Parameter length (Write request) + (byte)(dataLength >> 8 & 0xff), (byte)(dataLength & 0xff), // Data length + + // Write request + 0x05, // Function code + 1, // Number of items + + // Request item + 0x12, // Spec + 10, // Length of request item + 0x10, // AddressingMode S7 any + (byte) variableType, // Variable type + (byte) (length >> 8 & 0xff), // Length, upper byte + (byte) (length & 0xff), // Length, lower byte + (byte) (dbNumber >> 8 & 0xff), // DB number, upper byte + (byte) (dbNumber & 0xff), // DB number, lower byte + (byte) area, + (byte) (address >> 16 & 0xff), // Address, upper byte + (byte) (address >> 8 & 0xff), // Address, middle byte + (byte) (address & 0xff), // Address, lower byte, + + // Data + 0, // ErrorCode + (byte) transportSize, // Transport size + (byte) (data.Length >> 5 & 0xff), + (byte) (data.Length << 3 & 0xff), + }.Concat(data).ToArray(), new byte[] + { + // TPKT + 3, // Version + 0, // Reserved + 0, 22, // Length + + // Data header + 2, // Length + 0b1111_0000, // Data identifier + 0b1_000_0000, // PDU number and end of transmission + + // S7 header + 0x32, // Protocol ID + 0x03, // Message type ack data + 0, 0, // Reserved + 1, 1, // PDU reference + 0, 2, // Parameter length (Write response) + 0, 1, // Data length + 0, // Error class + 0, // Error code + + // Write response + 0x05, // Function code + 1, // Number of items + + // Result code per item + 0xff, // ErrorCode + }.Concat(data).ToArray()); + } + + public Task Serve(out int port) + { + var socket = CreateBoundListenSocket(out port); + socket.Listen(); + + async Task Impl() + { + var socketIn = await socket.AcceptAsync(); + + var buffer = ArrayPool.Shared.Rent(1024); + try + { + foreach (var (request, response) in _sequence) + { + var bytesReceived = await socketIn.ReceiveAsync(buffer, SocketFlags.None); + + var received = buffer.Take(bytesReceived).ToArray(); + _output.WriteLine($"=> {BitConverter.ToString(received)}"); + received.ShouldBe(request); + + _output.WriteLine($"<= {BitConverter.ToString(response)}"); + socketIn.Send(response); + } + } + finally + { + ArrayPool.Shared.Return(buffer); + } + + socketIn.Close(); + } + + return Impl(); + } + + private static Socket CreateBoundListenSocket(out int port) + { + var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + var endpoint = new IPEndPoint(IPAddress.Loopback, 0); + + socket.Bind(endpoint); + + var localEndpoint = (IPEndPoint)socket.LocalEndPoint!; + port = localEndpoint.Port; + + return socket; + } +} \ No newline at end of file diff --git a/Sally7.Tests/Protocol/CommunicationTests.cs b/Sally7.Tests/Protocol/CommunicationTests.cs new file mode 100644 index 0000000..6ceb1cf --- /dev/null +++ b/Sally7.Tests/Protocol/CommunicationTests.cs @@ -0,0 +1,82 @@ +using System.Net; +using Sally7.Protocol.Cotp; +using Sally7.Protocol.S7; +using Xunit.Abstractions; + +namespace Sally7.Tests.Protocol; + +public class CommunicationTests +{ + private readonly ITestOutputHelper _output; + + public CommunicationTests(ITestOutputHelper output) + { + this._output = output; + } + + [Fact] + public async Task Verify_Open() + { + var sourceTsap = new Tsap(201, 202); + var destinationTsap = new Tsap(203, 204); + + var communication = new CommunicationSequence(_output) + .AddConnectRequest(PduSizeParameter.PduSize.Pdu1024, sourceTsap, destinationTsap) + .AddCommunicationSetup(); + + async Task Client(int port) + { + var conn = new S7Connection(IPAddress.Loopback.ToString(), port, sourceTsap, destinationTsap); + await conn.OpenAsync(); + conn.Close(); + } + + await Task.WhenAll(communication.Serve(out var port), Client(port)); + } + + [Fact] + public async Task Verify_Read_Single() + { + var sourceTsap = new Tsap(201, 202); + var destinationTsap = new Tsap(203, 204); + var dataItem = new DataBlockDataItem(9, 6); + + var communication = new CommunicationSequence(_output) + .AddConnectRequest(PduSizeParameter.PduSize.Pdu1024, sourceTsap, destinationTsap) + .AddCommunicationSetup() + .AddRead(Area.DataBlock, 9, 6 << 3, 2, TransportSize.Byte, VariableType.Byte, new byte[] { 2, 1}); + + async Task Client(int port) + { + var conn = new S7Connection(IPAddress.Loopback.ToString(), port, sourceTsap, destinationTsap); + await conn.OpenAsync(); + await conn.ReadAsync(dataItem); + conn.Close(); + } + + await Task.WhenAll(communication.Serve(out var port), Client(port)); + dataItem.Value.ShouldBe((short) 513); + } + + [Fact] + public async Task Verify_Write_Single() + { + var sourceTsap = new Tsap(201, 202); + var destinationTsap = new Tsap(203, 204); + var dataItem = new DataBlockDataItem(9, 6) { Value = 513 }; + + var communication = new CommunicationSequence(_output) + .AddConnectRequest(PduSizeParameter.PduSize.Pdu1024, sourceTsap, destinationTsap).AddCommunicationSetup() + .AddWrite(Area.DataBlock, 9, 6 << 3, 2, TransportSize.Byte, VariableType.Byte, new byte[] { 2, 1 }); + + async Task Client(int port) + { + var conn = new S7Connection(IPAddress.Loopback.ToString(), port, sourceTsap, destinationTsap); + await conn.OpenAsync(); + await conn.WriteAsync(dataItem); + conn.Close(); + } + + await Task.WhenAll(communication.Serve(out var port), Client(port)); + } +} \ No newline at end of file diff --git a/Sally7.Tests/Sally7.Tests.csproj b/Sally7.Tests/Sally7.Tests.csproj index d2c1919..4e09d10 100644 --- a/Sally7.Tests/Sally7.Tests.csproj +++ b/Sally7.Tests/Sally7.Tests.csproj @@ -1,7 +1,7 @@  - net6 + net7.0;net6.0 latest enable enable @@ -9,12 +9,17 @@ + - + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/Sally7.Tests/ToPlcConverterTests.cs b/Sally7.Tests/ToPlcConverterTests.cs index 625a915..0989459 100644 --- a/Sally7.Tests/ToPlcConverterTests.cs +++ b/Sally7.Tests/ToPlcConverterTests.cs @@ -13,7 +13,7 @@ public class Elements public void ConvertByteToPlc(byte value) { // Arrange - var converter = ConverterFactory.GetToPlcConverter(); + var converter = ConverterFactory.GetToPlcConverter(1); var buffer = new byte[sizeof(byte)]; // Act @@ -28,7 +28,7 @@ public void ConvertByteToPlc(byte value) public void ConvertSByteToPlc(sbyte value) { // Arrange - var converter = ConverterFactory.GetToPlcConverter(); + var converter = ConverterFactory.GetToPlcConverter(1); var buffer = new byte[sizeof(sbyte)]; // Act @@ -97,7 +97,7 @@ public void ConvertDoubleToPlc(double value) private static void TestConvertToPlc(T value, int size, Func getBytes) { // Arrange - var converter = ConverterFactory.GetToPlcConverter(); + var converter = ConverterFactory.GetToPlcConverter(1); var buffer = new byte[size]; // Act @@ -118,7 +118,7 @@ public class Arrays public void ConvertFloatArrToPlc(float value) { // Arrange - var converter = ConverterFactory.GetToPlcConverter(); + var converter = ConverterFactory.GetToPlcConverter(1); var buffer = new byte[sizeof(float)]; // Act diff --git a/Sally7.sln.DotSettings b/Sally7.sln.DotSettings new file mode 100644 index 0000000..dc82065 --- /dev/null +++ b/Sally7.sln.DotSettings @@ -0,0 +1,3 @@ + + <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> \ No newline at end of file diff --git a/Sally7/DataBlockDataItem.cs b/Sally7/DataBlockDataItem.cs index 6fe721f..d4c916d 100644 --- a/Sally7/DataBlockDataItem.cs +++ b/Sally7/DataBlockDataItem.cs @@ -1,4 +1,5 @@ using System; +using Sally7.Internal; using Sally7.Protocol; using Sally7.Protocol.S7; using Sally7.ValueConversion; @@ -7,95 +8,75 @@ namespace Sally7 { public class DataBlockDataItem : IDataItem { - private readonly VariableType variableType; - private readonly TransportSize transportSize; - private readonly int elementSize; - private readonly ConvertToS7 toS7Converter; - private readonly ConvertFromS7 fromS7Converter; + private readonly ConvertToS7 _toS7Converter; + private readonly ConvertFromS7 _fromS7Converter; + private TValue? _value; - public DataBlockDataItem() : this(ConverterFactory.GetToPlcConverter(), - ConverterFactory.GetFromPlcConverter(), ConversionHelper.GetElementSize()) + public DataBlockDataItem(BigEndianShort dbNumber, int startByte, int length = 1) : this(dbNumber, startByte, 0, + length) { - if (typeof(TValue) == typeof(bool)) - { - variableType = VariableType.Bit; - transportSize = TransportSize.Bit; - } - else - { - variableType = VariableType.Byte; - transportSize = TransportSize.Byte; - } - } - - private DataBlockDataItem(ConvertToS7 toS7Converter, ConvertFromS7 fromS7Converter, int elementSize) - { - this.toS7Converter = toS7Converter; - this.fromS7Converter = fromS7Converter; - this.elementSize = elementSize; - - if (typeof(TValue).IsValueType) SetLength(1); - } - - private Address address; - private int startByte; - private int bit; - private TValue? value; - private int length; - - public BigEndianShort DbNumber { get; set; } - public BigEndianShort ReadCount { get; private set; } - - public int Length - { - get => length; - set => SetLength(value); - } - - public TValue? Value - { - get => value; - set => this.value = value; } - public int StartByte + public DataBlockDataItem(BigEndianShort dbNumber, int startByte, int bit, int length = 1) { - get => startByte; - set => address.FromStartByteAndBit(startByte = value, bit); - } + Assertions.AssertDataItemLengthIsValidForType(length, typeof(TValue)); + Assertions.AssertBitIsValidForType(bit, typeof(TValue)); - public int Bit - { - get => bit; - set => address.FromStartByteAndBit(startByte, bit = value); - } + _toS7Converter = ConverterFactory.GetToPlcConverter(length); + _fromS7Converter = ConverterFactory.GetFromPlcConverter(length); + var elementSize = ConversionHelper.GetElementSize(); - Address IDataItem.Address => address; - Area IDataItem.Area => Area.DataBlock; - TransportSize IDataItem.TransportSize => transportSize; - VariableType IDataItem.VariableType => variableType; + DbNumber = dbNumber; + StartByte = startByte; + Bit = bit; + Length = length; - int IDataItem.WriteValue(Span output) => toS7Converter.Invoke(value, Length, output); + if (typeof(TValue) == typeof(bool)) + { + VariableType = VariableType.Bit; + TransportSize = TransportSize.Bit; + } + else + { + VariableType = VariableType.Byte; + TransportSize = TransportSize.Byte; + } - void IDataItem.ReadValue(ReadOnlySpan input) => fromS7Converter.Invoke(ref value, input, Length); + Address = Address.FromStartByteAndBit(startByte, bit); - private void SetLength(int newLength) - { - if (length == newLength) return; - - length = newLength; if (typeof(TValue) == typeof(string)) { ReadCount = length + 2; } else if (typeof(TValue) == typeof(bool[])) { - ReadCount = (length + 7) >> 3; // bit-hack for (length + 7) / 8 + ReadCount = (length + 7) >> 3; // Round to bytes } else { ReadCount = length * elementSize; } } + + public BigEndianShort DbNumber { get; } + public int StartByte { get; } + public int Bit { get; } + public int Length { get; } + + public TValue? Value + { + get => _value; + set => this._value = value; + } + + public Address Address { get; } + public BigEndianShort ReadCount { get; } + public Area Area => Area.DataBlock; + public TransportSize TransportSize { get; } + public VariableType VariableType { get; } + + int IDataItem.WriteValue(Span output) => _toS7Converter.Invoke(Value, Length, output); + + void IDataItem.ReadValue(ReadOnlySpan input) => _fromS7Converter.Invoke(ref _value, input, Length); } } \ No newline at end of file diff --git a/Sally7/Infrastructure/SpanExtensions.cs b/Sally7/Infrastructure/ConvertExtensions.cs similarity index 66% rename from Sally7/Infrastructure/SpanExtensions.cs rename to Sally7/Infrastructure/ConvertExtensions.cs index 6b0522c..9912d77 100644 --- a/Sally7/Infrastructure/SpanExtensions.cs +++ b/Sally7/Infrastructure/ConvertExtensions.cs @@ -1,10 +1,14 @@ using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Sally7.Infrastructure { - internal static class SpanExtensions + internal static class ConvertExtensions { + public static ref T AsStruct(this ref byte destination) where T : struct => + ref Unsafe.As(ref destination); + public static ref T Struct(this Span span, int offset) where T : struct => ref MemoryMarshal.Cast(span.Slice(offset))[0]; diff --git a/Sally7/Internal/Assertions.cs b/Sally7/Internal/Assertions.cs index 0257d48..bdc3078 100644 --- a/Sally7/Internal/Assertions.cs +++ b/Sally7/Internal/Assertions.cs @@ -16,5 +16,46 @@ public static void AssertTimeoutIsValid(TimeSpan value) private static void ThrowTimeoutIsInvalid(TimeSpan value) => throw new ArgumentOutOfRangeException(nameof(value), $"The timeout {value.TotalMilliseconds}ms is less than -1 or greater than Int32.MaxValue."); + + public static void AssertDataItemLengthIsValidForType(int length, Type type) + { + if (length < 1) + { + throw new ArgumentOutOfRangeException(nameof(length), + $"Length must be greater than or equal to 1. Value provided was: {length}."); + } + + if (type.IsValueType && length != 1) + { + throw new ArgumentOutOfRangeException(nameof(length), + $"Length can't be set for value of type {type} because it is of constant size. Value provided was: {length}."); + } + + if (type == typeof(string) && length > byte.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(length), + $"Length of a string can't be greater than 255. Value provided was: {length}."); + } + } + + public static void AssertBitIsValidForType(int bit, Type type) + { + if (bit != 0 && type != typeof(bool)) + { + throw new ArgumentException("Bit can only be set when value type is boolean.", nameof(bit)); + } + + if (bit < 0) + { + throw new ArgumentOutOfRangeException(nameof(bit), + $"Bit value can't be less than 0. Value provided was: {bit}."); + } + + if (bit > 7) + { + throw new ArgumentOutOfRangeException(nameof(bit), + $"Bit value can't be greater than 7 (increment {nameof(DataBlockDataItem.StartByte)} instead). Value provided was: {bit}."); + } + } } } diff --git a/Sally7/Internal/NetworkOrderSerializer.cs b/Sally7/Internal/NetworkOrderSerializer.cs new file mode 100644 index 0000000..73ac698 --- /dev/null +++ b/Sally7/Internal/NetworkOrderSerializer.cs @@ -0,0 +1,26 @@ +using System; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; + +namespace Sally7.Internal; + +internal static class NetworkOrderSerializer +{ + public static void WriteUInt16(ref byte destination, ushort value) + { + Unsafe.WriteUnaligned(ref destination, + BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(value) : value); + } + + public static void WriteUInt32(ref byte destination, uint value) + { + Unsafe.WriteUnaligned(ref destination, + BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(value) : value); + } + + public static void WriteUInt64(ref byte destination, ulong value) + { + Unsafe.WriteUnaligned(ref destination, + BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(value) : value); + } +} \ No newline at end of file diff --git a/Sally7/Internal/SocketAwaitable.cs b/Sally7/Internal/SocketAwaitable.cs index c76eef9..9bfa336 100644 --- a/Sally7/Internal/SocketAwaitable.cs +++ b/Sally7/Internal/SocketAwaitable.cs @@ -18,7 +18,7 @@ internal sealed class SocketAwaitable : INotifyCompletion private static readonly Action Sentinel = () => { }; public bool WasCompleted; - private Action? continuation; + private Action? _continuation; public readonly SocketAsyncEventArgs EventArgs; public SocketAwaitable(SocketAsyncEventArgs eventArgs) @@ -26,7 +26,7 @@ public SocketAwaitable(SocketAsyncEventArgs eventArgs) EventArgs = eventArgs ?? throw new ArgumentNullException(nameof(eventArgs)); eventArgs.Completed += delegate { - var prev = continuation ?? Interlocked.CompareExchange(ref continuation, Sentinel, null); + var prev = _continuation ?? Interlocked.CompareExchange(ref _continuation, Sentinel, null); prev?.Invoke(); }; } @@ -34,7 +34,7 @@ public SocketAwaitable(SocketAsyncEventArgs eventArgs) internal void Reset() { WasCompleted = false; - continuation = null; + _continuation = null; } public SocketAwaitable GetAwaiter() @@ -46,8 +46,8 @@ public SocketAwaitable GetAwaiter() public void OnCompleted(Action continuation) { - if (this.continuation == Sentinel || - Interlocked.CompareExchange(ref this.continuation, continuation, null) == Sentinel) + if (this._continuation == Sentinel || + Interlocked.CompareExchange(ref this._continuation, continuation, null) == Sentinel) { continuation.Invoke(); } diff --git a/Sally7/Internal/SocketTpktReader.cs b/Sally7/Internal/SocketTpktReader.cs index 28270ed..d617ac5 100644 --- a/Sally7/Internal/SocketTpktReader.cs +++ b/Sally7/Internal/SocketTpktReader.cs @@ -9,7 +9,7 @@ internal partial class SocketTpktReader { private const int TpktSize = 4; - private readonly Socket socket; + private readonly Socket _socket; private static int GetTpktLength(ReadOnlySpan span) { diff --git a/Sally7/Internal/SocketTpktReader.desktop.cs b/Sally7/Internal/SocketTpktReader.desktop.cs index 115ae8e..ef662c1 100644 --- a/Sally7/Internal/SocketTpktReader.desktop.cs +++ b/Sally7/Internal/SocketTpktReader.desktop.cs @@ -10,14 +10,14 @@ namespace Sally7.Internal { internal partial class SocketTpktReader { - private readonly SocketAwaitable awaitable; - private readonly SocketAsyncEventArgs args; + private readonly SocketAwaitable _awaitable; + private readonly SocketAsyncEventArgs _args; public SocketTpktReader(Socket socket) { - this.socket = socket; - args = new SocketAsyncEventArgs(); - awaitable = new SocketAwaitable(args); + this._socket = socket; + _args = new SocketAsyncEventArgs(); + _awaitable = new SocketAwaitable(_args); } public async ValueTask ReadAsync(Memory message, CancellationToken cancellationToken) @@ -27,25 +27,25 @@ public async ValueTask ReadAsync(Memory message, CancellationToken ca Sally7Exception.ThrowMemoryWasNotArrayBased(); } - args.SetBuffer(segment.Array, segment.Offset, TpktSize); + _args.SetBuffer(segment.Array, segment.Offset, TpktSize); int count = 0; do { if (count > 0) { - args.SetBuffer(segment.Offset + count, TpktSize - count); + _args.SetBuffer(segment.Offset + count, TpktSize - count); } cancellationToken.ThrowIfCancellationRequested(); - await socket.ReceiveAsync(awaitable); + await _socket.ReceiveAsync(_awaitable); - if (args.BytesTransferred <= 0) + if (_args.BytesTransferred <= 0) { TpktException.ThrowConnectionWasClosedWhileReading(); } - count += args.BytesTransferred; + count += _args.BytesTransferred; } while (count < TpktSize); @@ -54,15 +54,15 @@ public async ValueTask ReadAsync(Memory message, CancellationToken ca while (count < receivedLength) { cancellationToken.ThrowIfCancellationRequested(); - args.SetBuffer(segment.Offset + count, receivedLength - count); - await socket.ReceiveAsync(awaitable); + _args.SetBuffer(segment.Offset + count, receivedLength - count); + await _socket.ReceiveAsync(_awaitable); - if (args.BytesTransferred <= 0) + if (_args.BytesTransferred <= 0) { TpktException.ThrowConnectionWasClosedWhileReading(); } - count += args.BytesTransferred; + count += _args.BytesTransferred; } return receivedLength; diff --git a/Sally7/Internal/SocketTpktReader.netcore.cs b/Sally7/Internal/SocketTpktReader.netcore.cs index a16379e..79d1983 100644 --- a/Sally7/Internal/SocketTpktReader.netcore.cs +++ b/Sally7/Internal/SocketTpktReader.netcore.cs @@ -9,7 +9,7 @@ namespace Sally7.Internal { internal partial class SocketTpktReader { - public SocketTpktReader(Socket socket) => this.socket = socket; + public SocketTpktReader(Socket socket) => this._socket = socket; public async ValueTask ReadAsync(Memory message, CancellationToken cancellationToken) { @@ -22,7 +22,7 @@ public async ValueTask ReadAsync(Memory message, CancellationToken ca buffer = message[count..TpktSize]; } - int read = await socket.ReceiveAsync(buffer, SocketFlags.None, cancellationToken).ConfigureAwait(false); + int read = await _socket.ReceiveAsync(buffer, SocketFlags.None, cancellationToken).ConfigureAwait(false); if (read <= 0) { @@ -38,7 +38,7 @@ public async ValueTask ReadAsync(Memory message, CancellationToken ca while (count < receivedLength) { buffer = message[count..receivedLength]; - int read = await socket.ReceiveAsync(buffer, SocketFlags.None, cancellationToken).ConfigureAwait(false); + int read = await _socket.ReceiveAsync(buffer, SocketFlags.None, cancellationToken).ConfigureAwait(false); if (read <= 0) { diff --git a/Sally7/Internal/TypeExtensions.cs b/Sally7/Internal/TypeExtensions.cs new file mode 100644 index 0000000..92d65ed --- /dev/null +++ b/Sally7/Internal/TypeExtensions.cs @@ -0,0 +1,15 @@ +using System.Runtime.CompilerServices; + +namespace Sally7.Internal; + +internal static class TypeExtensions +{ + public static ref byte GetOffset(this ref byte destination, uint offset) + { +#if NET6_0_OR_GREATER + return ref Unsafe.Add(ref destination, offset); +#else + return ref Unsafe.Add(ref destination, (int) offset); +#endif + } +} \ No newline at end of file diff --git a/Sally7/Internal/WireFormatting.cs b/Sally7/Internal/WireFormatting.cs new file mode 100644 index 0000000..e4e1d0d --- /dev/null +++ b/Sally7/Internal/WireFormatting.cs @@ -0,0 +1,65 @@ +using Sally7.Protocol.S7.Messages; +using Sally7.RequestExecutor; + +namespace Sally7.Internal; + +internal static class WireFormatting +{ + private const uint Tpkt = 0x03_00_00_00; + + /// + /// Data struct. + /// + /// + /// This struct is only 3 bytes long, final byte is overwritten. + /// + private const int Data = 2 << 24 // Length + | 0b1111_0000 << 16 // Identifier + | 0b1000_0000 << 8; // PDU number and EOT + + /// + /// The PDU reference. + /// + /// + /// This value is here for legacy reasons. The modifies the lower + /// byte to identify responses, the upper byte isn't actually used at the moment. + /// + private const ushort PduRef = 1 << 8; + + private static ulong JobRequestHeader1 = 0x32L << 56 | (long)MessageType.JobRequest << 48 | PduRef << 16; + + public static uint WriteTpkt(ref byte destination, int length) + { + return WriteUInt32(ref destination, (uint) (Tpkt | length)); + } + + public static uint WriteData(ref byte destination) + { + // For performance we write an int instead of separate values, the final byte of the int will be overwritten. + WriteUInt32(ref destination, Data); + + return 3; + } + + public static uint WriteJobRequestHeader(ref byte destination, int paramLength, int dataLength) + { + var header = JobRequestHeader1 | (ushort) paramLength; + + NetworkOrderSerializer.WriteUInt64(ref destination, header); + NetworkOrderSerializer.WriteUInt16(ref destination.GetOffset(8), (ushort)dataLength); + + return 10; + } + + public static uint WriteUInt16(ref byte destination, ushort value) + { + NetworkOrderSerializer.WriteUInt16(ref destination, value); + return sizeof(ushort); + } + + public static uint WriteUInt32(ref byte destination, uint value) + { + NetworkOrderSerializer.WriteUInt32(ref destination, value); + return sizeof(uint); + } +} \ No newline at end of file diff --git a/Sally7/Protocol/BigEndianShort.cs b/Sally7/Protocol/BigEndianShort.cs index add096e..a008bb4 100644 --- a/Sally7/Protocol/BigEndianShort.cs +++ b/Sally7/Protocol/BigEndianShort.cs @@ -1,4 +1,7 @@ -using System.Runtime.InteropServices; +using System; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace Sally7.Protocol { @@ -8,10 +11,21 @@ public struct BigEndianShort public byte High; public byte Low; - public static implicit operator BigEndianShort(int value) => - new BigEndianShort {High = (byte) (value >> 8), Low = (byte) value}; + public static implicit operator BigEndianShort(int intValue) + { + var value = BitConverter.IsLittleEndian + ? BinaryPrimitives.ReverseEndianness((ushort)intValue) + : (ushort)intValue; + return Unsafe.As(ref value); + } - public static implicit operator BigEndianShort(byte value) => new BigEndianShort {High = 0, Low = value}; + public static implicit operator BigEndianShort(byte byteValue) + { + var value = BitConverter.IsLittleEndian + ? BinaryPrimitives.ReverseEndianness((ushort)byteValue) + : byteValue; + return Unsafe.As(ref value); + } public static implicit operator int(BigEndianShort ns) => ns.High << 8 | ns.Low; diff --git a/Sally7/Protocol/Cotp/Data.cs b/Sally7/Protocol/Cotp/Data.cs index e40920a..717cd87 100644 --- a/Sally7/Protocol/Cotp/Data.cs +++ b/Sally7/Protocol/Cotp/Data.cs @@ -10,13 +10,6 @@ internal struct Data public byte DataIdentifier; public byte PduNumberAndEot; - public void Init() - { - Length = 2; - DataIdentifier = 0b1111_0000; - PduNumberAndEot = 0b1_000_0000; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly void Assert() { diff --git a/Sally7/Protocol/IsoOverTcp/Tpkt.cs b/Sally7/Protocol/IsoOverTcp/Tpkt.cs index 3fa4527..4f0717d 100644 --- a/Sally7/Protocol/IsoOverTcp/Tpkt.cs +++ b/Sally7/Protocol/IsoOverTcp/Tpkt.cs @@ -32,13 +32,6 @@ public readonly void Assert() } } - public void Init(BigEndianShort length) - { - Version = IsoVersion; - Reserved = 0; - Length = length; - } - public readonly int MessageLength() => Length - 4; } } \ No newline at end of file diff --git a/Sally7/Protocol/S7/Address.cs b/Sally7/Protocol/S7/Address.cs index 3b4da60..b374428 100644 --- a/Sally7/Protocol/S7/Address.cs +++ b/Sally7/Protocol/S7/Address.cs @@ -9,12 +9,10 @@ public struct Address public byte Mid; public byte Low; - public void FromStartByteAndBit(int startByte, int bit) + public static Address FromStartByteAndBit(int startByte, int bit) { var value = startByte * 8 + bit; - Low = (byte) value; - Mid = (byte) (value >> 8); - High = (byte) (value >> 16); + return new Address { Low = (byte)value, Mid = (byte)(value >> 8), High = (byte)(value >> 16) }; } public static implicit operator Address(int value) diff --git a/Sally7/Protocol/S7/Messages/Header.cs b/Sally7/Protocol/S7/Messages/Header.cs index 55f542d..86d855a 100644 --- a/Sally7/Protocol/S7/Messages/Header.cs +++ b/Sally7/Protocol/S7/Messages/Header.cs @@ -11,16 +11,6 @@ internal struct Header public HeaderErrorClass ErrorClass; public byte ErrorCode; - public void Init(MessageType messageType, BigEndianShort paramLength, BigEndianShort dataLength) - { - ProtocolId = 0x32; - MessageType = messageType; - Reserved = default; - PduRef = 1; - ParamLength = paramLength; - DataLength = dataLength; - } - public readonly void Assert(MessageType messageType) { if (ProtocolId != 0x32) diff --git a/Sally7/RequestExecutor/ConcurrentRequestExecutor.cs b/Sally7/RequestExecutor/ConcurrentRequestExecutor.cs index af71170..e2e3b68 100644 --- a/Sally7/RequestExecutor/ConcurrentRequestExecutor.cs +++ b/Sally7/RequestExecutor/ConcurrentRequestExecutor.cs @@ -16,17 +16,17 @@ internal sealed class ConcurrentRequestExecutor : IRequestExecutor { private const int JobIdIndex = 12; - private readonly Socket socket; - private readonly int bufferSize; - private readonly int maxRequests; - private readonly MemoryPool memoryPool; - private readonly SocketTpktReader reader; - private readonly JobPool jobPool; - private readonly Signal sendSignal; - private readonly Signal receiveSignal; + private readonly Socket _socket; + private readonly int _bufferSize; + private readonly int _maxRequests; + private readonly MemoryPool _memoryPool; + private readonly SocketTpktReader _reader; + private readonly JobPool _jobPool; + private readonly Signal _sendSignal; + private readonly Signal _receiveSignal; #if !NETSTANDARD2_1_OR_GREATER && !NET5_0_OR_GREATER - private readonly SocketAwaitable sendAwaitable; + private readonly SocketAwaitable _sendAwaitable; #endif /// @@ -48,55 +48,55 @@ public ConcurrentRequestExecutor(S7Connection connection, MemoryPool? memo } Connection = connection; - socket = connection.TcpClient.Client; - bufferSize = connection.Parameters.GetRequiredBufferSize(); - maxRequests = connection.Parameters.MaximumNumberOfConcurrentRequests; - this.memoryPool = memoryPool ?? MemoryPool.Shared; + _socket = connection.TcpClient.Client; + _bufferSize = connection.Parameters.GetRequiredBufferSize(); + _maxRequests = connection.Parameters.MaximumNumberOfConcurrentRequests; + this._memoryPool = memoryPool ?? MemoryPool.Shared; - jobPool = new JobPool(connection.Parameters.MaximumNumberOfConcurrentRequests); - sendSignal = new Signal(); - receiveSignal = new Signal(); + _jobPool = new JobPool(connection.Parameters.MaximumNumberOfConcurrentRequests); + _sendSignal = new Signal(); + _receiveSignal = new Signal(); - if (!sendSignal.TryInit()) Sally7Exception.ThrowFailedToInitSendingSignal(); - if (!receiveSignal.TryInit()) Sally7Exception.ThrowFailedToInitReceivingSignal(); + if (!_sendSignal.TryInit()) Sally7Exception.ThrowFailedToInitSendingSignal(); + if (!_receiveSignal.TryInit()) Sally7Exception.ThrowFailedToInitReceivingSignal(); - reader = new SocketTpktReader(socket); + _reader = new SocketTpktReader(_socket); #if !NETSTANDARD2_1_OR_GREATER && !NET5_0_OR_GREATER - sendAwaitable = new SocketAwaitable(new SocketAsyncEventArgs()); + _sendAwaitable = new SocketAwaitable(new SocketAsyncEventArgs()); #endif } public void Dispose() { - jobPool.Dispose(); - sendSignal.Dispose(); - receiveSignal.Dispose(); + _jobPool.Dispose(); + _sendSignal.Dispose(); + _receiveSignal.Dispose(); } /// public async ValueTask> PerformRequest(ReadOnlyMemory request, Memory response, CancellationToken cancellationToken) { - int jobId = await jobPool.RentJobIdAsync(cancellationToken).ConfigureAwait(false); + int jobId = await _jobPool.RentJobIdAsync(cancellationToken).ConfigureAwait(false); try { - jobPool.SetBufferForRequest(jobId, response); + _jobPool.SetBufferForRequest(jobId, response); - using (IMemoryOwner mo = memoryPool.Rent(request.Length)) + using (IMemoryOwner mo = _memoryPool.Rent(request.Length)) { request.CopyTo(mo.Memory); mo.Memory.Span[JobIdIndex] = (byte) jobId; - _ = await sendSignal.WaitAsync(cancellationToken).ConfigureAwait(false); + _ = await _sendSignal.WaitAsync(cancellationToken).ConfigureAwait(false); // If we bail while sending the PLC might still respond to the data that was sent. // This both breaks the send-one-receive-one flow as well as it might end up // completing a new job that reused the ID. - var closeOnCancel = cancellationToken.MaybeUnsafeRegister(SocketHelper.CloseSocketCallback, socket); + var closeOnCancel = cancellationToken.MaybeUnsafeRegister(SocketHelper.CloseSocketCallback, _socket); try { #if NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER - int written = await socket.SendAsync(mo.Memory.Slice(0, request.Length), SocketFlags.None, cancellationToken).ConfigureAwait(false); + int written = await _socket.SendAsync(mo.Memory.Slice(0, request.Length), SocketFlags.None, cancellationToken).ConfigureAwait(false); Debug.Assert(written == request.Length); #else if (!MemoryMarshal.TryGetArray(mo.Memory.Slice(0, request.Length), out ArraySegment segment)) @@ -104,8 +104,8 @@ public async ValueTask> PerformRequest(ReadOnlyMemory request Sally7Exception.ThrowMemoryWasNotArrayBased(); } - sendAwaitable.EventArgs.SetBuffer(segment.Array, segment.Offset, segment.Count); - await socket.SendAsync(sendAwaitable); + _sendAwaitable.EventArgs.SetBuffer(segment.Array, segment.Offset, segment.Count); + await _socket.SendAsync(_sendAwaitable); #endif } finally @@ -116,7 +116,7 @@ public async ValueTask> PerformRequest(ReadOnlyMemory request closeOnCancel.Dispose(); #endif - if (!sendSignal.TryRelease()) + if (!_sendSignal.TryRelease()) { Sally7Exception.ThrowFailedToSignalSendDone(); } @@ -128,18 +128,18 @@ public async ValueTask> PerformRequest(ReadOnlyMemory request Request rec; var length = 0; - using (IMemoryOwner mo = memoryPool.Rent(bufferSize)) + using (IMemoryOwner mo = _memoryPool.Rent(_bufferSize)) { - _ = await receiveSignal.WaitAsync(cancellationToken).ConfigureAwait(false); + _ = await _receiveSignal.WaitAsync(cancellationToken).ConfigureAwait(false); // If we bail while reading we break the send-one-receive-one flow, so we might as well close right away. // There is minimal risk of closing connections while data was actually received but handling here // avoids registering on the cancellationToken on every socket call. var closeOnCancel = - cancellationToken.MaybeUnsafeRegister(SocketHelper.CloseSocketCallback, socket); + cancellationToken.MaybeUnsafeRegister(SocketHelper.CloseSocketCallback, _socket); try { - length = await reader.ReadAsync(mo.Memory, cancellationToken).ConfigureAwait(false); + length = await _reader.ReadAsync(mo.Memory, cancellationToken).ConfigureAwait(false); } catch (Exception) when (cancellationToken.IsCancellationRequested) { @@ -153,7 +153,7 @@ public async ValueTask> PerformRequest(ReadOnlyMemory request closeOnCancel.Dispose(); #endif - if (!receiveSignal.TryRelease()) + if (!_receiveSignal.TryRelease()) { Sally7Exception.ThrowFailedToSignalReceiveDone(); } @@ -162,7 +162,7 @@ public async ValueTask> PerformRequest(ReadOnlyMemory request Memory message = mo.Memory.Slice(0, length); int replyJobId = mo.Memory.Span[JobIdIndex]; - if ((uint)(replyJobId - 1) >= (uint)maxRequests) + if ((uint)(replyJobId - 1) >= (uint)_maxRequests) { // todo: This is breaking, because we return the current jobId to the pool // so when that gets answered it might complete an incorrect request. @@ -170,7 +170,7 @@ public async ValueTask> PerformRequest(ReadOnlyMemory request } // todo: There's no state validation on the request, potentially this is not rented out at all. - rec = jobPool.GetRequest(replyJobId); + rec = _jobPool.GetRequest(replyJobId); message.CopyTo(rec.Buffer); } @@ -178,11 +178,11 @@ public async ValueTask> PerformRequest(ReadOnlyMemory request rec.Complete(length); // await the actual completion before returning this job ID to the pool - return await jobPool.GetRequest(jobId); + return await _jobPool.GetRequest(jobId); } finally { - jobPool.ReturnJobId(jobId); + _jobPool.ReturnJobId(jobId); } } } diff --git a/Sally7/S7Connection.cs b/Sally7/S7Connection.cs index ff2b433..c691bd3 100644 --- a/Sally7/S7Connection.cs +++ b/Sally7/S7Connection.cs @@ -20,25 +20,29 @@ public sealed class S7Connection : IDisposable /// public static TimeSpan DefaultRequestTimeout => TimeSpan.FromSeconds(5); - private const int IsoOverTcpPort = 102; + /// + /// The default port number used for S7 communication. + /// + public const int DefaultPort = 102; - private readonly string host; - private readonly Tsap sourceTsap; - private readonly Tsap destinationTsap; - private readonly RequestExecutorFactory executorFactory; + private readonly string _host; + private readonly int _port = DefaultPort; + private readonly Tsap _sourceTsap; + private readonly Tsap _destinationTsap; + private readonly RequestExecutorFactory _executorFactory; - private int bufferSize; - private MemoryPool? memoryPool; - private IRequestExecutor? requestExecutor; + private int _bufferSize; + private MemoryPool? _memoryPool; + private IRequestExecutor? _requestExecutor; public static RequestExecutorFactory DefaultRequestExecutorFactory { get; set; } = - conn => new ConcurrentRequestExecutor(conn, conn.memoryPool); + conn => new ConcurrentRequestExecutor(conn, conn._memoryPool); public TcpClient TcpClient { get; } = new() {NoDelay = true}; public IS7ConnectionParameters? Parameters { get; private set; } - private TimeSpan requestTimeout = DefaultRequestTimeout; + private TimeSpan _requestTimeout = DefaultRequestTimeout; /// /// Gets or sets the timeout for performing requests. @@ -51,12 +55,12 @@ public sealed class S7Connection : IDisposable /// public TimeSpan RequestTimeout { - get => requestTimeout; + get => _requestTimeout; set { Assertions.AssertTimeoutIsValid(value); - requestTimeout = value; + _requestTimeout = value; } } @@ -75,11 +79,36 @@ public TimeSpan RequestTimeout /// public S7Connection(string host, Tsap sourceTsap, Tsap destinationTsap, MemoryPool? memoryPool = default, RequestExecutorFactory? executorFactory = default) { - this.host = host; - this.sourceTsap = sourceTsap; - this.destinationTsap = destinationTsap; - this.memoryPool = memoryPool; - this.executorFactory = executorFactory ?? DefaultRequestExecutorFactory; + this._host = host; + this._sourceTsap = sourceTsap; + this._destinationTsap = destinationTsap; + this._memoryPool = memoryPool; + this._executorFactory = executorFactory ?? DefaultRequestExecutorFactory; + } + + /// + /// Initializes a new instance of the class with a specified host, port, + /// source TSAP and destination TSAP. + /// + /// Use the to create a connection using default TSAP values. + /// + /// The PLC host, specified as IP address or hostname. + /// The TCP port to connect to. + /// The local TSAP for the connection. + /// The remote TSAP for the connection. + /// The memory pool used to allocate buffers. + /// + /// The factory used to create an executor after the connection is initialized. + /// + public S7Connection(string host, int port, Tsap sourceTsap, Tsap destinationTsap, MemoryPool? memoryPool = default, + RequestExecutorFactory? executorFactory = default) + { + this._host = host; + this._port = port; + this._sourceTsap = sourceTsap; + this._destinationTsap = destinationTsap; + this._memoryPool = memoryPool; + this._executorFactory = executorFactory ?? DefaultRequestExecutorFactory; } /// @@ -104,9 +133,9 @@ public void Close() public void Dispose() { TcpClient.Dispose(); - requestExecutor?.Dispose(); + _requestExecutor?.Dispose(); - if (memoryPool is Sally7MemoryPool mp) + if (_memoryPool is Sally7MemoryPool mp) { mp.Dispose(); } @@ -126,10 +155,10 @@ public async Task OpenAsync(CancellationToken cancellationToken = default) linkedToken.MaybeUnsafeRegister(SocketHelper.CloseSocketCallback, TcpClient.Client); #if NET5_0_OR_GREATER - await TcpClient.ConnectAsync(host, IsoOverTcpPort, linkedToken).ConfigureAwait(false); + await TcpClient.ConnectAsync(_host, _port, linkedToken).ConfigureAwait(false); #else linkedToken.ThrowIfCancellationRequested(); - await TcpClient.ConnectAsync(host, IsoOverTcpPort).ConfigureAwait(false); + await TcpClient.ConnectAsync(_host, _port).ConfigureAwait(false); #endif var stream = TcpClient.GetStream(); @@ -138,7 +167,7 @@ public async Task OpenAsync(CancellationToken cancellationToken = default) try { await stream.FrameworkSpecificWriteAsync(buffer, 0, - S7ConnectionHelpers.BuildConnectRequest(buffer, sourceTsap, destinationTsap), + S7ConnectionHelpers.BuildConnectRequest(buffer, _sourceTsap, _destinationTsap), cancellationToken) .ConfigureAwait(false); @@ -154,11 +183,11 @@ await stream out var maxRequests); Parameters = new S7ConnectionParameters(pduSize, maxRequests); - bufferSize = Parameters.GetRequiredBufferSize(); + _bufferSize = Parameters.GetRequiredBufferSize(); - memoryPool ??= new Sally7MemoryPool(bufferSize); + _memoryPool ??= new Sally7MemoryPool(_bufferSize); - requestExecutor = executorFactory.Invoke(this); + _requestExecutor = _executorFactory.Invoke(this); } finally { @@ -187,7 +216,7 @@ public async Task ReadAsync(IDataItem[] dataItems, CancellationToken cancellatio IRequestExecutor executor = GetExecutorOrThrow(); - using IMemoryOwner mo = memoryPool!.Rent(bufferSize); + using IMemoryOwner mo = _memoryPool!.Rent(_bufferSize); Memory mem = mo.Memory; int length = S7ConnectionHelpers.BuildReadRequest(mem.Span, dataItems); @@ -214,7 +243,7 @@ public async Task WriteAsync(IDataItem[] dataItems, CancellationToken cancellati { IRequestExecutor executor = GetExecutorOrThrow(); - using IMemoryOwner mo = memoryPool!.Rent(bufferSize); + using IMemoryOwner mo = _memoryPool!.Rent(_bufferSize); Memory mem = mo.Memory; int length = S7ConnectionHelpers.BuildWriteRequest(mem.Span, dataItems); @@ -242,14 +271,14 @@ public async Task WriteAsync(IDataItem[] dataItems, CancellationToken cancellati private IRequestExecutor GetExecutorOrThrow() { - if (requestExecutor is null) + if (_requestExecutor is null) { Throw(); [DoesNotReturn] static void Throw() => throw new InvalidOperationException("Can't perform read when the connection is not yet open."); } - return requestExecutor; + return _requestExecutor; } private CancellationTokenSource CreateRequestTimeoutCancellationTokenSource(CancellationToken userToken) diff --git a/Sally7/S7ConnectionHelpers.cs b/Sally7/S7ConnectionHelpers.cs index ce6e5e2..95dd742 100644 --- a/Sally7/S7ConnectionHelpers.cs +++ b/Sally7/S7ConnectionHelpers.cs @@ -2,10 +2,9 @@ using System.Collections.Generic; using System.Runtime.InteropServices; using Sally7.Infrastructure; -using Sally7.Protocol; +using Sally7.Internal; using Sally7.Protocol.Cotp; using Sally7.Protocol.Cotp.Messages; -using Sally7.Protocol.IsoOverTcp; using Sally7.Protocol.S7; using Sally7.Protocol.S7.Messages; @@ -15,29 +14,29 @@ internal static class S7ConnectionHelpers { public static int BuildConnectRequest(Span buffer, Tsap sourceTsap, Tsap destinationTsap) { + ref var start = ref MemoryMarshal.GetReference(buffer); + ref var message = ref buffer.Struct(4); message.Init(PduSizeParameter.PduSize.Pdu1024, sourceTsap, destinationTsap); + var len = 4 + ConnectionRequestMessage.Size; - buffer.Struct(0).Init(len); + WireFormatting.WriteTpkt(ref start, len); return len; } public static int BuildCommunicationSetup(Span buffer) { - ref var data = ref buffer.Struct(4); - data.Init(); + ref var start = ref MemoryMarshal.GetReference(buffer); - ref var header = ref buffer.Struct
(7); - header.Init(MessageType.JobRequest, CommunicationSetup.Size, 0); + var len = 17 + CommunicationSetup.Size; + var offset = WireFormatting.WriteTpkt(ref start, len); + offset += WireFormatting.WriteData(ref start.GetOffset(offset)); + offset += WireFormatting.WriteJobRequestHeader(ref start.GetOffset(offset), CommunicationSetup.Size, 0); - // Error class and error code are not used, so next starts at 7 + 10 - ref var setup = ref buffer.Struct(17); + ref var setup = ref start.GetOffset(offset).AsStruct(); setup.Init(10, 10, 960); - var len = 17 + CommunicationSetup.Size; - buffer.Struct(0).Init(len); - return len; } @@ -112,12 +111,13 @@ private static void BuildRequestItem(ref RequestItem requestItem, IDataItem data requestItem.VariableType = dataItem.VariableType; } - private static int BuildS7JobRequest(Span buffer, BigEndianShort parameterLength, BigEndianShort dataLength) + private static int BuildS7JobRequest(Span buffer, int parameterLength, int dataLength) { + ref var start = ref MemoryMarshal.GetReference(buffer); var len = parameterLength + dataLength + 17; // Error omitted - buffer.Struct(0).Init(len); - buffer.Struct(4).Init(); - buffer.Struct
(7).Init(MessageType.JobRequest, parameterLength, dataLength); + var offset = WireFormatting.WriteTpkt(ref start, len); + offset += WireFormatting.WriteData(ref start.GetOffset(offset)); + offset += WireFormatting.WriteJobRequestHeader(ref start.GetOffset(offset), parameterLength, dataLength); return len; } diff --git a/Sally7/Sally7.csproj b/Sally7/Sally7.csproj index 2af1f01..6c21680 100644 --- a/Sally7/Sally7.csproj +++ b/Sally7/Sally7.csproj @@ -1,7 +1,7 @@  - net46;net461;netstandard2.0;netstandard2.1;net5.0 + net461;netstandard2.0;netstandard2.1;net5.0;net6.0;net7.0 true true true @@ -24,6 +24,7 @@ + @@ -45,7 +46,7 @@ - + diff --git a/Sally7/ValueConversion/BufferHelper.cs b/Sally7/ValueConversion/BufferHelper.cs new file mode 100644 index 0000000..20b8e47 --- /dev/null +++ b/Sally7/ValueConversion/BufferHelper.cs @@ -0,0 +1,69 @@ +using Sally7.Internal; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Sally7.ValueConversion; + +internal static class BufferHelper +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CopyAndAlign64Bit(ReadOnlySpan input, Span output, int numberOfItems) + { + ref var destination = ref MemoryMarshal.GetReference(output); + ref var source = ref MemoryMarshal.GetReference(input); + + var limit = (uint) numberOfItems * sizeof(ulong); + + var offset = 0u; + while (offset < limit) + { + var value = Unsafe.ReadUnaligned(ref source.GetOffset(offset)); + NetworkOrderSerializer.WriteUInt64(ref destination.GetOffset(offset), value); + + offset += sizeof(ulong); + } + + return (int)offset; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CopyAndAlign32Bit(ReadOnlySpan input, Span output, int numberOfItems) + { + ref var destination = ref MemoryMarshal.GetReference(output); + ref var source = ref MemoryMarshal.GetReference(input); + + var limit = (uint) numberOfItems * sizeof(uint); + + var offset = 0u; + while (offset < limit) + { + var value = Unsafe.ReadUnaligned(ref source.GetOffset(offset)); + NetworkOrderSerializer.WriteUInt32(ref destination.GetOffset(offset), value); + + offset += sizeof(uint); + } + + return (int)offset; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CopyAndAlign16Bit(ReadOnlySpan input, Span output, int numberOfItems) + { + ref var destination = ref MemoryMarshal.GetReference(output); + ref var source = ref MemoryMarshal.GetReference(input); + + var limit = (uint) numberOfItems * sizeof(ushort); + + var offset = 0u; + while (offset < limit) + { + var value = Unsafe.ReadUnaligned(ref source.GetOffset(offset)); + NetworkOrderSerializer.WriteUInt16(ref destination.GetOffset(offset), value); + + offset += sizeof(ushort); + } + + return (int)offset; + } +} \ No newline at end of file diff --git a/Sally7/ValueConversion/ConversionHelper.cs b/Sally7/ValueConversion/ConversionHelper.cs index 804e2d1..e59f788 100644 --- a/Sally7/ValueConversion/ConversionHelper.cs +++ b/Sally7/ValueConversion/ConversionHelper.cs @@ -6,11 +6,11 @@ namespace Sally7.ValueConversion { internal static class ConversionHelper { - private static readonly MethodInfo sizeOfMethod = typeof(Unsafe).GetMethod(nameof(Unsafe.SizeOf))!; + private static readonly MethodInfo SizeOfMethod = typeof(Unsafe).GetMethod(nameof(Unsafe.SizeOf))!; public static int SizeOf(Type type) { - return (int) sizeOfMethod.MakeGenericMethod(type).Invoke(null, Array.Empty())!; + return (int) SizeOfMethod.MakeGenericMethod(type).Invoke(null, Array.Empty())!; } public static int GetElementSize() diff --git a/Sally7/ValueConversion/ConverterFactory.cs b/Sally7/ValueConversion/ConverterFactory.cs index 489100d..372f51c 100644 --- a/Sally7/ValueConversion/ConverterFactory.cs +++ b/Sally7/ValueConversion/ConverterFactory.cs @@ -9,10 +9,10 @@ namespace Sally7.ValueConversion internal static class ConverterFactory { - public static ConvertFromS7 GetFromPlcConverter() => - Unsafe.As>(FromS7Conversions.GetConverter())!; + public static ConvertFromS7 GetFromPlcConverter(int length) => + Unsafe.As>(FromS7Conversions.GetConverter(length))!; - public static ConvertToS7 GetToPlcConverter() => - Unsafe.As>(ToS7Conversions.GetConverter())!; + public static ConvertToS7 GetToPlcConverter(int length) => + Unsafe.As>(ToS7Conversions.GetConverter(length))!; } } diff --git a/Sally7/ValueConversion/FromS7Conversions.cs b/Sally7/ValueConversion/FromS7Conversions.cs index 455cffe..6102761 100644 --- a/Sally7/ValueConversion/FromS7Conversions.cs +++ b/Sally7/ValueConversion/FromS7Conversions.cs @@ -7,7 +7,7 @@ namespace Sally7.ValueConversion { internal static class FromS7Conversions { - public static Delegate GetConverter() + public static Delegate GetConverter(int length) { if (typeof(TValue).IsPrimitive || typeof(TValue).IsEnum) { @@ -53,13 +53,8 @@ private static void ConvertToLong(ref long value, ReadOnlySpan input, int private static void ConvertToLongArray(ref long[]? value, ReadOnlySpan input, int length) { value ??= new long[length]; - int i = 0; - while (!input.IsEmpty) - { - ConvertToLong(ref value[i++], input, 1); - input = input.Slice(sizeof(long)); - } + BufferHelper.CopyAndAlign64Bit(input, Unsafe.As(ref value), length); } private static void ConvertToInt(ref int value, ReadOnlySpan input, int length) @@ -68,13 +63,8 @@ private static void ConvertToInt(ref int value, ReadOnlySpan input, int le private static void ConvertToIntArray(ref int[]? value, ReadOnlySpan input, int length) { value ??= new int[length]; - int i = 0; - while (!input.IsEmpty) - { - ConvertToInt(ref value[i++], input, 1); - input = input.Slice(sizeof(int)); - } + BufferHelper.CopyAndAlign32Bit(input, Unsafe.As(ref value), length); } private static void ConvertToShort(ref short value, ReadOnlySpan input, int length) @@ -83,13 +73,8 @@ private static void ConvertToShort(ref short value, ReadOnlySpan input, in private static void ConvertToShortArray(ref short[]? value, ReadOnlySpan input, int length) { value ??= new short[length]; - int i = 0; - while (!input.IsEmpty) - { - ConvertToShort(ref value[i++], input, 1); - input = input.Slice(sizeof(short)); - } + BufferHelper.CopyAndAlign16Bit(input, Unsafe.As(ref value), length); } private static void ConvertToByte(ref byte value, ReadOnlySpan input, int length) diff --git a/Sally7/ValueConversion/ToS7Conversions.cs b/Sally7/ValueConversion/ToS7Conversions.cs index 4aac8fe..7d206f7 100644 --- a/Sally7/ValueConversion/ToS7Conversions.cs +++ b/Sally7/ValueConversion/ToS7Conversions.cs @@ -2,13 +2,15 @@ using System.Buffers.Binary; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; +using Sally7.Internal; namespace Sally7.ValueConversion { internal static class ToS7Conversions { - public static Delegate GetConverter() + public static Delegate GetConverter(int length) { if (typeof(TValue).IsPrimitive || typeof(TValue).IsEnum) { @@ -59,10 +61,7 @@ private static int ConvertFromLongArray(in long[]? value, int length, Span { if (value == null) throw new ArgumentNullException(nameof(value), "Value can't be null."); - for (var i = 0; i < value.Length; i++) - ConvertFromLong(value[i], 1, output.Slice(i * sizeof(long))); - - return value.Length * sizeof(long); + return BufferHelper.CopyAndAlign64Bit(Unsafe.As(ref Unsafe.AsRef(value)), output, length); } private static int ConvertFromInt(in int value, int length, Span output) @@ -76,10 +75,7 @@ private static int ConvertFromIntArray(in int[]? value, int length, Span o { if (value == null) throw new ArgumentNullException(nameof(value), "Value can't be null."); - for (var i = 0; i < value.Length; i++) - ConvertFromInt(value[i], 1, output.Slice(i * sizeof(int))); - - return value.Length * sizeof(int); + return BufferHelper.CopyAndAlign32Bit(Unsafe.As(ref Unsafe.AsRef(value)), output, length); } private static int ConvertFromShort(in short value, int length, Span output) @@ -93,10 +89,7 @@ private static int ConvertFromShortArray(in short[]? value, int length, Span(ref Unsafe.AsRef(value)), output, length); } private static int ConvertFromByte(in byte value, int length, Span output) @@ -112,7 +105,7 @@ private static int ConvertFromByteArray(in byte[]? value, int length, Span value.AsSpan().CopyTo(output); - return value.Length; + return length; } private static int ConvertFromBoolArray(in bool[]? value, int length, Span output)