Skip to content

Commit

Permalink
Alternate versions
Browse files Browse the repository at this point in the history
  • Loading branch information
benaadams committed Feb 27, 2018
1 parent bc48bd2 commit 9504657
Show file tree
Hide file tree
Showing 24 changed files with 2,153 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ namespace MemoryLookupBenchmark
/// <summary>Thin wrapper around <see cref="SafeBuffer"/> for slicing large memory blocks.</summary>
/// <typeparam name="T"></typeparam>
[StructLayout(LayoutKind.Sequential)]
internal readonly struct SafeBufferSlice<T>
internal readonly struct BufferSlice<T>
{
private readonly SafeBuffer _buffer;
private readonly long _offset;
private readonly int _length;

internal SafeBufferSlice(SafeBuffer buffer, long offset, int length)
internal BufferSlice(SafeBuffer buffer, long offset, int length)
{
_buffer = buffer;
_offset = offset;
Expand Down
40 changes: 30 additions & 10 deletions MemoryLookupBenchmark/MemoryAccessBenchmark.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using BenchmarkDotNet.Attributes;
using System;
using System.Buffers;
using System.Buffers.ReadOnlySequenceSegment;
using System.Buffers.Current;
using System.Buffers.IMemoryList;
using System.Runtime.InteropServices;

namespace MemoryLookupBenchmark
Expand All @@ -12,7 +14,9 @@ public class MemoryAccessBenchmark
public const long ItemCountMask = ItemCount - 1;

private readonly MemoryMappedFileMemory _memory;
private readonly ReadOnlySequence<byte> _readOnlyBuffer;
private readonly System.Buffers.ReadOnlySequenceSegment.ReadOnlySequence<byte> _readOnlyBufferROSS;
private readonly System.Buffers.IMemoryList.ReadOnlySequence<byte> _readOnlyBufferIMemoryList;
private readonly System.Buffers.Current.ReadOnlySequence<byte> _readOnlyBufferCurrent;
private XorShiftRandom _random;
private readonly IntPtr _constantMemoryPointer;

Expand All @@ -21,7 +25,9 @@ public unsafe MemoryAccessBenchmark()
// Create a memory mapped file and fill it with random bytes
_memory = new MemoryMappedFileMemory(ItemLength * ItemCount);
GenerateRandomBytes(_memory.DataPointer, _memory.Length);
_readOnlyBuffer = _memory.CreateReadOnlyBuffer();
_readOnlyBufferROSS = _memory.CreateReadOnlyBufferROSS();
_readOnlyBufferIMemoryList = _memory.CreateReadOnlyBufferIMemoryList();
_readOnlyBufferCurrent = _memory.CreateReadOnlyBufferCurrent();

// Create the same RNG for both benchmarks
_random = XorShiftRandom.Create(0x6780867534);
Expand Down Expand Up @@ -53,10 +59,10 @@ private static unsafe void GenerateRandomBytes(byte* pointer, long count)
}
}

[Benchmark(Description = "Only generate the random item index.")]
[Benchmark(Description = "Generate random index")]
public long RandomOnly() => (long)(_random.Next() & ItemCountMask) * ItemLength;

[Benchmark(Description = "Generate the random index, and copy static data to the stack using Spans.")]
[Benchmark(Description = "Generate random index, copy static data. Local Span")]
public unsafe long NoRandomAccess()
{
Span<byte> destination = stackalloc byte[ItemLength];
Expand All @@ -65,25 +71,39 @@ public unsafe long NoRandomAccess()
return index;
}

[Benchmark(Baseline = true, Description = "Copy a random item to the stack using a locally generated Span.")]
[Benchmark(Baseline = true, Description = "MM item. Local Span")]
public unsafe void Direct()
{
Span<byte> destination = stackalloc byte[ItemLength];
new ReadOnlySpan<byte>(_memory.DataPointer + (long)(_random.Next() & ItemCountMask) * ItemLength, ItemLength).CopyTo(destination);
}

[Benchmark(Description = "Copy a random item to the stack using the custom implemented SafeBufferSlice<T> struct.")]
[Benchmark(Description = "MM item. BufferSlice<T>")]
public void Custom()
{
Span<byte> destination = stackalloc byte[ItemLength];
_memory.Slice((long)(_random.Next() & ItemCountMask) * ItemLength, ItemLength).Span.CopyTo(destination);
}

