Skip to content

Commit

Permalink
Improved FastJsonWriter and added tests and benchmarks.
Browse files Browse the repository at this point in the history
  • Loading branch information
ashmind committed Oct 8, 2016
1 parent 39ae61f commit 9734a8e
Show file tree
Hide file tree
Showing 19 changed files with 513 additions and 48 deletions.
21 changes: 21 additions & 0 deletions MirrorSharp.Benchmarks/MirrorSharp.Benchmarks.xproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>

<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>35d73c72-267b-4598-abc0-f1e1bacde451</ProjectGuid>
<RootNamespace>MirrorSharp.Benchmarks</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
</PropertyGroup>

<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
126 changes: 126 additions & 0 deletions MirrorSharp.Benchmarks/Of.Json/ComplexObjectBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
using System;
using System.IO;
using BenchmarkDotNet.Attributes;
using MirrorSharp.Internal;
using Newtonsoft.Json;

namespace MirrorSharp.Benchmarks.Of.Json {
public class ComplexObjectBenchmarks : JsonBenchmarksBase {
/*
{
"type":"completions",
"completions":{
"span":{"start":70,"length":0},
"list":[
{
"filterText":"Equals",
"displayText":"Equals",
"tags":["method","public"]
},
{
"filterText":"GetHashCode",
"displayText":"GetHashCode",
"tags":["method","public"]
},{
"filterText":"GetType",
"displayText":"GetType",
"tags":["method","public"]
},{
"filterText":"ToString",
"displayText":"ToString",
"tags":["method","public"]
}
]
}
}
*/

[Benchmark]
public ArraySegment<byte> NewtonsoftJson_JsonWriter() {
_memoryStream.Seek(0, SeekOrigin.Begin);

var writer = _newtonsoftJsonWriter;
writer.WriteStartObject();
writer.WritePropertyName("type");
writer.WriteValue("completions");
writer.WritePropertyName("completions");
writer.WriteStartObject();
writer.WritePropertyName("span");
writer.WriteStartObject();
writer.WritePropertyName("start");
writer.WriteValue(70);
writer.WritePropertyName("length");
writer.WriteValue(0);
writer.WriteEndObject();
writer.WritePropertyName("list");
writer.WriteStartArray();
WriteCompletion(writer, "Equals");
WriteCompletion(writer, "GetHashCode");
WriteCompletion(writer, "GetType");
WriteCompletion(writer, "ToString");
writer.WriteEndArray();
writer.WriteEndObject();
writer.WriteEndObject();

return FlushNewtonsoftJsonWriterAndGetBuffer();
}

[Benchmark]
public ArraySegment<byte> MirrorSharp_FastJsonWriter() {
_fastJsonWriter.Reset();

var writer = _fastJsonWriter;
writer.WriteStartObject();
writer.WritePropertyName("type");
writer.WriteValue("completions");
writer.WritePropertyName("completions");
writer.WriteStartObject();
writer.WritePropertyName("span");
writer.WriteStartObject();
writer.WritePropertyName("start");
writer.WriteValue(70);
writer.WritePropertyName("length");
writer.WriteValue(0);
writer.WriteEndObject();
writer.WritePropertyName("list");
writer.WriteStartArray();
WriteCompletion(writer, "Equals");
WriteCompletion(writer, "GetHashCode");
WriteCompletion(writer, "GetType");
WriteCompletion(writer, "ToString");
writer.WriteEndArray();
writer.WriteEndObject();
writer.WriteEndObject();

return _fastJsonWriter.WrittenSegment;
}

private static void WriteCompletion(JsonTextWriter writer, string text) {
writer.WriteStartObject();
writer.WritePropertyName("filterText");
writer.WriteValue(text);
writer.WritePropertyName("displayText");
writer.WriteValue(text);
writer.WritePropertyName("tags");
writer.WriteStartArray();
writer.WriteValue("method");
writer.WriteValue("public");
writer.WriteEndArray();
writer.WriteEndObject();
}

private static void WriteCompletion(FastUtf8JsonWriter writer, string text) {
writer.WriteStartObject();
writer.WritePropertyName("filterText");
writer.WriteValue(text);
writer.WritePropertyName("displayText");
writer.WriteValue(text);
writer.WritePropertyName("tags");
writer.WriteStartArray();
writer.WriteValue("method");
writer.WriteValue("public");
writer.WriteEndArray();
writer.WriteEndObject();
}
}
}
37 changes: 37 additions & 0 deletions MirrorSharp.Benchmarks/Of.Json/JsonBenchmarksBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Buffers;
using System.IO;
using BenchmarkDotNet.Attributes;
using MirrorSharp.Internal;
using Newtonsoft.Json;

