Add MessagePack.Experimental package which includes SIMD(Single Instruction Multiple Data) accelerated primitive array formatters. (#988)

* Add Support for .NET Core 3.1

* [update]MessagePackWriter.Write(string) uses AVX2

* Add netcoreapp3.1 to test target frameworks

* Update target framework to netcoreapp3.1

* Add Benchmark project to test hardware intrinsics

* Change the module and assembly name

* This is too slow.
Need more tuning.

* Speed up but lose in performance

* Rollback

* Faster sbyte[] Serialization

* Add Document Comment

* Add Write(sbyte[]) to public APIs

* Modify not using Generic Unmanaged Struct Pointer

* Remove API MessagePackWriter.Write(sbyte[])

* Fix naming rule

* Make short[] serialization faster

* Remove empty line

* Revert changes in MessagePackWriter.cs

* Add Hardware Counters

* Add Alignment of 32

* Add new test for int[]

* Add Formatter T4 Template

* Sse41 is faster

* short[] process 4 short values

* Down the API Level from AVX2 to SSE4.2

* Add T4 Template Comment

* Add bool[] and float[] formatters

* Make field property

* Split Elements by blank line.

* Remove trailing white space

* Add comment for bool[] Serialization

* Unity does not allow switch expression.

* fix typo

* Move to MessagePack.Experimental

* Split file not to contain 2 namespaces

* Update benchmark competitor version from 2.1.152 to 2.1.165

* Fix: destination span length is larger than required length.

* Add double[] formatter.

* Rename variable and add test

* Add Comment to float/double[] formatters.

* Revert changes unrelated to perf work

Also:
* deleted `IntegerArrayFormatterHelper.cs` which the PR had added but seems to not use.
* replaced MessagePack_2_1_165.dll with the one from the nuget package by that version. The one placed here previously was slightly different and I don't know why, but using the official build seems prudent.

* Re-change the assembly/module name of MessagePack_2_1_165.dll

* Remove namespace of "Experimental"

* Disable warning CS0436 "Same type name in the primary package"

* Revert "Re-change the assembly/module name of MessagePack_2_1_165.dll"

This reverts commit dad1c6fa55a1e9498eb4e883ccfab377364cdbac.

Co-authored-by: Andrew Arnott <andrewarnott@gmail.com>
This commit is contained in:
pCYSl5EDgo 2020-09-14 21:38:01 +09:00 коммит произвёл GitHub
Родитель 40b47166fd
Коммит bf2ea7adb5
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
22 изменённых файлов: 3243 добавлений и 230 удалений

Просмотреть файл

@ -80,7 +80,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.MSBuild.Tasks",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePackAnalyzer.Tests", "tests\MessagePackAnalyzer.Tests\MessagePackAnalyzer.Tests.csproj", "{7E5FB4B9-A0F5-4B10-A1F3-03AC0BC8265A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessagePack.Generator.Tests", "tests\MessagePack.Generator.Tests\MessagePack.Generator.Tests.csproj", "{6AC51E68-4681-463A-B4B6-BD53517244B2}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.Generator.Tests", "tests\MessagePack.Generator.Tests\MessagePack.Generator.Tests.csproj", "{6AC51E68-4681-463A-B4B6-BD53517244B2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HardwareIntrinsicsBenchmark", "benchmark\HardwareIntrinsicsBenchmark\HardwareIntrinsicsBenchmark.csproj", "{4C9BB260-62D8-49CD-9F9C-9AA6A8BFC637}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.Experimental", "src\MessagePack.Experimental\MessagePack.Experimental.csproj", "{AC2503A7-736D-4AE6-9355-CF35D9DF6139}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessagePack.Experimental.Tests", "tests\MessagePack.Experimental.Tests\MessagePack.Experimental.Tests.csproj", "{8AB40D1C-1134-4D77-B39A-19AEDC729450}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -280,6 +286,30 @@ Global
{6AC51E68-4681-463A-B4B6-BD53517244B2}.Release|Any CPU.Build.0 = Release|Any CPU
{6AC51E68-4681-463A-B4B6-BD53517244B2}.Release|NoVSIX.ActiveCfg = Release|Any CPU
{6AC51E68-4681-463A-B4B6-BD53517244B2}.Release|NoVSIX.Build.0 = Release|Any CPU
{4C9BB260-62D8-49CD-9F9C-9AA6A8BFC637}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4C9BB260-62D8-49CD-9F9C-9AA6A8BFC637}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4C9BB260-62D8-49CD-9F9C-9AA6A8BFC637}.Debug|NoVSIX.ActiveCfg = Debug|Any CPU
{4C9BB260-62D8-49CD-9F9C-9AA6A8BFC637}.Debug|NoVSIX.Build.0 = Debug|Any CPU
{4C9BB260-62D8-49CD-9F9C-9AA6A8BFC637}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4C9BB260-62D8-49CD-9F9C-9AA6A8BFC637}.Release|Any CPU.Build.0 = Release|Any CPU
{4C9BB260-62D8-49CD-9F9C-9AA6A8BFC637}.Release|NoVSIX.ActiveCfg = Release|Any CPU
{4C9BB260-62D8-49CD-9F9C-9AA6A8BFC637}.Release|NoVSIX.Build.0 = Release|Any CPU
{AC2503A7-736D-4AE6-9355-CF35D9DF6139}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AC2503A7-736D-4AE6-9355-CF35D9DF6139}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AC2503A7-736D-4AE6-9355-CF35D9DF6139}.Debug|NoVSIX.ActiveCfg = Debug|Any CPU
{AC2503A7-736D-4AE6-9355-CF35D9DF6139}.Debug|NoVSIX.Build.0 = Debug|Any CPU
{AC2503A7-736D-4AE6-9355-CF35D9DF6139}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AC2503A7-736D-4AE6-9355-CF35D9DF6139}.Release|Any CPU.Build.0 = Release|Any CPU
{AC2503A7-736D-4AE6-9355-CF35D9DF6139}.Release|NoVSIX.ActiveCfg = Release|Any CPU
{AC2503A7-736D-4AE6-9355-CF35D9DF6139}.Release|NoVSIX.Build.0 = Release|Any CPU
{8AB40D1C-1134-4D77-B39A-19AEDC729450}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8AB40D1C-1134-4D77-B39A-19AEDC729450}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8AB40D1C-1134-4D77-B39A-19AEDC729450}.Debug|NoVSIX.ActiveCfg = Debug|Any CPU
{8AB40D1C-1134-4D77-B39A-19AEDC729450}.Debug|NoVSIX.Build.0 = Debug|Any CPU
{8AB40D1C-1134-4D77-B39A-19AEDC729450}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8AB40D1C-1134-4D77-B39A-19AEDC729450}.Release|Any CPU.Build.0 = Release|Any CPU
{8AB40D1C-1134-4D77-B39A-19AEDC729450}.Release|NoVSIX.ActiveCfg = Release|Any CPU
{8AB40D1C-1134-4D77-B39A-19AEDC729450}.Release|NoVSIX.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -309,6 +339,9 @@ Global
{8DB135F5-A6FE-44E4-9853-7B48ED21F21B} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
{7E5FB4B9-A0F5-4B10-A1F3-03AC0BC8265A} = {19FE674A-AC94-4E7E-B24C-2285D1D04CDE}
{6AC51E68-4681-463A-B4B6-BD53517244B2} = {19FE674A-AC94-4E7E-B24C-2285D1D04CDE}
{4C9BB260-62D8-49CD-9F9C-9AA6A8BFC637} = {51A614B0-E583-4DD2-AC7D-6A65634582E0}
{AC2503A7-736D-4AE6-9355-CF35D9DF6139} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
{8AB40D1C-1134-4D77-B39A-19AEDC729450} = {19FE674A-AC94-4E7E-B24C-2285D1D04CDE}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B3911209-2DBF-47F8-98F6-BBC0EDFE63DE}

Просмотреть файл

@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>HardwareIntrinsicsBenchmark</AssemblyName>
<RootNamespace>Benchmark</RootNamespace>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.12.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\MessagePack.Experimental\MessagePack.Experimental.csproj">
<Aliases>newmsgpack</Aliases>
</ProjectReference>
<ProjectReference Include="..\..\src\MessagePack.Annotations\MessagePack.Annotations.csproj">
<Aliases>newmsgpack</Aliases>
</ProjectReference>
<ProjectReference Include="..\..\src\MessagePack\MessagePack.csproj">
<Aliases>newmsgpack</Aliases>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Reference Include="MessagePack_2_1_165">
<HintPath>MessagePack_2_1_165.dll</HintPath>
<Aliases>oldmsgpack</Aliases>
<Private>true</Private>
<SpecificVersion>false</SpecificVersion>
</Reference>
</ItemGroup>
</Project>

Двоичные данные
benchmark/HardwareIntrinsicsBenchmark/MessagePack_2_1_165.dll Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,21 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Benchmark;
using BenchmarkDotNet.Running;
namespace HardwareIntrinsicsBenchmark
{
internal class Program
{
private static void Main()
{
BenchmarkRunner.Run<BooleanArrayBenchmarkMessagePackNoSingleInstructionMultipleDataVsMessagePackSingleInstructionMultipleData>();
BenchmarkRunner.Run<Int8ArrayBenchmarkMessagePackNoSingleInstructionMultipleDataVsMessagePackSingleInstructionMultipleData>();
BenchmarkRunner.Run<Int16ArrayBenchmarkMessagePackNoSingleInstructionMultipleDataVsMessagePackSingleInstructionMultipleData>();
BenchmarkRunner.Run<Int32ArrayBenchmarkMessagePackNoSingleInstructionMultipleDataVsMessagePackSingleInstructionMultipleData>();
BenchmarkRunner.Run<SingleArrayBenchmarkMessagePackNoSingleInstructionMultipleDataVsMessagePackSingleInstructionMultipleData>();
BenchmarkRunner.Run<DoubleArrayBenchmarkMessagePackNoSingleInstructionMultipleDataVsMessagePackSingleInstructionMultipleData>();
}
}
}

Просмотреть файл

