Skip to content

Commit

Permalink
Leverage modern .NET features for improved perf. (#803)
Browse files Browse the repository at this point in the history
- When serializing objects, recognize ISpanFormattable on .NET 6
to avoid a temp string allocation in that case.

- Use binary primitives to serialize scalar types more efficiently,
avoiding repeated bound checks.
  • Loading branch information
geeknoid authored Dec 8, 2022
1 parent efb4ace commit c49a261
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
Microsoft.Extensions.Logging.GenevaLoggingExtensions
OpenTelemetry.Exporter.Geneva.GenevaBaseExporter<T>
OpenTelemetry.Exporter.Geneva.GenevaBaseExporter<T>.GenevaBaseExporter() -> void
OpenTelemetry.Exporter.Geneva.GenevaExporterHelperExtensions
OpenTelemetry.Exporter.Geneva.GenevaExporterOptions
OpenTelemetry.Exporter.Geneva.GenevaExporterOptions.ConnectionString.get -> string
OpenTelemetry.Exporter.Geneva.GenevaExporterOptions.ConnectionString.set -> void
OpenTelemetry.Exporter.Geneva.GenevaExporterOptions.CustomFields.get -> System.Collections.Generic.IEnumerable<string>
OpenTelemetry.Exporter.Geneva.GenevaExporterOptions.CustomFields.set -> void
OpenTelemetry.Exporter.Geneva.GenevaExporterOptions.GenevaExporterOptions() -> void
OpenTelemetry.Exporter.Geneva.GenevaExporterOptions.PrepopulatedFields.get -> System.Collections.Generic.IReadOnlyDictionary<string, object>
OpenTelemetry.Exporter.Geneva.GenevaExporterOptions.PrepopulatedFields.set -> void
OpenTelemetry.Exporter.Geneva.GenevaExporterOptions.TableNameMappings.get -> System.Collections.Generic.IReadOnlyDictionary<string, string>
OpenTelemetry.Exporter.Geneva.GenevaExporterOptions.TableNameMappings.set -> void
OpenTelemetry.Exporter.Geneva.GenevaLogExporter
OpenTelemetry.Exporter.Geneva.GenevaLogExporter.GenevaLogExporter(OpenTelemetry.Exporter.Geneva.GenevaExporterOptions options) -> void
OpenTelemetry.Exporter.Geneva.GenevaMetricExporter
OpenTelemetry.Exporter.Geneva.GenevaMetricExporter.GenevaMetricExporter(OpenTelemetry.Exporter.Geneva.GenevaMetricExporterOptions options) -> void
OpenTelemetry.Exporter.Geneva.GenevaMetricExporterExtensions
OpenTelemetry.Exporter.Geneva.GenevaMetricExporterOptions
OpenTelemetry.Exporter.Geneva.GenevaMetricExporterOptions.ConnectionString.get -> string
OpenTelemetry.Exporter.Geneva.GenevaMetricExporterOptions.ConnectionString.set -> void
OpenTelemetry.Exporter.Geneva.GenevaMetricExporterOptions.GenevaMetricExporterOptions() -> void
OpenTelemetry.Exporter.Geneva.GenevaMetricExporterOptions.MetricExportIntervalMilliseconds.get -> int
OpenTelemetry.Exporter.Geneva.GenevaMetricExporterOptions.MetricExportIntervalMilliseconds.set -> void
OpenTelemetry.Exporter.Geneva.GenevaMetricExporterOptions.PrepopulatedMetricDimensions.get -> System.Collections.Generic.IReadOnlyDictionary<string, object>
OpenTelemetry.Exporter.Geneva.GenevaMetricExporterOptions.PrepopulatedMetricDimensions.set -> void
OpenTelemetry.Exporter.Geneva.GenevaTraceExporter
OpenTelemetry.Exporter.Geneva.GenevaTraceExporter.GenevaTraceExporter(OpenTelemetry.Exporter.Geneva.GenevaExporterOptions options) -> void
override OpenTelemetry.Exporter.Geneva.GenevaLogExporter.Dispose(bool disposing) -> void
override OpenTelemetry.Exporter.Geneva.GenevaLogExporter.Export(in OpenTelemetry.Batch<OpenTelemetry.Logs.LogRecord> batch) -> OpenTelemetry.ExportResult
override OpenTelemetry.Exporter.Geneva.GenevaMetricExporter.Dispose(bool disposing) -> void
override OpenTelemetry.Exporter.Geneva.GenevaMetricExporter.Export(in OpenTelemetry.Batch<OpenTelemetry.Metrics.Metric> batch) -> OpenTelemetry.ExportResult
override OpenTelemetry.Exporter.Geneva.GenevaTraceExporter.Dispose(bool disposing) -> void
override OpenTelemetry.Exporter.Geneva.GenevaTraceExporter.Export(in OpenTelemetry.Batch<System.Diagnostics.Activity> batch) -> OpenTelemetry.ExportResult
static Microsoft.Extensions.Logging.GenevaLoggingExtensions.AddGenevaLogExporter(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions options, System.Action<OpenTelemetry.Exporter.Geneva.GenevaExporterOptions> configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions
static OpenTelemetry.Exporter.Geneva.GenevaExporterHelperExtensions.AddGenevaTraceExporter(this OpenTelemetry.Trace.TracerProviderBuilder builder, System.Action<OpenTelemetry.Exporter.Geneva.GenevaExporterOptions> configure) -> OpenTelemetry.Trace.TracerProviderBuilder
static OpenTelemetry.Exporter.Geneva.GenevaMetricExporterExtensions.AddGenevaMetricExporter(this OpenTelemetry.Metrics.MeterProviderBuilder builder, System.Action<OpenTelemetry.Exporter.Geneva.GenevaMetricExporterOptions> configure = null) -> OpenTelemetry.Metrics.MeterProviderBuilder
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
OpenTelemetry.Exporter.Geneva.ExceptionStackExportMode
OpenTelemetry.Exporter.Geneva.ExceptionStackExportMode.Drop = 0 -> OpenTelemetry.Exporter.Geneva.ExceptionStackExportMode
OpenTelemetry.Exporter.Geneva.ExceptionStackExportMode.ExportAsString = 1 -> OpenTelemetry.Exporter.Geneva.ExceptionStackExportMode
OpenTelemetry.Exporter.Geneva.GenevaExporterOptions.ExceptionStackExportMode.get -> OpenTelemetry.Exporter.Geneva.ExceptionStackExportMode
OpenTelemetry.Exporter.Geneva.GenevaExporterOptions.ExceptionStackExportMode.set -> void
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
// </copyright>

using System;
#if NET6_0_OR_GREATER
using System.Buffers.Binary;
#endif
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
Expand Down Expand Up @@ -62,6 +65,8 @@ internal static class MessagePackSerializer
private const int LIMIT_MAX_FIX_ARRAY_LENGTH = 15;
private const int STRING_SIZE_LIMIT_CHAR_COUNT = (1 << 14) - 1; // 16 * 1024 - 1 = 16383

private const int MAX_STACK_ALLOC_SIZE_IN_BYTES = 256;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int SerializeNull(byte[] buffer, int cursor)
{
Expand Down Expand Up @@ -195,18 +200,27 @@ public static int SerializeUInt64(byte[] buffer, int cursor, ulong value)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int WriteInt16(byte[] buffer, int cursor, short value)
{
#if NET6_0_OR_GREATER
BinaryPrimitives.WriteInt16BigEndian(buffer.AsSpan(cursor), value);
return cursor + sizeof(short);
#else
unchecked
{
buffer[cursor++] = (byte)(value >> 8);
buffer[cursor++] = (byte)value;
}

return cursor;
#endif
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int WriteInt32(byte[] buffer, int cursor, int value)
{
#if NET6_0_OR_GREATER
BinaryPrimitives.WriteInt32BigEndian(buffer.AsSpan(cursor), value);
return cursor + sizeof(int);
#else
unchecked
{
buffer[cursor++] = (byte)(value >> 24);
Expand All @@ -216,11 +230,16 @@ public static int WriteInt32(byte[] buffer, int cursor, int value)
}

return cursor;
#endif
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int WriteInt64(byte[] buffer, int cursor, long value)
{
#if NET6_0_OR_GREATER
BinaryPrimitives.WriteInt64BigEndian(buffer.AsSpan(cursor), value);
return cursor + sizeof(long);
#else
unchecked
{
buffer[cursor++] = (byte)(value >> 56);
Expand All @@ -234,23 +253,33 @@ public static int WriteInt64(byte[] buffer, int cursor, long value)
}

return cursor;
#endif
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int WriteUInt16(byte[] buffer, int cursor, ushort value)
{
#if NET6_0_OR_GREATER
BinaryPrimitives.WriteUInt16BigEndian(buffer.AsSpan(cursor), value);
return cursor + sizeof(ushort);
#else
unchecked
{
buffer[cursor++] = (byte)(value >> 8);
buffer[cursor++] = (byte)value;
}

return cursor;
#endif
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int WriteUInt32(byte[] buffer, int cursor, uint value)
{
#if NET6_0_OR_GREATER
BinaryPrimitives.WriteUInt32BigEndian(buffer.AsSpan(cursor), value);
return cursor + sizeof(uint);
#else
unchecked
{
buffer[cursor++] = (byte)(value >> 24);
Expand All @@ -260,11 +289,16 @@ public static int WriteUInt32(byte[] buffer, int cursor, uint value)
}

return cursor;
#endif
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int WriteUInt64(byte[] buffer, int cursor, ulong value)
{
#if NET6_0_OR_GREATER
BinaryPrimitives.WriteUInt64BigEndian(buffer.AsSpan(cursor), value);
return cursor + sizeof(ulong);
#else
unchecked
{
buffer[cursor++] = (byte)(value >> 56);
Expand All @@ -278,6 +312,7 @@ public static int WriteUInt64(byte[] buffer, int cursor, ulong value)
}

return cursor;
#endif
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down Expand Up @@ -381,6 +416,45 @@ public static int SerializeAsciiString(byte[] buffer, int cursor, string value)
return cursor;
}

#if NET6_0_OR_GREATER

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int SerializeUnicodeString(byte[] buffer, int cursor, ReadOnlySpan<char> value)
{
if (value == null)
{
return SerializeNull(buffer, cursor);
}

int start = cursor;
var cch = value.Length;
int cb;
cursor += 3;
if (cch <= STRING_SIZE_LIMIT_CHAR_COUNT)
{
cb = Encoding.UTF8.GetBytes(value.Slice(0, cch), buffer.AsSpan(cursor));
cursor += cb;
}
else
{
cb = Encoding.UTF8.GetBytes(value.Slice(0, STRING_SIZE_LIMIT_CHAR_COUNT - 3), buffer.AsSpan(cursor));
cursor += cb;
cb += 3;

// append "..." to indicate the string truncation
buffer[cursor++] = 0x2E;
buffer[cursor++] = 0x2E;
buffer[cursor++] = 0x2E;
}

buffer[start] = STR16;
buffer[start + 1] = unchecked((byte)(cb >> 8));
buffer[start + 2] = unchecked((byte)cb);
return cursor;
}

#else

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int SerializeUnicodeString(byte[] buffer, int cursor, string value)
{
Expand Down Expand Up @@ -416,6 +490,8 @@ public static int SerializeUnicodeString(byte[] buffer, int cursor, string value
return cursor;
}

#endif

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int WriteArrayHeader(byte[] buffer, int cursor, int length)
{
Expand Down Expand Up @@ -565,6 +641,18 @@ public static int Serialize(byte[] buffer, int cursor, object obj)
return SerializeUtcDateTime(buffer, cursor, v.ToUniversalTime());
case DateTimeOffset v:
return SerializeUtcDateTime(buffer, cursor, v.UtcDateTime);

#if NET6_0_OR_GREATER
case ISpanFormattable v:
Span<char> tmp = stackalloc char[MAX_STACK_ALLOC_SIZE_IN_BYTES / sizeof(char)];
if (v.TryFormat(tmp, out int charsWritten, string.Empty, CultureInfo.InvariantCulture))
{
return SerializeUnicodeString(buffer, cursor, tmp.Slice(0, charsWritten));
}

goto default;
#endif

default:
string repr = null;

Expand All @@ -583,11 +671,7 @@ public static int Serialize(byte[] buffer, int cursor, object obj)

public static int SerializeSpan(byte[] buffer, int cursor, Span<byte> value)
{
for (int i = 0; i < value.Length; ++i)
{
buffer[cursor++] = value[i];
}

return cursor;
value.CopyTo(buffer.AsSpan(cursor));
return cursor + value.Length;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<Description>An OpenTelemetry .NET exporter that exports to local ETW or UDS</Description>
<Authors>OpenTelemetry Authors</Authors>
<NoWarn>$(NoWarn),NU5104,CS1591,SA1123,SA1310,CA1031,CA1810,CA1822,CA2000,CA2208,SA1204,SA1201,SA1202,SA1308,SA1309,SA1311,SA1402,SA1602,SA1649</NoWarn>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
<TargetFrameworks Condition="$(OS) == 'Windows_NT'">$(TargetFrameworks);net462</TargetFrameworks>
<MinVerTagPrefix>Exporter.Geneva-</MinVerTagPrefix>
<EnableAnalysis>true</EnableAnalysis>
Expand Down

0 comments on commit c49a261

Please sign in to comment.