namespace MirrorSharp.Benchmarks.Of.Json {
public class JsonBenchmarksBase {
// ReSharper disable InconsistentNaming
protected MemoryStream _memoryStream;
protected JsonTextWriter _newtonsoftJsonWriter;
protected FastUtf8JsonWriter _fastJsonWriter;
// ReSharper restore InconsistentNaming

[Setup]
public void Setup() {
_memoryStream = new MemoryStream();
_newtonsoftJsonWriter = new JsonTextWriter(new StreamWriter(_memoryStream)) {
Formatting = Formatting.None
};
_fastJsonWriter = new FastUtf8JsonWriter(ArrayPool<byte>.Shared);
}

protected ArraySegment<byte> FlushNewtonsoftJsonWriterAndGetBuffer() {
_newtonsoftJsonWriter.Flush();
ArraySegment<byte> buffer;
_memoryStream.TryGetBuffer(out buffer);
return buffer;
}

[Cleanup]
public void Cleanup() {
_fastJsonWriter.Dispose();
}
}
}
31 changes: 31 additions & 0 deletions MirrorSharp.Benchmarks/Of.Json/WriteValueInt32Benchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using MirrorSharp.Benchmarks.Of.Json;
using MirrorSharp.Internal;
using Newtonsoft.Json;

namespace MirrorSharp.Benchmarks {
public class WriteValueInt32Benchmarks : JsonBenchmarksBase {
[Params(-111, 1, 1111111)]
public int Value { get; set; }

[Benchmark]
public ArraySegment<byte> NewtonsoftJson_JsonWriter() {
_memoryStream.Seek(0, SeekOrigin.Begin);
_newtonsoftJsonWriter.WriteValue(Value);
return FlushNewtonsoftJsonWriterAndGetBuffer();
}

[Benchmark]
public ArraySegment<byte> MirrorSharp_FastJsonWriter() {
_fastJsonWriter.Reset();
_fastJsonWriter.WriteValue(Value);
return _fastJsonWriter.WrittenSegment;
}
}
}
29 changes: 29 additions & 0 deletions MirrorSharp.Benchmarks/Of.Json/WriteValueStringBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.IO;
using BenchmarkDotNet.Attributes;

namespace MirrorSharp.Benchmarks.Of.Json {
public class WriteValueStringBenchmarks : JsonBenchmarksBase {
private const string String = @"
using System;
public class C {
public void M() {
}
}
";

[Benchmark]
public ArraySegment<byte> NewtonsoftJson_JsonWriter() {
_memoryStream.Seek(0, SeekOrigin.Begin);
_newtonsoftJsonWriter.WriteValue(String);
return FlushNewtonsoftJsonWriterAndGetBuffer();
}

[Benchmark]
public ArraySegment<byte> MirrorSharp_FastJsonWriter() {
_fastJsonWriter.Reset();
_fastJsonWriter.WriteValue(String);
return _fastJsonWriter.WrittenSegment;
}
}
}
14 changes: 14 additions & 0 deletions MirrorSharp.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BenchmarkDotNet.Running;
using MirrorSharp.Benchmarks.Of.Json;