@ -0,0 +1,391 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
extern alias oldmsgpack;
extern alias newmsgpack;
using System;
using System.Linq;
using System.Runtime.InteropServices;
using BenchmarkDotNet.Attributes;
#pragma warning disable SA1649 // File name should match first type name
namespace Benchmark
{
[ShortRunJob]
public class BooleanArrayBenchmarkMessagePackNoSingleInstructionMultipleDataVsMessagePackSingleInstructionMultipleData
{
[Params(64, 1024, 16 * 1024 * 1024)]
public int Size { get; set; }
private bool[] input;
private byte[] inputSerialized;
private bool[] inputTrue;
private bool[] inputFalse;
private newmsgpack::MessagePack.MessagePackSerializerOptions options;
[GlobalSetup]
public void SetUp()
{
var resolver = newmsgpack::MessagePack.Resolvers.CompositeResolver.Create(newmsgpack::MessagePack.Resolvers.PrimitiveArrayResolver.Instance, newmsgpack::MessagePack.Resolvers.StandardResolver.Instance);
options = newmsgpack::MessagePack.MessagePackSerializerOptions.Standard.WithResolver(resolver);
inputFalse = new bool[Size];
inputTrue = new bool[Size];
input = new bool[Size];
var r = new Random();
for (var i = 0; i < inputTrue.Length; i++)
{
inputTrue[i] = true;
input[i] = r.Next(0, 2) == 0;
}
inputSerialized = newmsgpack::MessagePack.MessagePackSerializer.Serialize(input, options);
if (!oldmsgpack::MessagePack.MessagePackSerializer.Serialize(input).SequenceEqual(inputSerialized))
{
throw new InvalidProgramException();
}
}
[Benchmark]
public byte[] SerializeSingleInstructionMultipleData()
{
return newmsgpack::MessagePack.MessagePackSerializer.Serialize(input, options);
}
[Benchmark]
public byte[] SerializeNoSingleInstructionMultipleData()
{
return oldmsgpack::MessagePack.MessagePackSerializer.Serialize(input);
}
[Benchmark]
public bool[] DeSerializeSingleInstructionMultipleData()
{
return newmsgpack::MessagePack.MessagePackSerializer.Deserialize<bool[]>(inputSerialized, options);
}
[Benchmark]
public bool[] DeserializeNoSingleInstructionMultipleData()
{
return oldmsgpack::MessagePack.MessagePackSerializer.Deserialize<bool[]>(inputSerialized);
}
[Benchmark]
public byte[] SerializeSingleInstructionMultipleDataFalse()
{
return newmsgpack::MessagePack.MessagePackSerializer.Serialize(inputFalse, options);
}
[Benchmark]
public byte[] SerializeNoSingleInstructionMultipleDataFalse()
{
return oldmsgpack::MessagePack.MessagePackSerializer.Serialize(inputFalse);
}
[Benchmark]
public byte[] SerializeSingleInstructionMultipleDataTrue()
{
return newmsgpack::MessagePack.MessagePackSerializer.Serialize(inputTrue, options);
}
[Benchmark]
public byte[] SerializeNoSingleInstructionMultipleDataTrue()
{
return oldmsgpack::MessagePack.MessagePackSerializer.Serialize(inputTrue);
}
}
[ShortRunJob]
public class Int8ArrayBenchmarkMessagePackNoSingleInstructionMultipleDataVsMessagePackSingleInstructionMultipleData
{
[Params(64, 1024, 16 * 1024 * 1024)]
public int Size { get; set; }
private sbyte[] input;
private sbyte[] inputM32;
private sbyte[] inputM33;
private sbyte[] zero;
private newmsgpack::MessagePack.MessagePackSerializerOptions options;
[GlobalSetup]
public void SetUp()
{
var resolver = newmsgpack::MessagePack.Resolvers.CompositeResolver.Create(newmsgpack::MessagePack.Resolvers.PrimitiveArrayResolver.Instance, newmsgpack::MessagePack.Resolvers.StandardResolver.Instance);
options = newmsgpack::MessagePack.MessagePackSerializerOptions.Standard.WithResolver(resolver);
zero = new sbyte[Size];
inputM33 = new sbyte[Size];
inputM32 = new sbyte[Size];
input = new sbyte[Size];
var r = new Random();
r.NextBytes(MemoryMarshal.AsBytes(input.AsSpan()));
for (var i = 0; i < inputM32.Length; i++)
{
inputM32[i] = -32;
inputM33[i] = -33;
}
if (!oldmsgpack::MessagePack.MessagePackSerializer.Serialize(input).SequenceEqual(newmsgpack::MessagePack.MessagePackSerializer.Serialize(input, options)))
{
throw new InvalidProgramException();
}
}
[Benchmark]
public byte[] SerializeSingleInstructionMultipleData()
{
return newmsgpack::MessagePack.MessagePackSerializer.Serialize(input, options);
}
[Benchmark]
public byte[] SerializeNoSingleInstructionMultipleData()
{
return oldmsgpack::MessagePack.MessagePackSerializer.Serialize(input);
}
[Benchmark]
public byte[] SerializeSingleInstructionMultipleDataZero()
{
return newmsgpack::MessagePack.MessagePackSerializer.Serialize(zero, options);
}
[Benchmark]
public byte[] SerializeNoSingleInstructionMultipleDataZero()
{
return oldmsgpack::MessagePack.MessagePackSerializer.Serialize(zero);
}
[Benchmark]
public byte[] SerializeSingleInstructionMultipleDataM32()
{
return newmsgpack::MessagePack.MessagePackSerializer.Serialize(inputM32, options);
}
[Benchmark]
public byte[] SerializeNoSingleInstructionMultipleDataM32()
{
return oldmsgpack::MessagePack.MessagePackSerializer.Serialize(inputM32);
}
[Benchmark]
public byte[] SerializeSingleInstructionMultipleDataM33()
{
return newmsgpack::MessagePack.MessagePackSerializer.Serialize(inputM33, options);
}
[Benchmark]
public byte[] SerializeNoSingleInstructionMultipleDataM33()
{
return oldmsgpack::MessagePack.MessagePackSerializer.Serialize(inputM33);
}
}
[ShortRunJob]
public class Int16ArrayBenchmarkMessagePackNoSingleInstructionMultipleDataVsMessagePackSingleInstructionMultipleData
{
[Params(16, 1024, 16 * 1024 * 1024)]
public int Size { get; set; }
private newmsgpack::MessagePack.MessagePackSerializerOptions options;
private short[] input;
private short[] zero;
[GlobalSetup]
public void SetUp()
{
var resolver = newmsgpack::MessagePack.Resolvers.CompositeResolver.Create(newmsgpack::MessagePack.Resolvers.PrimitiveArrayResolver.Instance, newmsgpack::MessagePack.Resolvers.StandardResolver.Instance);
options = newmsgpack::MessagePack.MessagePackSerializerOptions.Standard.WithResolver(resolver);
input = new short[Size];
zero = new short[Size];
var r = new Random();
r.NextBytes(MemoryMarshal.AsBytes(input.AsSpan()));
if (!oldmsgpack::MessagePack.MessagePackSerializer.Serialize(input).SequenceEqual(newmsgpack::MessagePack.MessagePackSerializer.Serialize(input, options)))
{
throw new InvalidProgramException();
}
}
[Benchmark]
public byte[] SerializeSingleInstructionMultipleData()
{
return newmsgpack::MessagePack.MessagePackSerializer.Serialize(input, options);
}
[Benchmark]
public byte[] SerializeNoSingleInstructionMultipleData()
{
return oldmsgpack::MessagePack.MessagePackSerializer.Serialize(input);
}
[Benchmark]
public byte[] SerializeSingleInstructionMultipleDataZero()
{
return newmsgpack::MessagePack.MessagePackSerializer.Serialize(zero, options);
}
[Benchmark]
public byte[] SerializeNoSingleInstructionMultipleDataZero()
{
return oldmsgpack::MessagePack.MessagePackSerializer.Serialize(zero);
}
}
[ShortRunJob]
public class Int32ArrayBenchmarkMessagePackNoSingleInstructionMultipleDataVsMessagePackSingleInstructionMultipleData
{
[Params(8, 1024, 16 * 1024 * 1024)]
public int Size { get; set; }
private newmsgpack::MessagePack.MessagePackSerializerOptions options;
private int[] input;
private int[] zero;
private int[] inputShortMin;
[GlobalSetup]
public void SetUp()
{
var resolver = newmsgpack::MessagePack.Resolvers.CompositeResolver.Create(newmsgpack::MessagePack.Resolvers.PrimitiveArrayResolver.Instance, newmsgpack::MessagePack.Resolvers.StandardResolver.Instance);
options = newmsgpack::MessagePack.MessagePackSerializerOptions.Standard.WithResolver(resolver);
input = new int[Size];
zero = new int[Size];
inputShortMin = new int[Size];
var r = new Random();
r.NextBytes(MemoryMarshal.AsBytes(input.AsSpan()));
for (var i = 0; i < inputShortMin.Length; i++)
{
inputShortMin[i] = short.MinValue;
}
if (!oldmsgpack::MessagePack.MessagePackSerializer.Serialize(input).SequenceEqual(newmsgpack::MessagePack.MessagePackSerializer.Serialize(input, options)))
{
throw new InvalidProgramException();
}
}
[Benchmark]
public byte[] SerializeSingleInstructionMultipleData()
{
return newmsgpack::MessagePack.MessagePackSerializer.Serialize(input, options);
}
[Benchmark]
public byte[] SerializeNoSingleInstructionMultipleData()
{
return oldmsgpack::MessagePack.MessagePackSerializer.Serialize(input);
}
[Benchmark]
public byte[] SerializeSingleInstructionMultipleDataZero()
{
return newmsgpack::MessagePack.MessagePackSerializer.Serialize(zero, options);
}
[Benchmark]
public byte[] SerializeNoSingleInstructionMultipleDataZero()
{
return oldmsgpack::MessagePack.MessagePackSerializer.Serialize(zero);
}
[Benchmark]
public byte[] SerializeSingleInstructionMultipleDataShortMin()
{
return newmsgpack::MessagePack.MessagePackSerializer.Serialize(inputShortMin, options);
}
[Benchmark]
public byte[] SerializeNoSingleInstructionMultipleDataShortMin()
{
return oldmsgpack::MessagePack.MessagePackSerializer.Serialize(inputShortMin);
}
}
[ShortRunJob]
public class SingleArrayBenchmarkMessagePackNoSingleInstructionMultipleDataVsMessagePackSingleInstructionMultipleData
{
[Params(64, 1024, 16 * 1024 * 1024)]
public int Size { get; set; }
private newmsgpack::MessagePack.MessagePackSerializerOptions options;
private float[] input;
[GlobalSetup]
public void SetUp()
{
var resolver = newmsgpack::MessagePack.Resolvers.CompositeResolver.Create(newmsgpack::MessagePack.Resolvers.PrimitiveArrayResolver.Instance, newmsgpack::MessagePack.Resolvers.StandardResolver.Instance);
options = newmsgpack::MessagePack.MessagePackSerializerOptions.Standard.WithResolver(resolver);
input = new float[Size];
var r = new Random();
for (var i = 0; i < input.Length; i++)
{
input[i] = (float)r.NextDouble();
}
if (!oldmsgpack::MessagePack.MessagePackSerializer.Serialize(input).SequenceEqual(newmsgpack::MessagePack.MessagePackSerializer.Serialize(input, options)))
{
throw new InvalidProgramException();
}
}
[Benchmark]
public byte[] SerializeSingleInstructionMultipleData()
{
return newmsgpack::MessagePack.MessagePackSerializer.Serialize(input, options);
}
[Benchmark]
public byte[] SerializeNoSingleInstructionMultipleData()
{
return oldmsgpack::MessagePack.MessagePackSerializer.Serialize(input);
}
}
[ShortRunJob]
public class DoubleArrayBenchmarkMessagePackNoSingleInstructionMultipleDataVsMessagePackSingleInstructionMultipleData
{
[Params(64, 1024, 16 * 1024 * 1024)]
public int Size { get; set; }
private newmsgpack::MessagePack.MessagePackSerializerOptions options;
private double[] input;
[GlobalSetup]
public void SetUp()
{
var resolver = newmsgpack::MessagePack.Resolvers.CompositeResolver.Create(newmsgpack::MessagePack.Resolvers.PrimitiveArrayResolver.Instance, newmsgpack::MessagePack.Resolvers.StandardResolver.Instance);
options = newmsgpack::MessagePack.MessagePackSerializerOptions.Standard.WithResolver(resolver);
input = new double[Size];
var r = new Random();
for (var i = 0; i < input.Length; i++)
{
input[i] = r.NextDouble();
}
if (!oldmsgpack::MessagePack.MessagePackSerializer.Serialize(input).SequenceEqual(newmsgpack::MessagePack.MessagePackSerializer.Serialize(input, options)))
{
throw new InvalidProgramException();
}
}
[Benchmark]
public byte[] SerializeSingleInstructionMultipleData()
{
return newmsgpack::MessagePack.MessagePackSerializer.Serialize(input, options);
}
[Benchmark]
public byte[] SerializeNoSingleInstructionMultipleData()
{
return oldmsgpack::MessagePack.MessagePackSerializer.Serialize(input);
}
}
}