[Benchmark(Description = "Copy a random item to the stack using the ReadOnlySequence<T> struct.")]
public void ReadOnlyBuffer()
[Benchmark(Description = "MM item. ReadOnlySequence<T> (current)")]
public void ReadOnlyBufferCurrent()
{
Span<byte> destination = stackalloc byte[ItemLength];
_readOnlyBuffer.Slice((long)(_random.Next() & ItemCountMask) * ItemLength, ItemLength).CopyTo(destination);
_readOnlyBufferCurrent.Slice((long)(_random.Next() & ItemCountMask) * ItemLength, ItemLength).CopyTo(destination);
}

[Benchmark(Description = "MM item. ReadOnlySequence<T> (PR dotnet/corefx#27455)")]
public void ReadOnlyBufferIMemoryList()
{
Span<byte> destination = stackalloc byte[ItemLength];
_readOnlyBufferIMemoryList.Slice((long)(_random.Next() & ItemCountMask) * ItemLength, ItemLength).CopyTo(destination);
}

[Benchmark(Description = "MM item. ReadOnlySequence<T> (PR dotnet/corefx#27499)")]
public void ReadOnlyBufferROSS()
{
Span<byte> destination = stackalloc byte[ItemLength];
_readOnlyBufferROSS.Slice((long)(_random.Next() & ItemCountMask) * ItemLength, ItemLength).CopyTo(destination);
}
}
}
4 changes: 0 additions & 4 deletions MemoryLookupBenchmark/MemoryLookupBenchmark.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,5 @@
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.10.12" />
</ItemGroup>

<ItemGroup>
<Folder Include="corefx\" />
</ItemGroup>

</Project>
10 changes: 7 additions & 3 deletions MemoryLookupBenchmark/MemoryMappedFileMemory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,12 @@ public void Dispose()

public long Length => _dataLength;

public SafeBufferSlice<byte> Slice(long offset, int length) => new SafeBufferSlice<byte>(_buffer, _dataOffset + offset, length);
public BufferSlice<byte> Slice(long offset, int length) => new BufferSlice<byte>(_buffer, _dataOffset + offset, length);

public ReadOnlySequence<byte> CreateReadOnlyBuffer() => SafeBufferOwnedMemory<byte>.CreateReadOnlyBuffer(_buffer, _dataOffset, _dataLength);
}
public System.Buffers.ReadOnlySequenceSegment.ReadOnlySequence<byte> CreateReadOnlyBufferROSS() => ReadOnlySequenceSegment.SafeBufferOwnedMemory<byte>.CreateReadOnlyBuffer(_buffer, _dataOffset, _dataLength);

public System.Buffers.IMemoryList.ReadOnlySequence<byte> CreateReadOnlyBufferIMemoryList() => IMemoryList.SafeBufferOwnedMemory<byte>.CreateReadOnlyBuffer(_buffer, _dataOffset, _dataLength);

public System.Buffers.Current.ReadOnlySequence<byte> CreateReadOnlyBufferCurrent() => Current.SafeBufferOwnedMemory<byte>.CreateReadOnlyBuffer(_buffer, _dataOffset, _dataLength);
}
}
2 changes: 1 addition & 1 deletion MemoryLookupBenchmark/corefx/SequencePosition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//using System.Numerics.Hashing;
using System.ComponentModel;