namespace MirrorSharp.Benchmarks {
public static class Program {
public static void Main(string[] args) {
BenchmarkRunner.Run<WriteValueStringBenchmarks>();
}
}
}
19 changes: 19 additions & 0 deletions MirrorSharp.Benchmarks/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("MirrorSharp.Benchmarks")]
[assembly: AssemblyTrademark("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("35d73c72-267b-4598-abc0-f1e1bacde451")]
22 changes: 22 additions & 0 deletions MirrorSharp.Benchmarks/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"version": "1.0.0-*",
"buildOptions": {
"emitEntryPoint": true
},

"dependencies": {
"BenchmarkDotNet": "0.9.9",
"Newtonsoft.Json": "9.0.1",
"MirrorSharp.Common": "*",
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.0"
}
},

"frameworks": {
"netcoreapp1.0": {
"imports": [ "dnxcore50", "portable-net45+win8+wp8+wpa81", "portable-net45+win8" ]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace MirrorSharp.Internal.Commands {
public interface ICommandResultSender {
FastJsonWriter StartJsonMessage(string messageTypeName);
FastUtf8JsonWriter StartJsonMessage(string messageTypeName);
Task SendJsonMessageAsync(CancellationToken cancellationToken);
}
}
16 changes: 9 additions & 7 deletions MirrorSharp.Common/Internal/Connection.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;
using System.Buffers;
using System.Collections.Immutable;
using System.IO;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -12,16 +12,15 @@ public class Connection : ICommandResultSender, IDisposable {
private readonly WorkSession _session;
private readonly ImmutableArray<ICommandHandler> _handlers;
private readonly byte[] _inputBuffer = new byte[4096];
private readonly byte[] _outputBuffer = new byte[4096];

private readonly FastJsonWriter _messageWriter;
private readonly FastUtf8JsonWriter _messageWriter;
private readonly IConnectionOptions _options;

public Connection(WebSocket socket, WorkSession session, ImmutableArray<ICommandHandler> handlers, IConnectionOptions options = null) {
_socket = socket;
_session = session;
_handlers = handlers;
_messageWriter = new FastJsonWriter(_outputBuffer);
_messageWriter = new FastUtf8JsonWriter(ArrayPool<byte>.Shared);
_options = options ?? new MirrorSharpOptions();
}

Expand Down Expand Up @@ -97,7 +96,7 @@ private Task SendErrorAsync(string message, CancellationToken cancellationToken)
return SendJsonMessageAsync(cancellationToken);
}

private FastJsonWriter StartJsonMessage(string messageTypeName) {
private FastUtf8JsonWriter StartJsonMessage(string messageTypeName) {
_messageWriter.Reset();
_messageWriter.WriteStartObject();
_messageWriter.WriteProperty("type", messageTypeName);
Expand All @@ -112,9 +111,12 @@ private Task SendJsonMessageAsync(CancellationToken cancellationToken) {
);
}

public void Dispose() => _session.Dispose();
public void Dispose() {
_messageWriter.Dispose();
_session.Dispose();
}

FastJsonWriter ICommandResultSender.StartJsonMessage(string messageTypeName) => StartJsonMessage(messageTypeName);
FastUtf8JsonWriter ICommandResultSender.StartJsonMessage(string messageTypeName) => StartJsonMessage(messageTypeName);
Task ICommandResultSender.SendJsonMessageAsync(CancellationToken cancellationToken) => SendJsonMessageAsync(cancellationToken);
}
}
13 changes: 13 additions & 0 deletions MirrorSharp.Common/Internal/EncodingExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MirrorSharp.Internal {
public static class EncodingExtensions {
public static string GetString(this Encoding encoding, ArraySegment<byte> segment) {
return encoding.GetString(segment.Array, segment.Offset, segment.Count);
}
}
}
2 changes: 1 addition & 1 deletion MirrorSharp.Common/Internal/FastConvert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public static string CharToString(char c) {
}

private static string SlowUtf8ByteArrayToString(ArraySegment<byte> bytes) {
return Encoding.UTF8.GetString(bytes.Array, bytes.Offset, bytes.Count);
return Encoding.UTF8.GetString(bytes);
}
}
}
Loading

0 comments on commit 9734a8e

Please sign in to comment.