Просмотреть файл

@ -27,7 +27,7 @@ namespace Benchmark
Job baseConfig = Job.ShortRun.WithIterationCount(1).WithWarmupCount(1);
// Add(baseConfig.With(Runtime.Clr).With(Jit.RyuJit).With(Platform.X64));
this.Add(baseConfig.With(CoreRuntime.Core30).With(Jit.RyuJit).With(Platform.X64));
this.Add(baseConfig.With(CoreRuntime.Core31).With(Jit.RyuJit).With(Platform.X64));
this.Add(MarkdownExporter.GitHub);
this.Add(CsvExporter.Default);

Просмотреть файл

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>SerializerBenchmark</AssemblyName>
<RootNamespace>Benchmark</RootNamespace>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -0,0 +1,483 @@
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#const string Zero = " 0,";
const string I8 = " MessagePackCode.Int8,";
const string I16 = " MessagePackCode.Int16,";
const string I32 = " MessagePackCode.Int32,";
const string U8 = " MessagePackCode.UInt8,";
const string U16 = " MessagePackCode.UInt16,";
const string U32 = " MessagePackCode.UInt32,";
var constantBuffer = new StringBuilder(16 * 32);
var lengthBuffer = new StringBuilder(256);
var accumulatedLength = 0;
int range;#>
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
/* THIS (.cs) FILE IS GENERATED. DO NOT CHANGE IT.
* CHANGE THE .tt FILE INSTEAD. */
/*
**** REFERENCE BLOG POST ****
About SIMD(Single Instruction Multiple Data). See: https://en.wikipedia.org/wiki/SIMD
In August 2020, .NET Core 3.1 provides the `Hardware Intrinsics` for x86/x64 processor.
Blog Post : https://devblogs.microsoft.com/dotnet/hardware-intrinsics-in-net-core/
API Document : https://docs.microsoft.com/ja-jp/dotnet/api/system.runtime.intrinsics.X86
Incoming .NET 5 will provide the Hardware Intrinsics API for Arm processor.
Blog Post : https://devblogs.microsoft.com/dotnet/announcing-net-5-preview-4-and-our-journey-to-one-net/#user-content-arm64-performance
API Document : https://docs.microsoft.com/ja-jp/dotnet/api/system.runtime.intrinsics.Arm
*/
/*
In x86/x64, there are several streaming SIMD extensions.
Official Site: https://software.intel.com/sites/landingpage/IntrinsicsGuide/
.NET Core 3.1 only provides up to AVX2 streaming SIMD extension.
AVX2 is supproted since Intel's Haswell micro architecture(2013/6).
Sse4.2 and PopCnt are supported since Intel's Nehalem micro architecture(2008/11).
Because of a problem in serializing MessagePack, I implemented all of them using SSE4.2 features instead of AVX2.
In MessagePack, the size of the resultant encoded numeric value varies according to the range to which the numeric value belongs.
| max excluded | min excluded | encoded byte length | MessagePack code |
| ---------------------------------- | -------------------------------------- | ------------------- | ---------------- |
| short.MinValue | int.MinValue - 1 | 5 | Int32 |
| MessagePackRange.MinFixNegativeInt | sbyte.MinValue - 1 | 2 | Int8 |
| sbyte.MinValue | short.MinValue - 1 | 3 | Int16 |
| sbyte.MaxValue + 1 | MessagePackRange.MinFixNegativeInt - 1 | 1 | FixNum |
| byte.MaxValue + 1 | sbyte.MaxValue | 2 | UInt8 |
| ushort.MaxValue + 1 | byte.MaxValue | 3 | UInt16 |
| uint.MaxValue + 1 | ushort.MaxValue | 5 | UInt32 |
We will insert appropriate MessagePackCode with appropriate reordering of the input byte sequence to correspond to the variable-length output results.
In SIMD programming circles, "reordering" is referred to as "shuffle" or "permute".
MessagePack serialization using SIMD consists of the following steps.
- Load the Input Values.
- Classify the Input Values.
- Calculate Total Output Byte Count.
- Get the destination span from writer with the Total Output Byte Count.
In subsequent steps, the Input Values are divided into halves and treated as the Input Values.
There are 16 input values in the sbyte type, 8 in the short type, and 4 in the int type.
- Shuffle the Input Values to make the Output Byte Sequence.
- Place the appropriate MessagePack Codes in the Output Byte Sequence according to the classification.
- Store the Output Byte Sequence in the destination span.
*/
/*
How to classify the input values?
Conditional branching is not favored in SIMD programming.
The technique of Shader programming is similar to that of SIMD.
I use only Sse2.CompareGreaterThan(left, right). See : https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmpgt_epi8&expand=915
public static System.Runtime.Intrinsics.Vector128<sbyte> CompareGreaterThan (System.Runtime.Intrinsics.Vector128<sbyte> left, System.Runtime.Intrinsics.Vector128<sbyte> right);
Pseudo Code:
System.Runtime.Intrinsics.Vector128<sbyte> CompareGreaterThan (System.Runtime.Intrinsics.Vector128<sbyte> left, System.Runtime.Intrinsics.Vector128<sbyte> right)
{
Vector128<sbyte> answer = default;
sbyte* leftPointer = (sbyte*)&left;
sbyte* rightPointer = (sbyte*)&right;
sbyte* answerPointer = (sbyte*)&answer;
for (int i = 0; i < 16; i++)
{
if (*leftPointer++ > *rightPointer++)
{
*answer++ = -1;
}
else
{
*answer++ = 0;
}
}
return answer;
}
If the condition is true, the corresponding bit intervals are all 1.
This state where all the bits are 1 is sometimes regarded as -1, and sometimes it is simply used as a bit mask.
*/
/*
What is the `shuffle`?
See : https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_shuffle_epi8&expand=5153
See : https://www.officedaytime.com/simd512e/simdimg/si.php?f=pshufb
public static System.Runtime.Intrinsics.Vector128<byte> Shuffle (System.Runtime.Intrinsics.Vector128<byte> value, System.Runtime.Intrinsics.Vector128<byte> mask);
`shuffle` packed 8-bit integers in value according to shuffle control mask in the corresponding 8-bit element of mask, and returns.
Example(pseudo code):
Vector128<byte> input = Vector128.Create(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10);
Vector128<byte> shuffle = Vector128.Create(0x80, 0, 15, 0x80, 2, 2, 3, 0x80, 0x80, 14, 13, 12, 0, 5, 1, 6);
Vector128<byte> result = Ssse3.Shuffle(input, shuffle);
Console.WriteLine(result.ToString());
// <0, 0x01, 0x10, 0, 0x03, 0x03, 0x04, 0, 0, 0x0F, 0x0E, 0x0D, 0x01, 0x06, 0x02, 0x07>
*/
using System;
#pragma warning disable SA1649 // File name should match first type name
#pragma warning disable CS0436 // The same name of the primary package
namespace MessagePack.Formatters
{
internal static class SingleInstructionMultipleDataPrimitiveArrayFormatterHelper
{
/*
Sse2.MaskMove(src, mask, dest);
Vector128<byte> is a byte array consists of 16 elements.
This function stores the byte element of src at the corresponding position of mask if it is greater than or equal to 0x80 and does nothing if it is less than that.
See : https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=maskmove&expand=3555
*/
/// <summary>
/// Gets Mask Vector Table.
/// MessagePack SIMD Serialization needs to store the output bytes to the destination memory.
/// <example>
/// <code>
/// /* Vector128&lt;byte&gt; vector128; int outputLength; byte* destination; */
/// var maskTable = SingleInstructionMultipleDataPrimitiveArrayFormatterHelper.StoreMaskTable;
/// fixed (byte* maskTablePointer = &amp;maskTable[0])
/// {
/// var mask = Sse2.LoadVector128(maskTablePointer + (outputLength &lt;&lt; 4));
/// Sse2.MaskMove(vector128, mask, destination);
/// }
/// </code>
/// </example>
/// </summary>
public static ReadOnlySpan<byte> StoreMaskTable => new byte[17 * 16] { <#
for (int count = 0; count <= 16; count++)
{
int index = 0;
for (; index < count; index++)
{
#>0x80, <#
}
for (; index < 16; index++)
{
#>0, <#
}
}
#>};
}
public sealed partial class SByteArrayFormatter
{
/*
1(2) : -32 > i >= short.MinValue
0(1) : sbyte.MaxValue >= i >= -32
2 ^ 8 == 256
Values of sbyte type can be classified into two types by MessagePack Code(FixNum and Int8).
If 16 Input Values are all in the range of FixNum, they can be just copied to the destination span.
If not, `shuffle` is needed.
Vector<byte> ShuffleAndMaskTable[256].
C# doesn't allow the above expression.
So ShuffleAndMaskTable is written as byte[4096].
Sse2.CompareGreaterThan(-32, i) can classify them.
Sse2.MoveMask gathers highest bits of Vector128<sbyte>.
In other words, this API compiles the comparison results of 16 sbyte numbers into 16 bits.
Lower 8bits represents the results of the first 8 sbyte values.
Total Output Byte Sequence Length can be calculated by Popcnt.PopCount static method.
See : https://docs.microsoft.com/en-us/cpp/intrinsics/popcnt16-popcnt-popcnt64?view=vs-2019
Since there is only one type of MessagePack code(==Int8) in the case of sbyte, I used Sse41.BlendVariable static method to synthesize the Output Byte Sequence.
Pseudo Code:
public static System.Runtime.Intrinsics.Vector128<sbyte> BlendVariable (System.Runtime.Intrinsics.Vector128<sbyte> left, System.Runtime.Intrinsics.Vector128<sbyte> right, System.Runtime.Intrinsics.Vector128<sbyte> mask)
{
Vector128<sbyte> answer = default;
sbyte* leftPointer = (sbyte*)&left;
sbyte* rightPointer = (sbyte*)&right;
sbyte* maskPointer = (sbyte*)&mask;
sbyte* answerPointer = (sbyte*)&answer;
for (int i = 0; i < 16; i++)
{
bool shouldSelectLeft = mask[i] >= 0;
if (shouldSelectLeft)
{
answer[i] = left[i];
}
else
{
answer[i] = right[i];
}
}
return answer;
}
*/
private static ReadOnlySpan<byte> ShuffleAndMaskTable => new byte[256 * 16] {<#
for (uint bitPattern = 0; bitPattern < 256; bitPattern++)
{
// Examples
// bitPattern == 0b00000000 then shuffleVector == <0, 1, 2, 3, 4, 5, 6, 7, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80>
// bitPattern == 0b10101011 then shuffleVector == <0x80, 0, 0x80, 1, 2, 0x80, 3, 4, 0x80, 5, 6, 0x80, 7, 0x80, 0x80, 0x80>
accumulatedLength = 0;
for (var j = 0; j < 8; j++)
{
var isFixNum = ((bitPattern >> j) & 1) == 0;
if (isFixNum)
{
#> <#=j#>,<#
accumulatedLength++;
}
else
{
#> 0x80, <#=j#>,<#
accumulatedLength += 2;
}
}
// fill zero
for (; accumulatedLength < 16; accumulatedLength++)
{
#> 0x80,<#
}
}
#> };
}
public sealed partial class Int16ArrayFormatter
{
/*
0(3) : sbyte.MinValue > i >= short.MinValue
-1(2) : MessagePackRange.MinFixNegativeInt > i > sbyte.MinValue - 1
-2(1) : sbyte.MaxValue + 1 > i > MessagePackRange.MinFixNegativeInt - 1
-3(2) : byte.MaxValue + 1 > i > sbyte.MaxValue
-4(3) : ushort.MaxValue >= i > byte.MaxValue
5 ^ 4 == 625
*/
<#
Action<int, int> EmbedInt16 = (int i, int baseIndex) =>
{
switch (i)
{
case 4:
accumulatedLength += 3;
constantBuffer.Append(U16).Append(Zero).Append(Zero);
#> 0x80, <#=baseIndex + 1#>, <#=baseIndex#>,<#
break;
case 3:
accumulatedLength += 2;
constantBuffer.Append(U8).Append(Zero);
#> 0x80, <#=baseIndex#>,<#
break;
case 2:
accumulatedLength++;
constantBuffer.Append(Zero);
#> <#=baseIndex#>,<#
break;
case 1:
accumulatedLength += 2;
constantBuffer.Append(I8).Append(Zero);
#> 0x80, <#=baseIndex#>,<#
break;
case 0:
accumulatedLength += 3;
constantBuffer.Append(I16).Append(Zero).Append(Zero);
#> 0x80, <#=baseIndex + 1#>, <#=baseIndex#>,<#
break;
}
};
#>
private const int CountTableOffset = 625 * 32;
private static ReadOnlySpan<byte> ShuffleAndMaskTable => new byte[625 * 36] {<#
lengthBuffer.Clear();
range = 5;
for (var i3 = 0; i3 < range; i3++)
{
for (var i2 = 0; i2 < range; i2++)
{
for (var i1 = 0; i1 < range; i1++)
{
for (var i0 = 0; i0 < range; i0++)
{
constantBuffer.Clear();
accumulatedLength = 0;
EmbedInt16(i0, 0);
EmbedInt16(i1, 2);
EmbedInt16(i2, 4);
EmbedInt16(i3, 6);
lengthBuffer.Append(' ').Append(accumulatedLength).Append(", 0, 0, 0");
if (i1 != range - 1 || i0 != range - 1 || i2 != range - 1 || i3 != range - 1)
{
lengthBuffer.Append(',');
}
for (; accumulatedLength < 16; accumulatedLength++)
{
#> 0x80,<#
constantBuffer.Append(Zero);
}
#><#=constantBuffer.ToString()#><#
}
}
}
}
#><#=lengthBuffer.ToString()#> };
}
public sealed partial class Int32ArrayFormatter
{
/*
0(5) : short.MinValue > i >= int.MinValue
-1(3) : sbyte.MinValue > i >= short.MinValue
-2(2) : -32 > i >= sbyte.MinValue
-3(1) : sbyte.MaxValue + 1 > i >= -32
-4(2) : byte.MaxValue + 1 > i > sbyte.MaxValue
-5(3) : ushort.MaxValue + 1 > i > byte.MaxValue
-6(5) : int.MaxValue >= i > ushort.MaxValue
7 ^ 2 == 49
*/
<#
Action<int, int> EmbedInt32 = (int i, int baseIndex) =>
{
switch (i)
{
case 6:
accumulatedLength += 5;
constantBuffer.Append(U32).Append(Zero).Append(Zero).Append(Zero).Append(Zero);
#> 0x80, <#=baseIndex+3#>, <#=baseIndex+2#>, <#=baseIndex+1#>, <#=baseIndex#>,<#
break;
case 5:
accumulatedLength += 3;
constantBuffer.Append(U16).Append(Zero).Append(Zero);
#> 0x80, <#=baseIndex+1#>, <#=baseIndex#>,<#
break;
case 4:
accumulatedLength += 2;
constantBuffer.Append(U8).Append(Zero);
#> 0x80, <#=baseIndex#>,<#
break;
case 3:
accumulatedLength++;
constantBuffer.Append(Zero);
#> <#=baseIndex#>,<#
break;
case 2:
accumulatedLength += 2;
constantBuffer.Append(I8).Append(Zero);
#> 0x80, <#=baseIndex#>,<#
break;
case 1:
accumulatedLength += 3;
constantBuffer.Append(I16).Append(Zero).Append(Zero);
#> 0x80, <#=baseIndex+1#>, <#=baseIndex#>,<#
break;
case 0:
accumulatedLength += 5;
constantBuffer.Append(I32).Append(Zero).Append(Zero).Append(Zero).Append(Zero);
#> 0x80, <#=baseIndex+3#>, <#=baseIndex+2#>, <#=baseIndex+1#>, <#=baseIndex#>,<#
break;
}
};
#>
private const int CountTableOffset = 49 * 32;
private static ReadOnlySpan<byte> ShuffleAndMaskTable => new byte[49 * 36] {<#
lengthBuffer.Clear();
range = 7;
for (var i1 = 0; i1 < range; i1++)
{
for (var i0 = 0; i0 < range; i0++)
{
constantBuffer.Clear();
accumulatedLength = 0;
EmbedInt32(i0, 0);
EmbedInt32(i1, 4);
lengthBuffer.Append(' ').Append(accumulatedLength).Append(", 0, 0, 0");
if (i1 != range - 1 || i0 != range - 1)
{
lengthBuffer.Append(',');
}
for (; accumulatedLength < 16; accumulatedLength++)
{
#> 0x80,<#
constantBuffer.Append(Zero);
}
#><#=constantBuffer.ToString()#><#
}
}
#><#=lengthBuffer.ToString()#> };
}
<#
Type[] types = new []
{
typeof(sbyte),
typeof(short),
typeof(int),
typeof(float),
typeof(double),
typeof(bool),
};
foreach (var type in types)
{
var name = type.Name;
var formatter = name + "ArrayFormatter";
#>
public sealed partial class <#= formatter #>
{
public static readonly <#= formatter #> Instance = new <#= formatter #>();
private <#= formatter #>()
{
}
<#
if (type != typeof(bool))
{
#>
public <#= name #>[]? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
if (reader.TryReadNil())
{
return default;
}
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<<#= name #>>();
}
var array = new <#= name #>[len];
for (var i = 0; i < array.Length; i++)
{
array[i] = reader.Read<#= name #>();
}
return array;
}
<#
}
#>
}
<#
}
#>
}

Просмотреть файл

@ -0,0 +1,765 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
#pragma warning disable SA1649 // File name should match first type name
namespace MessagePack.Formatters
{
public sealed partial class SByteArrayFormatter : IMessagePackFormatter<sbyte[]?>
{
public unsafe void Serialize(ref MessagePackWriter writer, sbyte[]? value, MessagePackSerializerOptions options)
{
if (value == null)
{
writer.WriteNil();
return;
}
var inputLength = value.Length;
writer.WriteArrayHeader(inputLength);
if (inputLength == 0)
{
return;
}
fixed (sbyte* pSource = &value[0])
{
var inputEnd = pSource + inputLength;
var inputIterator = pSource;
if (Popcnt.IsSupported)
{
const int ShiftCount = 4;
const int Stride = 1 << ShiftCount;
// We enter the SIMD mode when there are more than the Stride after alignment adjustment.
if (inputLength < Stride << 1)
{
goto ProcessEach;
}
{
// Make InputIterator Aligned
var offset = UnsafeMemoryAlignmentUtility.CalculateDifferenceAlign16(inputIterator);
inputLength -= offset;
var offsetEnd = inputIterator + offset;
while (inputIterator != offsetEnd)
{
writer.Write(*inputIterator++);
}
}
fixed (byte* tablePointer = &ShuffleAndMaskTable[0])
{
fixed (byte* maskTablePointer = &SingleInstructionMultipleDataPrimitiveArrayFormatterHelper.StoreMaskTable[0])
{
var vectorMinFixNegInt = Vector128.Create((sbyte)MessagePackRange.MinFixNegativeInt);
var vectorMessagePackCodeInt8 = Vector128.Create(MessagePackCode.Int8);
for (var vectorizedEnd = inputIterator + ((inputLength >> ShiftCount) << ShiftCount); inputIterator != vectorizedEnd; inputIterator += Stride)
{
var current = Sse2.LoadVector128(inputIterator);
var index = unchecked((uint)Sse2.MoveMask(Sse2.CompareGreaterThan(vectorMinFixNegInt, current)));
if (index == 0)
{
// When all 32 input values are in the FixNum range.
var span = writer.GetSpan(Stride);
Sse2.Store((sbyte*)Unsafe.AsPointer(ref span[0]), current);
writer.Advance(Stride);
continue;
}
unchecked
{
var index0 = (byte)index;
var index1 = (byte)(index >> 8);
var count0 = (int)(Popcnt.PopCount(index0) + 8);
var count1 = (int)(Popcnt.PopCount(index1) + 8);
var countTotal = count0 + count1;
var destination = writer.GetSpan(countTotal);
fixed (byte* pDestination = &destination[0])
{
var tempDestination = pDestination;
var shuffle0 = Sse2.LoadVector128(tablePointer + (index0 << 4));
var shuffled0 = Ssse3.Shuffle(current.AsByte(), shuffle0);
var answer0 = Sse41.BlendVariable(shuffled0, vectorMessagePackCodeInt8, shuffle0);
Sse2.MaskMove(answer0, Sse2.LoadVector128(maskTablePointer + (count0 << 4)), tempDestination);
tempDestination += count0;
var shuffle1 = Sse2.LoadVector128(tablePointer + (index1 << 4));
var shift1 = Sse2.ShiftRightLogical128BitLane(current.AsByte(), 8);
var shuffled1 = Ssse3.Shuffle(shift1, shuffle1);
var answer1 = Sse41.BlendVariable(shuffled1, vectorMessagePackCodeInt8, shuffle1);
Sse2.MaskMove(answer1, Sse2.LoadVector128(maskTablePointer + (count1 << 4)), tempDestination);
}
writer.Advance(countTotal);
}
}
}
}
}
ProcessEach:
while (inputIterator != inputEnd)
{
writer.Write(*inputIterator++);
}
}
}
}
public sealed partial class Int16ArrayFormatter : IMessagePackFormatter<short[]?>
{
public unsafe void Serialize(ref MessagePackWriter writer, short[]? value, MessagePackSerializerOptions options)
{
if (value == null)
{
writer.WriteNil();
return;
}
var inputLength = value.Length;
writer.WriteArrayHeader(inputLength);
if (inputLength == 0)
{
return;
}
fixed (short* pSource = &value[0])
{
var inputEnd = pSource + inputLength;
var inputIterator = pSource;
if (Sse41.IsSupported)
{
const int ShiftCount = 3;
const int Stride = 1 << ShiftCount;
if (inputLength < Stride << 1)
{
goto ProcessEach;
}
{
// Make InputIterator Aligned
var offset = UnsafeMemoryAlignmentUtility.CalculateDifferenceAlign16(inputIterator);
// When offset is times of 2, you can adjust memory address.
if ((offset & 1) == 0)
{
offset >>= 1;
inputLength -= offset;
var offsetEnd = inputIterator + offset;
while (inputIterator != offsetEnd)
{
writer.Write(*inputIterator++);
}
}
}
fixed (byte* tablePointer = &ShuffleAndMaskTable[0])
{
var countPointer = (int*)(tablePointer + CountTableOffset);
fixed (byte* maskTablePointer = &SingleInstructionMultipleDataPrimitiveArrayFormatterHelper.StoreMaskTable[0])
{
var vectorSByteMinNeg1 = Vector128.Create((short)(sbyte.MinValue - 1));
var vectorMinFixNeg1 = Vector128.Create((short)(MessagePackRange.MinFixNegativeInt - 1));
var vectorSByteMax = Vector128.Create((short)sbyte.MaxValue);
var vectorByteMaxValue = Vector128.Create((short)byte.MaxValue);
var vectorM1M5M25M125 = Vector128.Create(-1, -5, -25, -125, -1, -5, -25, -125);
var vector02468101214 = Vector128.Create(0, 2, 4, 6, 8, 10, 12, 14, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80);
var vectorLoopLength = (inputLength >> ShiftCount) << ShiftCount;
for (var vectorizedEnd = inputIterator + vectorLoopLength; inputIterator != vectorizedEnd; inputIterator += Stride)
{
var current = Sse2.LoadVector128(inputIterator);
var isGreaterThanMinFixNeg1 = Sse2.CompareGreaterThan(current, vectorMinFixNeg1);
var isGreaterThanSByteMax = Sse2.CompareGreaterThan(current, vectorSByteMax);
if (Sse2.MoveMask(Sse2.AndNot(isGreaterThanSByteMax, isGreaterThanMinFixNeg1).AsByte()) == 0xFFFF)
{
var span = writer.GetSpan(Stride);
var answer = Ssse3.Shuffle(current.AsByte(), vector02468101214).AsUInt64();
Unsafe.As<byte, ulong>(ref span[0]) = answer.GetElement(0);
writer.Advance(Stride);
continue;
}
var countVector = Sse2.Add(isGreaterThanSByteMax, isGreaterThanMinFixNeg1);
var isGreaterThanSByteMinNeg1 = Sse2.CompareGreaterThan(current, vectorSByteMinNeg1);
countVector = Sse2.Add(countVector, isGreaterThanSByteMinNeg1);
var isGreaterThanByteMax = Sse2.CompareGreaterThan(current, vectorByteMaxValue);
countVector = Sse2.Add(countVector, isGreaterThanByteMax);
var indexVector = Sse2.MultiplyAddAdjacent(countVector, vectorM1M5M25M125);
indexVector = Ssse3.HorizontalAdd(indexVector, indexVector);
var index0 = indexVector.GetElement(0);
var index1 = indexVector.GetElement(1);
var count0 = countPointer[index0];
var count1 = countPointer[index1];
var countTotal = count0 + count1;
var destination = writer.GetSpan(countTotal);
fixed (byte* pDestination = &destination[0])
{
var tmpDestination = pDestination;
var item0 = tablePointer + (index0 << 5);
var shuffle0 = Sse2.LoadVector128(item0);
var shuffled0 = Ssse3.Shuffle(current.AsByte(), shuffle0);
var constant0 = Sse2.LoadVector128(item0 + 16);
var answer0 = Sse2.Or(shuffled0, constant0);
Sse2.MaskMove(answer0, Sse2.LoadVector128(maskTablePointer + (count0 << 4)), tmpDestination);
tmpDestination += count0;
var item1 = tablePointer + (index1 << 5);
var shuffle1 = Sse2.LoadVector128(item1);
current = Sse2.ShiftRightLogical128BitLane(current, 8);
var shuffled1 = Ssse3.Shuffle(current.AsByte(), shuffle1);
var constant1 = Sse2.LoadVector128(item1 + 16);
var answer1 = Sse2.Or(shuffled1, constant1);
Sse2.MaskMove(answer1, Sse2.LoadVector128(maskTablePointer + (count1 << 4)), tmpDestination);
}
writer.Advance(countTotal);
}
}
}
}
ProcessEach:
while (inputIterator != inputEnd)
{
writer.Write(*inputIterator++);
}
}
}
}
public sealed partial class Int32ArrayFormatter : IMessagePackFormatter<int[]?>
{
public unsafe void Serialize(ref MessagePackWriter writer, int[]? value, MessagePackSerializerOptions options)
{
if (value == null)
{
writer.WriteNil();
return;
}
var inputLength = value.Length;
writer.WriteArrayHeader(inputLength);
if (inputLength == 0)
{
return;
}
fixed (int* pSource = &value[0])
{
var inputEnd = pSource + inputLength;
var inputIterator = pSource;
if (Sse41.IsSupported)
{
const int ShiftCount = 2;
const int Stride = 1 << ShiftCount;
if (inputLength < Stride << 1)
{
goto ProcessEach;
}
{
// Make InputIterator Aligned
var offset = UnsafeMemoryAlignmentUtility.CalculateDifferenceAlign16(inputIterator);
// When offset is times of 4, you can adjust memory address.
if ((offset & 3) == 0)
{
offset >>= 2;
inputLength -= offset;
var offsetEnd = inputIterator + offset;
while (inputIterator != offsetEnd)
{
writer.Write(*inputIterator++);
}
}
}
fixed (byte* tablePointer = &ShuffleAndMaskTable[0])
{
var countPointer = (int*)(tablePointer + CountTableOffset);
fixed (byte* maskTablePointer = &SingleInstructionMultipleDataPrimitiveArrayFormatterHelper.StoreMaskTable[0])
{
var vectorShortMinValueM1 = Vector128.Create(short.MinValue - 1);
var vectorSByteMinValueM1 = Vector128.Create(sbyte.MinValue - 1);
var vectorMinFixNegIntM1 = Vector128.Create(MessagePackRange.MinFixNegativeInt - 1);
var vectorSByteMaxValue = Vector128.Create((int)sbyte.MaxValue);
var vectorByteMaxValue = Vector128.Create((int)byte.MaxValue);
var vectorUShortMaxValue = Vector128.Create((int)ushort.MaxValue);
var vectorM1M7 = Vector128.Create(-1, -7, -1, -7);
var vectorIn1Range = Vector128.Create(0, 4, 8, 12, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80);
for (var vectorizedEnd = inputIterator + ((inputLength >> ShiftCount) << ShiftCount); inputIterator != vectorizedEnd; inputIterator += Stride)
{
var current = Sse2.LoadVector128(inputIterator);
var isGreaterThanMinFixNegIntM1 = Sse2.CompareGreaterThan(current, vectorMinFixNegIntM1);
var isGreaterThanSByteMaxValue = Sse2.CompareGreaterThan(current, vectorSByteMaxValue);
if (Sse2.MoveMask(Sse2.AndNot(isGreaterThanSByteMaxValue, isGreaterThanMinFixNegIntM1).AsByte()) == 0xFFFF)
{
var answer = Ssse3.Shuffle(current.AsByte(), vectorIn1Range).AsUInt32();
var span = writer.GetSpan(Stride);
Unsafe.As<byte, uint>(ref span[0]) = answer.GetElement(0);
writer.Advance(Stride);
continue;
}
var indexVector = Sse2.Add(isGreaterThanSByteMaxValue, isGreaterThanMinFixNegIntM1);
indexVector = Sse2.Add(indexVector, Sse2.CompareGreaterThan(current, vectorUShortMaxValue));
indexVector = Sse2.Add(indexVector, Sse2.CompareGreaterThan(current, vectorByteMaxValue));
indexVector = Sse2.Add(indexVector, Sse2.CompareGreaterThan(current, vectorShortMinValueM1));
indexVector = Sse2.Add(indexVector, Sse2.CompareGreaterThan(current, vectorSByteMinValueM1));
indexVector = Sse41.MultiplyLow(indexVector, vectorM1M7);
indexVector = Ssse3.HorizontalAdd(indexVector, indexVector);
var index0 = indexVector.GetElement(0);
var index1 = indexVector.GetElement(1);
var count0 = countPointer[index0];
var count1 = countPointer[index1];
var countTotal = count0 + count1;
var destination = writer.GetSpan(countTotal);
fixed (byte* pDestination = &destination[0])
{
var tmpDestination = pDestination;
var item0 = tablePointer + (index0 << 5);
var shuffle0 = Sse2.LoadVector128(item0);
var shuffled0 = Ssse3.Shuffle(current.AsByte(), shuffle0);
var constant0 = Sse2.LoadVector128(item0 + 16);
var answer0 = Sse2.Or(shuffled0, constant0);
Sse2.MaskMove(answer0, Sse2.LoadVector128(maskTablePointer + (count0 << 4)), pDestination);
tmpDestination += count0;
var shift1 = Sse2.ShiftRightLogical128BitLane(current, 8).AsByte();
var item1 = tablePointer + (index1 << 5);
var shuffle1 = Sse2.LoadVector128(item1);
var shuffled1 = Ssse3.Shuffle(shift1, shuffle1);
var constant1 = Sse2.LoadVector128(item1 + 16);
var answer1 = Sse2.Or(shuffled1, constant1);
Sse2.MaskMove(answer1, Sse2.LoadVector128(maskTablePointer + (count1 << 4)), tmpDestination);
}
writer.Advance(countTotal);
}
}
}
}
ProcessEach:
while (inputIterator != inputEnd)
{
writer.Write(*inputIterator++);
}
}
}
}
public sealed partial class SingleArrayFormatter : IMessagePackFormatter<float[]?>
{
public unsafe void Serialize(ref MessagePackWriter writer, float[]? value, MessagePackSerializerOptions options)
{
if (value == null)
{
writer.WriteNil();
return;
}
var inputLength = value.Length;
writer.WriteArrayHeader(inputLength);
if (inputLength == 0)
{
return;
}
// output byte[] length can be calculated from input float[] length.
var outputLength = inputLength * 5;
var destination = writer.GetSpan(outputLength);
fixed (byte* pDestination = &destination[0])
{
var outputIterator = pDestination;
fixed (float* pSource = &value[0])
{
var inputEnd = pSource + inputLength;
var inputIterator = (uint*)pSource;
if (Sse42.IsSupported)
{
if (inputLength < 6)
{
goto ProcessEach;
}
// Process 3 floats at once.
// From 12 bytes to 15 bytes.
var vectorConstant = Vector128.Create(MessagePackCode.Float32, 0, 0, 0, 0, MessagePackCode.Float32, 0, 0, 0, 0, MessagePackCode.Float32, 0, 0, 0, 0, 0);
var vectorShuffle = Vector128.Create(0x80, 3, 2, 1, 0, 0x80, 7, 6, 5, 4, 0x80, 11, 10, 9, 8, 0x80);
var vectorLoopLength = ((inputLength / 3) - 1) * 3;
for (var vectorizedEnd = inputIterator + vectorLoopLength; inputIterator != vectorizedEnd; inputIterator += 3, outputIterator += 15)
{
// new float[] { 1.0, -2.0, 3.5, } is byte[12] { 00, 00, 80, 3f, 00, 00, 00, c0, 00, 00, 60, 40 } in binary expression;
var current = Sse2.LoadVector128((byte*)inputIterator);
// Output binary should be byte[15] { ca, 3f, 80, 00, 00, ca, c0, 00, 00, 00, ca, 40, 60, 00, 00 };
Sse2.Store(outputIterator, Sse2.Or(Ssse3.Shuffle(current, vectorShuffle), vectorConstant));
}
}
ProcessEach:
while (inputIterator != inputEnd)
{
// Encode float as Big Endian
*outputIterator++ = MessagePackCode.Float32;
var current = *inputIterator++;
*outputIterator++ = (byte)(current >> 24);
*outputIterator++ = (byte)(current >> 16);
*outputIterator++ = (byte)(current >> 8);
*outputIterator++ = (byte)current;
}
}
}
writer.Advance(outputLength);
}
}
public sealed partial class DoubleArrayFormatter : IMessagePackFormatter<double[]?>
{
public unsafe void Serialize(ref MessagePackWriter writer, double[]? value, MessagePackSerializerOptions options)
{
if (value == null)
{
writer.WriteNil();
return;
}
var inputLength = value.Length;
writer.WriteArrayHeader(inputLength);
if (inputLength == 0)
{
return;
}
var outputLength = inputLength * 9;
var destination = writer.GetSpan(outputLength);
fixed (byte* pDestination = &destination[0])
{
var outputIterator = pDestination;
fixed (double* pSource = &value[0])
{
var inputEnd = pSource + inputLength;
var inputIterator = (ulong*)pSource;
if (Avx2.IsSupported)
{
const int ShiftCount = 2;
const int Stride = 1 << ShiftCount;
if (inputLength < Stride << 1)
{
goto ProcessEach;
}
var vectorShuffle = Vector256.Create((byte)7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8);
for (var vectorizedEnd = inputIterator + ((inputLength >> ShiftCount) << ShiftCount); inputIterator != vectorizedEnd; inputIterator += Stride)
{
// Fetch 4 doubles.
var current = Avx.LoadVector256((byte*)inputIterator);
// Reorder Little Endian bytes to Big Endian.
var answer = Avx2.Shuffle(current, vectorShuffle).AsUInt64();
// Write 4 Big-Endian doubles.
*outputIterator++ = MessagePackCode.Float64;
*(ulong*)outputIterator = answer.GetElement(0);
outputIterator += 8;
*outputIterator++ = MessagePackCode.Float64;
*(ulong*)outputIterator = answer.GetElement(1);
outputIterator += 8;
*outputIterator++ = MessagePackCode.Float64;
*(ulong*)outputIterator = answer.GetElement(2);
outputIterator += 8;
*outputIterator++ = MessagePackCode.Float64;
*(ulong*)outputIterator = answer.GetElement(3);
outputIterator += 8;
}
}
else if (Ssse3.IsSupported)
{
const int ShiftCount = 1;
const int Stride = 1 << ShiftCount;
if (inputLength < Stride << 1)
{
goto ProcessEach;
}
var vectorShuffle = Vector128.Create((byte)7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8);
for (var vectorizedEnd = inputIterator + ((inputLength >> ShiftCount) << ShiftCount); inputIterator != vectorizedEnd; inputIterator += Stride)
{
var current = Sse2.LoadVector128((byte*)inputIterator);
var answer = Ssse3.Shuffle(current, vectorShuffle).AsUInt64();
*outputIterator++ = MessagePackCode.Float64;
*(ulong*)outputIterator = answer.GetElement(0);
outputIterator += 8;
*outputIterator++ = MessagePackCode.Float64;
*(ulong*)outputIterator = answer.GetElement(1);
outputIterator += 8;
}
}
ProcessEach:
while (inputIterator != inputEnd)
{
*outputIterator++ = MessagePackCode.Float64;
var current = *inputIterator++;
*outputIterator++ = (byte)(current >> 56);
*outputIterator++ = (byte)(current >> 48);
*outputIterator++ = (byte)(current >> 40);
*outputIterator++ = (byte)(current >> 32);
*outputIterator++ = (byte)(current >> 24);
*outputIterator++ = (byte)(current >> 16);
*outputIterator++ = (byte)(current >> 8);
*outputIterator++ = (byte)current;
}
}
}
writer.Advance(outputLength);
}
}
public sealed unsafe partial class BooleanArrayFormatter : IMessagePackFormatter<bool[]?>
{
public void Serialize(ref MessagePackWriter writer, bool[]? value, MessagePackSerializerOptions options)
{
if (value == null)
{
writer.WriteNil();
return;
}
var inputLength = value.Length;
writer.WriteArrayHeader(inputLength);
if (inputLength == 0)
{
return;
}
var outputLength = inputLength;
fixed (bool* pSource = &value[0])
{
var inputEnd = pSource + inputLength;
var inputIterator = pSource;
var destination = writer.GetSpan(inputLength);
fixed (byte* pDestination = &destination[0])
{
var outputIterator = pDestination;
if (Avx2.IsSupported)
{
const int ShiftCount = 5;
const int Stride = 1 << ShiftCount;
if (inputLength < Stride << 1)
{
goto ProcessEach;
}
{
// make output span align 32
var offset = UnsafeMemoryAlignmentUtility.CalculateDifferenceAlign32(outputIterator);
inputLength -= offset;
var offsetEnd = inputIterator + offset;
while (inputIterator != offsetEnd)
{
*outputIterator++ = *inputIterator++ ? MessagePackCode.True : MessagePackCode.False;
}
}
var vectorTrue = Vector256.Create(MessagePackCode.True).AsSByte();
var vectorLoopLength = (inputLength >> ShiftCount) << ShiftCount;
for (var vectorizedEnd = inputIterator + vectorLoopLength; inputIterator != vectorizedEnd; inputIterator += Stride, outputIterator += Stride)
{
// Load 32 bool values.
var current = Avx.LoadVector256((sbyte*)inputIterator);
// A value of false for the type bool is 0 for the sbyte representation.
var isTrue = Avx2.CompareEqual(current, Vector256<sbyte>.Zero);
// A value of true in the SIMD context is -1 for the sbyte representation.
// True is 0xc3 as MessagePackCode and false is 0xc2.
// Reinterpreted as sbyte values, they are -61 and -62, respectively.
// For each of the 32 true Vectors, we can add -1 to the false ones to get the answer.
var answer = Avx2.Add(vectorTrue, isTrue);
Avx.Store((sbyte*)outputIterator, answer);
}
}
else if (Sse2.IsSupported)
{
// for older x86 cpu
const int ShiftCount = 4;
const int Stride = 1 << ShiftCount;
if (inputLength < Stride << 1)
{
goto ProcessEach;
}
{
// make output span align 16
var offset = UnsafeMemoryAlignmentUtility.CalculateDifferenceAlign16(outputIterator);
inputLength -= offset;
var offsetEnd = inputIterator + offset;
while (inputIterator != offsetEnd)
{
*outputIterator++ = *inputIterator++ ? MessagePackCode.True : MessagePackCode.False;
}
}
var vectorTrue = Vector128.Create(MessagePackCode.True).AsSByte();
var vectorLoopLength = (inputLength >> ShiftCount) << ShiftCount;
for (var vectorizedEnd = inputIterator + vectorLoopLength; inputIterator != vectorizedEnd; inputIterator += Stride, outputIterator += Stride)
{
// Load 16 bool values.
var current = Sse2.LoadVector128((sbyte*)inputIterator);
// A value of false for the type bool is 0 for the sbyte representation.
var isTrue = Sse2.CompareEqual(current, Vector128<sbyte>.Zero);
// A value of true in the SIMD context is -1 for the sbyte representation.
// True is 0xc3 as MessagePackCode and false is 0xc2.
// Reinterpreted as sbyte values, they are -61 and -62, respectively.
// For each of the 16 true Vectors, we can add -1 to the false ones to get the answer.
var answer = Sse2.Add(vectorTrue, isTrue);
Sse2.Store((sbyte*)outputIterator, answer);
}
}
ProcessEach:
while (inputIterator != inputEnd)
{
*outputIterator++ = *inputIterator++ ? MessagePackCode.True : MessagePackCode.False;
}
}
writer.Advance(outputLength);
}
}
public bool[]? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
if (reader.TryReadNil())
{
return default;
}
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<bool>();
}
var rawSequence = reader.ReadRaw(len);
var array = new bool[len];
fixed (bool* destination = &array[0])
{
var outputIterator = destination;
foreach (var memory in rawSequence)
{
if (memory.IsEmpty)
{
continue;
}
fixed (byte* source = &memory.Span[0])
{
var inputIterator = source;
var inputLength = memory.Length;
var inputEnd = inputIterator + inputLength;
if (Avx2.IsSupported)
{
const int ShiftCount = 5;
const int Stride = 1 << ShiftCount;
if (inputLength < Stride << 1)
{
goto ProcessEach;
}
var vectorLoopLength = (inputLength >> ShiftCount) << ShiftCount;
var vectorFalse = Vector256.Create(MessagePackCode.False).AsSByte();
var vectorTrue = Vector256.Create(MessagePackCode.True).AsSByte();
var vectorOne = Vector256.Create((sbyte)1);
for (var vectorizedEnd = inputIterator + vectorLoopLength; inputIterator != vectorizedEnd; inputIterator += Stride, outputIterator += Stride)
{
var current = Avx.LoadVector256((sbyte*)inputIterator);
var isFalse = Avx2.CompareEqual(current, vectorFalse);
var isTrue = Avx2.CompareEqual(current, vectorTrue);
if (Avx2.MoveMask(Avx2.Xor(isFalse, isTrue)) != -1)
{
throw new MessagePackSerializationException("Unexpected msgpack code encountered.");
}
var answer = Avx2.And(isTrue, vectorOne);
Avx.Store((byte*)outputIterator, answer.AsByte());
}
}
else if (Sse2.IsSupported)
{
// for older x86 cpu
const int ShiftCount = 4;
const int Stride = 1 << ShiftCount;
if (inputLength < Stride << 1)
{
goto ProcessEach;
}
var vectorLoopLength = (inputLength >> ShiftCount) << ShiftCount;
var vectorFalse = Vector128.Create(MessagePackCode.False).AsSByte();
var vectorTrue = Vector128.Create(MessagePackCode.True).AsSByte();
var vectorOne = Vector128.Create((sbyte)1);
for (var vectorizedEnd = inputIterator + vectorLoopLength; inputIterator != vectorizedEnd; inputIterator += Stride, outputIterator += Stride)
{
var current = Sse2.LoadVector128((sbyte*)inputIterator);
var isFalse = Sse2.CompareEqual(current, vectorFalse);
var isTrue = Sse2.CompareEqual(current, vectorTrue);
if (Sse2.MoveMask(Sse2.Xor(isFalse, isTrue)) != 0xFFFF)
{
throw new MessagePackSerializationException("Unexpected msgpack code encountered.");
}
var answer = Sse2.And(isTrue, vectorOne);
Sse2.Store((byte*)outputIterator, answer.AsByte());
}
}
ProcessEach:
while (inputIterator != inputEnd)
{
switch (*inputIterator++)
{
case MessagePackCode.False:
*outputIterator++ = false;
break;
case MessagePackCode.True:
*outputIterator++ = true;
break;
default:
throw new MessagePackSerializationException("Unexpected msgpack code encountered.");
}
}
}
}
}
return array;
}
}
}