namespace System
namespace System.Buffers
{
/// <summary>
/// Represents position in non-contiguous set of memory.
Expand Down
20 changes: 17 additions & 3 deletions MemoryLookupBenchmark/corefx/ThrowHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using System.Buffers;
using System.Runtime.Serialization;

namespace System
namespace System.Buffers
{
//
// This pattern of easily inlinable "void Throw" routines that stack on top of NoInlining factory methods
Expand Down Expand Up @@ -130,10 +130,24 @@ public static bool TryParseThrowFormatException<T>(out T value, out int bytesCon
//
// ReadOnlySequence .ctor validation Throws coalesced to enable inlining of the .ctor
//
public static void ThrowArgumentValidationException<T>(ReadOnlySequenceSegment<T> startSegment, int startIndex, ReadOnlySequenceSegment<T> endSegment)
public static void ThrowArgumentValidationException<T>(System.Buffers.IMemoryList.IMemoryList<T> startSegment, int startIndex, System.Buffers.IMemoryList.IMemoryList<T> endSegment)
=> throw CreateArgumentValidationException(startSegment, startIndex, endSegment);

private static Exception CreateArgumentValidationException<T>(ReadOnlySequenceSegment<T> startSegment, int startIndex, ReadOnlySequenceSegment<T> endSegment)
private static Exception CreateArgumentValidationException<T>(System.Buffers.IMemoryList.IMemoryList<T> startSegment, int startIndex, System.Buffers.IMemoryList.IMemoryList<T> endSegment)
{
if (startSegment == null)
return CreateArgumentNullException(ExceptionArgument.startSegment);
else if (endSegment == null)
return CreateArgumentNullException(ExceptionArgument.endSegment);
else if ((uint)startSegment.Memory.Length < (uint)startIndex)
return CreateArgumentOutOfRangeException(ExceptionArgument.startIndex);
else
return CreateArgumentOutOfRangeException(ExceptionArgument.endIndex);
}
public static void ThrowArgumentValidationException<T>(System.Buffers.ReadOnlySequenceSegment.ReadOnlySequenceSegment<T> startSegment, int startIndex, System.Buffers.ReadOnlySequenceSegment.ReadOnlySequenceSegment<T> endSegment)
=> throw CreateArgumentValidationException(startSegment, startIndex, endSegment);

private static Exception CreateArgumentValidationException<T>(System.Buffers.ReadOnlySequenceSegment.ReadOnlySequenceSegment<T> startSegment, int startIndex, System.Buffers.ReadOnlySequenceSegment.ReadOnlySequenceSegment<T> endSegment)
{
if (startSegment == null)
return CreateArgumentNullException(ExceptionArgument.startSegment);
Expand Down
94 changes: 94 additions & 0 deletions MemoryLookupBenchmark/corefxCurrent/BuffersExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace System.Buffers.Current
{
/// <summary>
/// Extension methods for <see cref="ReadOnlySequence{T}"/>
/// </summary>
public static class BuffersExtensions
{
/// <summary>
/// Returns position of first occurrence of item in the <see cref="ReadOnlySequence{T}"/>
/// </summary>
public static SequencePosition? PositionOf<T>(this ReadOnlySequence<T> sequence, T value) where T : IEquatable<T>
{
SequencePosition position = sequence.Start;
SequencePosition result = position;
while (sequence.TryGet(ref position, out var memory))
{
var index = memory.Span.IndexOf(value);
if (index != -1)
{
return sequence.GetPosition(result, index);
}
result = position;
}
return null;
}

/// <summary>
/// Copy the <see cref="ReadOnlySequence{T}"/> to the specified <see cref="Span{Byte}"/>.
/// </summary>
/// <param name="sequence">The source <see cref="ReadOnlySequence{T}"/>.</param>
/// <param name="destination">The destination <see cref="Span{Byte}"/>.</param>
public static void CopyTo<T>(this ReadOnlySequence<T> sequence, Span<T> destination)
{
if (sequence.Length > destination.Length)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.destination);

foreach (var segment in sequence)
{
segment.Span.CopyTo(destination);
destination = destination.Slice(segment.Length);
}
}

/// <summary>
/// Converts the <see cref="ReadOnlySequence{T}"/> to an array
/// </summary>
public static T[] ToArray<T>(this ReadOnlySequence<T> sequence)
{
var array = new T[sequence.Length];
sequence.CopyTo(array);
return array;
}

/// <summary>
/// Writes contents of <paramref name="source"/> to <paramref name="bufferWriter"/>
/// </summary>
public static void Write<T>(this IBufferWriter<T> bufferWriter, ReadOnlySpan<T> source)
{
Span<T> destination = bufferWriter.GetSpan();

// Fast path, try copying to the available memory directly
if (source.Length <= destination.Length)
{
source.CopyTo(destination);
bufferWriter.Advance(source.Length);
return;
}

WriteMultiSegment(bufferWriter, source, destination);
}

private static void WriteMultiSegment<T>(IBufferWriter<T> bufferWriter, ReadOnlySpan<T> source, Span<T> destination)
{
while (true)
{
int writeSize = Math.Min(destination.Length, source.Length);
source.Slice(0, writeSize).CopyTo(destination);
bufferWriter.Advance(writeSize);
source = source.Slice(writeSize);
if (source.Length > 0)
{
destination = bufferWriter.GetSpan(source.Length);
continue;
}

return;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace System.Buffers
namespace System.Buffers.Current
{
/// <summary>
/// Represents a <typeparam name="T"/> sink
Expand Down
27 changes: 27 additions & 0 deletions MemoryLookupBenchmark/corefxCurrent/IMemoryList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace System.Buffers.Current
{
/// <summary>
/// Represents a linked list of <see cref="Memory{T}"/> nodes.
/// </summary>
public interface IMemoryList<T>
{
/// <summary>
/// The <see cref="Memory{T}"/> value for current node.
/// </summary>
Memory<T> Memory { get; }

/// <summary>
/// The next node.
/// </summary>
IMemoryList<T> Next { get; }

/// <summary>
/// The sum of node length before current.
/// </summary>
long RunningIndex { get; }
}
}
Loading

0 comments on commit 9504657

Please sign in to comment.