Просмотреть файл

@ -0,0 +1,52 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>8.0</LangVersion>
<Nullable>enable</Nullable>
<IsPackable>true</IsPackable>
<Title>MessagePack for C#, Experimental Plugins</Title>
<Description>Extremely Fast MessagePack Serializer for C#(.NET, .NET Core, Unity, Xamarin). Experimental implementations.</Description>
<PackageTags>MsgPack;MessagePack;Serialization;Formatter;Serializer</PackageTags>
</PropertyGroup>
<ItemGroup>
<None Update="FormatterHelpers.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>FormatterHelpers.cs</LastGenOutput>
</None>
<None Update="PrimitiveArrayGetFormatterHelper.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>PrimitiveArrayGetFormatterHelper.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<Compile Update="FormatterHelpers.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>FormatterHelpers.tt</DependentUpon>
</Compile>
<Compile Update="PrimitiveArrayGetFormatterHelper.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>PrimitiveArrayGetFormatterHelper.tt</DependentUpon>
</Compile>
<Compile Update="ResolverHelper.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>ResolverHelper.tt</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MessagePack\MessagePack.csproj" />
</ItemGroup>
<ItemGroup>
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,55 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
/* THIS (.cs) FILE IS GENERATED. DO NOT CHANGE IT.
* CHANGE THE .tt FILE INSTEAD. */
#pragma warning disable CS0436 // The same name of the primary package
using System;
namespace MessagePack.Resolvers
{
internal static class PrimitiveArrayGetFormatterHelper
{
internal static object? GetFormatter(Type t)
{
if (!t.IsArray)
{
return null;
}
if (t == typeof(SByte[]))
{
return Formatters.SByteArrayFormatter.Instance;
}
if (t == typeof(Int16[]))
{
return Formatters.Int16ArrayFormatter.Instance;
}
if (t == typeof(Int32[]))
{
return Formatters.Int32ArrayFormatter.Instance;
}
if (t == typeof(Single[]))
{
return Formatters.SingleArrayFormatter.Instance;
}
if (t == typeof(Double[]))
{
return Formatters.DoubleArrayFormatter.Instance;
}
if (t == typeof(Boolean[]))
{
return Formatters.BooleanArrayFormatter.Instance;
}
return null;
}
}
}

Просмотреть файл

@ -0,0 +1,55 @@
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#
Type[] types = new []
{
typeof(sbyte),
typeof(short),
typeof(int),
typeof(float),
typeof(double),
typeof(bool),
};
#>
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
/* THIS (.cs) FILE IS GENERATED. DO NOT CHANGE IT.
* CHANGE THE .tt FILE INSTEAD. */
#pragma warning disable CS0436 // The same name of the primary package
using System;
namespace MessagePack.Resolvers
{
internal static class PrimitiveArrayGetFormatterHelper
{
internal static object? GetFormatter(Type t)
{
if (!t.IsArray)
{
return null;
}
<#
foreach (var type in types)
{
var name = type.Name;
#>
if (t == typeof(<#= name #>[]))
{
return Formatters.<#= name + "ArrayFormatter" #>.Instance;
}
<#
}
#>
return null;
}
}
}

Просмотреть файл

@ -0,0 +1,31 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using MessagePack.Formatters;
namespace MessagePack.Resolvers
{
public sealed class PrimitiveArrayResolver : IFormatterResolver
{
public static readonly PrimitiveArrayResolver Instance = new PrimitiveArrayResolver();
private PrimitiveArrayResolver()
{
}
public IMessagePackFormatter<T>? GetFormatter<T>()
{
return FormatterCache<T>.Formatter;
}
private static class FormatterCache<T>
{
internal static readonly IMessagePackFormatter<T>? Formatter;
static FormatterCache()
{
Formatter = PrimitiveArrayGetFormatterHelper.GetFormatter(typeof(T)) as IMessagePackFormatter<T>;
}
}
}
}

Просмотреть файл

@ -0,0 +1,28 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Runtime.CompilerServices;
namespace MessagePack.Formatters
{
internal static unsafe class UnsafeMemoryAlignmentUtility
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int CalculateDifferenceAlign16(void* pointer)
{
var difference = new UIntPtr(pointer).ToUInt64();
difference -= (difference >> 4) << 4;
difference = (16 - difference) & 0xfUL;
return (int)difference;
}
public static int CalculateDifferenceAlign32(void* pointer)
{
var difference = new UIntPtr(pointer).ToUInt64();
difference -= (difference >> 5) << 5;
difference = (32 - difference) & 0x1fUL;
return (int)difference;
}
}
}

Просмотреть файл

@ -93,17 +93,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new Int16[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadInt16();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<Int16>();
}
var array = new Int16[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadInt16();
}
return array;
}
}
@ -189,17 +192,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new Int32[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadInt32();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<Int32>();
}
var array = new Int32[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadInt32();
}
return array;
}
}
@ -285,17 +291,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new Int64[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadInt64();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<Int64>();
}
var array = new Int64[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadInt64();
}
return array;
}
}
@ -381,17 +390,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new UInt16[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadUInt16();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<UInt16>();
}
var array = new UInt16[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadUInt16();
}
return array;
}
}
@ -477,17 +489,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new UInt32[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadUInt32();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<UInt32>();
}
var array = new UInt32[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadUInt32();
}
return array;
}
}
@ -573,17 +588,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new UInt64[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadUInt64();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<UInt64>();
}
var array = new UInt64[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadUInt64();
}
return array;
}
}
@ -669,17 +687,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new Single[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadSingle();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<Single>();
}
var array = new Single[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadSingle();
}
return array;
}
}
@ -765,17 +786,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new Double[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadDouble();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<Double>();
}
var array = new Double[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadDouble();
}
return array;
}
}
@ -861,17 +885,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new Boolean[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadBoolean();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<Boolean>();
}
var array = new Boolean[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadBoolean();
}
return array;
}
}
@ -1009,17 +1036,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new SByte[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadSByte();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<SByte>();
}
var array = new SByte[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadSByte();
}
return array;
}
}
@ -1105,17 +1135,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new Char[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadChar();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<Char>();
}
var array = new Char[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadChar();
}
return array;
}
}
@ -1201,17 +1234,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new DateTime[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadDateTime();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<DateTime>();
}
var array = new DateTime[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadDateTime();
}
return array;
}
}
}

Просмотреть файл

@ -182,6 +182,10 @@ namespace MessagePack.Tests
public static object[][] StandardClassFormatterTestData = new object[][]
{
new object[] { new byte[] { 1, 10, 100 }, new byte[0] { }, null },
new object[] { new bool[] { true, false, true, false, true, false, true, false, true, false, true, false, true, false, true, false, true, false, true, false, true, false, true, false, true, false, true, false, true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, }, new bool[0] { }, null },
new object[] { new sbyte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33 }, new sbyte[0] { }, null },
new object[] { new short[] { -0x1000, -0x4000, -0x1d13, (short)unchecked((sbyte)0x80), 0, 1, 2, 3, -0xd2, 0x32, 0x7f, -0x10, -32, -33, 128, 127, 0x457, 0x30, 0x123f, 0x90, 0xa0, 0xb0, 0xc0, 0xa1, 0xc1, 0x3c, 0x4d, 0x5e, 0x6f, 0x111, 0x211, 0x311, 0x411, 0x511, 0x611, 0x711, 0x811 }, new short[0], null },
new object[] { new int[] { 0x10000000, 0x20000000, 0x30000000, 0x30004000, 0x40000000, 0x50100100, 0x60000000, 0x70000000, 0x1000, 0x2000, 0x3000, 0x400, 128, 127, -32, 254, 256, ushort.MaxValue, ushort.MinValue, ushort.MinValue + 1, ushort.MaxValue + 2, -33, 15, 13, 0, 1, -1, 0x500, 0x600, 0x700, 0x8100 }, new int[0], null },
new object[] { "aaa", string.Empty, null },
new object[] { new Uri("Http://hogehoge.com"), new Uri("Https://hugahuga.com"), null },
new object[] { new Version(0, 0), new Version(1, 2, 3), new Version(255, 100, 30) },

Просмотреть файл

@ -93,17 +93,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new Int16[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadInt16();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<Int16>();
}
var array = new Int16[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadInt16();
}
return array;
}
}
@ -189,17 +192,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new Int32[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadInt32();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<Int32>();
}
var array = new Int32[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadInt32();
}
return array;
}
}
@ -285,17 +291,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new Int64[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadInt64();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<Int64>();
}
var array = new Int64[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadInt64();
}
return array;
}
}
@ -381,17 +390,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new UInt16[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadUInt16();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<UInt16>();
}
var array = new UInt16[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadUInt16();
}
return array;
}
}
@ -477,17 +489,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new UInt32[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadUInt32();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<UInt32>();
}
var array = new UInt32[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadUInt32();
}
return array;
}
}
@ -573,17 +588,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new UInt64[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadUInt64();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<UInt64>();
}
var array = new UInt64[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadUInt64();
}
return array;
}
}
@ -669,17 +687,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new Single[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadSingle();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<Single>();
}
var array = new Single[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadSingle();
}
return array;
}
}
@ -765,17 +786,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new Double[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadDouble();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<Double>();
}
var array = new Double[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadDouble();
}
return array;
}
}
@ -861,17 +885,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new Boolean[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadBoolean();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<Boolean>();
}
var array = new Boolean[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadBoolean();
}
return array;
}
}
@ -1009,17 +1036,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new SByte[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadSByte();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<SByte>();
}
var array = new SByte[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadSByte();
}
return array;
}
}
@ -1105,17 +1135,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new Char[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadChar();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<Char>();
}
var array = new Char[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadChar();
}
return array;
}
}
@ -1201,17 +1234,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new DateTime[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadDateTime();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<DateTime>();
}
var array = new DateTime[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.ReadDateTime();
}
return array;
}
}
}

Просмотреть файл

@ -15,8 +15,8 @@
typeof(UInt64),
typeof(Single),
typeof(Double),
typeof(bool),
typeof(byte),
typeof(bool),
typeof(byte),
typeof(sbyte),
typeof(char),
typeof(DateTime)
@ -119,17 +119,20 @@ namespace MessagePack.Formatters
{
return default;
}
else
{
var len = reader.ReadArrayHeader();
var array = new <#= t.Name #>[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.Read<#= t.Name #>();
}
return array;
var len = reader.ReadArrayHeader();
if (len == 0)
{
return Array.Empty<<#= t.Name #>>();
}
var array = new <#= t.Name #>[len];
for (int i = 0; i < array.Length; i++)
{
array[i] = reader.Read<#= t.Name #>();
}
return array;
}
}
<# } #>

Просмотреть файл

@ -0,0 +1,395 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Runtime.InteropServices;
using NUnit.Framework;
namespace MessagePack.Experimental.Tests
{
public class ArrayTests
{
private MessagePackSerializerOptions options;
[SetUp]
public void SetUp()
{
var resolver = MessagePack.Resolvers.CompositeResolver.Create(Resolvers.PrimitiveArrayResolver.Instance, MessagePack.Resolvers.StandardResolver.Instance);
options = MessagePackSerializerOptions.Standard.WithResolver(resolver);
}
[Test]
public void EmptySByteArrayTests()
{
var array = Array.Empty<SByte>();
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<SByte[]>(encoded, options);
Assert.IsNotNull(decoded);
Assert.AreEqual(0, decoded.Length);
}
[Test]
public void NullSByteArrayTests()
{
var array = default(SByte[]);
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<SByte[]>(encoded, options);
Assert.IsNull(decoded);
}
[TestCase(1)]
[TestCase(8)]
[TestCase(16)]
[TestCase(32)]
[TestCase(128)]
[TestCase(4096)]
public void DefaultSByteArrayTests(int length)
{
var array = new SByte[length];
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<SByte[]>(encoded, options);
Assert.IsNotNull(decoded);
Assert.AreEqual(length, decoded.Length);
for (var index = 0; index < array.Length; index++)
{
Assert.AreEqual(array[index], decoded[index]);
}
}
[TestCase(1)]
[TestCase(8)]
[TestCase(16)]
[TestCase(32)]
[TestCase(128)]
[TestCase(4096)]
public void MinValueSByteArrayTests(int length)
{
var array = new SByte[length];
for (var index = 0; index < array.Length; index++)
{
array[index] = SByte.MinValue;
}
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<SByte[]>(encoded, options);
Assert.IsNotNull(decoded);
Assert.AreEqual(length, decoded.Length);
for (var index = 0; index < array.Length; index++)
{
Assert.AreEqual(array[index], decoded[index]);
}
}
[TestCase(1)]
[TestCase(8)]
[TestCase(16)]
[TestCase(32)]
[TestCase(128)]
[TestCase(4096)]
public void RandomValueSByteArrayTests(int length)
{
var array = new SByte[length];
var r = new Random();
r.NextBytes(MemoryMarshal.AsBytes(array.AsSpan()));
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<SByte[]>(encoded, options);
Assert.IsNotNull(decoded);
Assert.AreEqual(length, decoded.Length);
for (var index = 0; index < array.Length; index++)
{
Assert.AreEqual(array[index], decoded[index]);
}
}
[Test]
public void EmptyInt16ArrayTests()
{
var array = Array.Empty<Int16>();
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<Int16[]>(encoded, options);
Assert.IsNotNull(decoded);
Assert.AreEqual(0, decoded.Length);
}
[Test]
public void NullInt16ArrayTests()
{
var array = default(Int16[]);
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<Int16[]>(encoded, options);
Assert.IsNull(decoded);
}
[TestCase(1)]
[TestCase(8)]
[TestCase(16)]
[TestCase(32)]
[TestCase(128)]
[TestCase(4096)]
public void DefaultInt16ArrayTests(int length)
{
var array = new Int16[length];
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<Int16[]>(encoded, options);
Assert.IsNotNull(decoded);
Assert.AreEqual(length, decoded.Length);
for (var index = 0; index < array.Length; index++)
{
Assert.AreEqual(array[index], decoded[index]);
}
}
[TestCase(1)]
[TestCase(8)]
[TestCase(16)]
[TestCase(32)]
[TestCase(128)]
[TestCase(4096)]
public void MinValueInt16ArrayTests(int length)
{
var array = new Int16[length];
for (var index = 0; index < array.Length; index++)
{
array[index] = Int16.MinValue;
}
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<Int16[]>(encoded, options);
Assert.IsNotNull(decoded);
Assert.AreEqual(length, decoded.Length);
for (var index = 0; index < array.Length; index++)
{
Assert.AreEqual(array[index], decoded[index]);
}
}
[TestCase(1)]
[TestCase(8)]
[TestCase(16)]
[TestCase(32)]
[TestCase(128)]
[TestCase(4096)]
public void RandomValueInt16ArrayTests(int length)
{
var array = new Int16[length];
var r = new Random();
r.NextBytes(MemoryMarshal.AsBytes(array.AsSpan()));
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<Int16[]>(encoded, options);
Assert.IsNotNull(decoded);
Assert.AreEqual(length, decoded.Length);
for (var index = 0; index < array.Length; index++)
{
Assert.AreEqual(array[index], decoded[index]);
}
}
[Test]
public void EmptyInt32ArrayTests()
{
var array = Array.Empty<Int32>();
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<Int32[]>(encoded, options);
Assert.IsNotNull(decoded);
Assert.AreEqual(0, decoded.Length);
}
[Test]
public void NullInt32ArrayTests()
{
var array = default(Int32[]);
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<Int32[]>(encoded, options);
Assert.IsNull(decoded);
}
[TestCase(1)]
[TestCase(8)]
[TestCase(16)]
[TestCase(32)]
[TestCase(128)]
[TestCase(4096)]
public void DefaultInt32ArrayTests(int length)
{
var array = new Int32[length];
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<Int32[]>(encoded, options);
Assert.IsNotNull(decoded);
Assert.AreEqual(length, decoded.Length);
for (var index = 0; index < array.Length; index++)
{
Assert.AreEqual(array[index], decoded[index]);
}
}
[TestCase(1)]
[TestCase(8)]
[TestCase(16)]
[TestCase(32)]
[TestCase(128)]
[TestCase(4096)]
public void MinValueInt32ArrayTests(int length)
{
var array = new Int32[length];
for (var index = 0; index < array.Length; index++)
{
array[index] = Int32.MinValue;
}
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<Int32[]>(encoded, options);
Assert.IsNotNull(decoded);
Assert.AreEqual(length, decoded.Length);
for (var index = 0; index < array.Length; index++)
{
Assert.AreEqual(array[index], decoded[index]);
}
}
[TestCase(1)]
[TestCase(8)]
[TestCase(16)]
[TestCase(32)]
[TestCase(128)]
[TestCase(4096)]
public void RandomValueInt32ArrayTests(int length)
{
var array = new Int32[length];
var r = new Random();
r.NextBytes(MemoryMarshal.AsBytes(array.AsSpan()));
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<Int32[]>(encoded, options);
Assert.IsNotNull(decoded);
Assert.AreEqual(length, decoded.Length);
for (var index = 0; index < array.Length; index++)
{
Assert.AreEqual(array[index], decoded[index]);
}
}
[Test]
public void EmptySingleArrayTests()
{
var array = Array.Empty<Single>();
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<Single[]>(encoded, options);
Assert.IsNotNull(decoded);
Assert.AreEqual(0, decoded.Length);
}
[Test]
public void NullSingleArrayTests()
{
var array = default(Single[]);
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<Single[]>(encoded, options);
Assert.IsNull(decoded);
}
[TestCase(1)]
[TestCase(8)]
[TestCase(16)]
[TestCase(32)]
[TestCase(128)]
[TestCase(4096)]
public void DefaultSingleArrayTests(int length)
{
var array = new Single[length];
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<Single[]>(encoded, options);
Assert.IsNotNull(decoded);
Assert.AreEqual(length, decoded.Length);
for (var index = 0; index < array.Length; index++)
{
Assert.AreEqual(array[index], decoded[index]);
}
}
[TestCase(1)]
[TestCase(8)]
[TestCase(16)]
[TestCase(32)]
[TestCase(128)]
[TestCase(4096)]
public void MinValueSingleArrayTests(int length)
{
var array = new Single[length];
for (var index = 0; index < array.Length; index++)
{
array[index] = Single.MinValue;
}
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<Single[]>(encoded, options);
Assert.IsNotNull(decoded);
Assert.AreEqual(length, decoded.Length);
for (var index = 0; index < array.Length; index++)
{
Assert.AreEqual(array[index], decoded[index]);
}
}
[Test]
public void EmptyBooleanArrayTests()
{
var array = Array.Empty<Boolean>();
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<Boolean[]>(encoded, options);
Assert.IsNotNull(decoded);
Assert.AreEqual(0, decoded.Length);
}
[Test]
public void NullBooleanArrayTests()
{
var array = default(Boolean[]);
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<Boolean[]>(encoded, options);
Assert.IsNull(decoded);
}
[TestCase(1)]
[TestCase(8)]
[TestCase(16)]
[TestCase(32)]
[TestCase(128)]
[TestCase(4096)]
public void DefaultBooleanArrayTests(int length)
{
var array = new Boolean[length];
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<Boolean[]>(encoded, options);
Assert.IsNotNull(decoded);
Assert.AreEqual(length, decoded.Length);
for (var index = 0; index < array.Length; index++)
{
Assert.AreEqual(array[index], decoded[index]);
}
}
}
}

Просмотреть файл

@ -0,0 +1,148 @@
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#
var types = new Type[]
{
typeof(sbyte),
typeof(short),
typeof(int),
typeof(float),
typeof(bool),
};
#>
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Runtime.InteropServices;
using NUnit.Framework;
namespace MessagePack.Experimental.Tests
{
public class ArrayTests
{
private MessagePackSerializerOptions options;
[SetUp]
public void SetUp()
{
var resolver = MessagePack.Resolvers.CompositeResolver.Create(Resolvers.PrimitiveArrayResolver.Instance, MessagePack.Resolvers.StandardResolver.Instance);
options = MessagePackSerializerOptions.Standard.WithResolver(resolver);
}
<#
foreach (var type in types)
{
var name = type.Name;
#>
[Test]
public void Empty<#= name #>ArrayTests()
{
var array = Array.Empty<<#= name #>>();
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<<#= name #>[]>(encoded, options);
Assert.IsNotNull(decoded);
Assert.AreEqual(0, decoded.Length);
}
[Test]
public void Null<#= name #>ArrayTests()
{
var array = default(<#= name #>[]);
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<<#= name #>[]>(encoded, options);
Assert.IsNull(decoded);
}
[TestCase(1)]
[TestCase(8)]
[TestCase(16)]
[TestCase(32)]
[TestCase(128)]
[TestCase(4096)]
public void Default<#= name #>ArrayTests(int length)
{
var array = new <#= name #>[length];
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<<#= name #>[]>(encoded, options);
Assert.IsNotNull(decoded);
Assert.AreEqual(length, decoded.Length);
for (var index = 0; index < array.Length; index++)
{
Assert.AreEqual(array[index], decoded[index]);
}
}
<#
if (type != typeof(bool))
{
#>
[TestCase(1)]
[TestCase(8)]
[TestCase(16)]
[TestCase(32)]
[TestCase(128)]
[TestCase(4096)]
public void MinValue<#= name #>ArrayTests(int length)
{
var array = new <#= name #>[length];
for (var index = 0; index < array.Length; index++)
{
array[index] = <#= name #>.MinValue;
}
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<<#= name #>[]>(encoded, options);
Assert.IsNotNull(decoded);
Assert.AreEqual(length, decoded.Length);
for (var index = 0; index < array.Length; index++)
{
Assert.AreEqual(array[index], decoded[index]);
}
}
<#
}
#>
<#
if (type != typeof(float) && type != typeof(bool))
{
#>
[TestCase(1)]
[TestCase(8)]
[TestCase(16)]
[TestCase(32)]
[TestCase(128)]
[TestCase(4096)]
public void RandomValue<#= name #>ArrayTests(int length)
{
var array = new <#= name #>[length];
var r = new Random();
r.NextBytes(MemoryMarshal.AsBytes(array.AsSpan()));
var encoded = MessagePackSerializer.Serialize(array, options);
Assert.IsNotNull(encoded);
var decoded = MessagePackSerializer.Deserialize<<#= name #>[]>(encoded, options);
Assert.IsNotNull(decoded);
Assert.AreEqual(length, decoded.Length);
for (var index = 0; index < array.Length; index++)
{
Assert.AreEqual(array[index], decoded[index]);
}
}
<#
}
#>
<#
}
#>
}
}

Просмотреть файл

@ -0,0 +1,43 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<None Update="ArrayTests.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>ArrayTests.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
</ItemGroup>
<ItemGroup>
<Compile Update="ArrayTests.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>ArrayTests.tt</DependentUpon>
</Compile>
<Compile Update="NullAndEmptyArrayTests.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>NullAndEmptyArrayTests.tt</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<PackageReference Include="nunit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\MessagePack.Experimental\MessagePack.Experimental.csproj" />
</ItemGroup>
</Project>