Skip to content

Commit

Permalink
Release 5.3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
sakno committed Mar 19, 2024
1 parent b20da06 commit faa56e3
Show file tree
Hide file tree
Showing 18 changed files with 255 additions and 39 deletions.
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
Release Notes
====

# 03-19-2024
<a href="https://www.nuget.org/packages/dotnext/5.3.0">DotNext 5.3.0</a>
* Added `StreamSource.AsSharedStream` extension method that allows to obtain read-only stream over memory block which position is local for each consuming async flow or thread. In other words, the stream can be shared between async flows for independent reads.

<a href="https://www.nuget.org/packages/dotnext.metaprogramming/5.3.0">DotNext.Metaprogramming 5.3.0</a>
* Updated dependencies

<a href="https://www.nuget.org/packages/dotnext.unsafe/5.3.0">DotNext.Unsafe 5.3.0</a>
* Updated dependencies

<a href="https://www.nuget.org/packages/dotnext.threading/5.3.0">DotNext.Threading 5.3.0</a>
* Improved performance of `IndexPool.Take` method

<a href="https://www.nuget.org/packages/dotnext.io/5.3.0">DotNext.IO 5.3.0</a>
* Updated dependencies

<a href="https://www.nuget.org/packages/dotnext.net.cluster/5.3.0">DotNext.Net.Cluster 5.3.0</a>
* Smallish performance improvements of WAL

<a href="https://www.nuget.org/packages/dotnext.aspnetcore.cluster/5.3.0">DotNext.AspNetCore.Cluster 5.3.0</a>
* Smallish performance improvements of WAL

# 03-08-2024
<a href="https://www.nuget.org/packages/dotnext/5.2.0">DotNext 5.2.0</a>
* Added `Number.IsPrime` static method that allows to check whether the specified number is a prime number
Expand Down
27 changes: 12 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,31 +44,28 @@ All these things are implemented in 100% managed code on top of existing .NET AP
* [NuGet Packages](https://www.nuget.org/profiles/rvsakno)

# What's new
Release Date: 03-08-2024
Release Date: 03-19-2024

<a href="https://www.nuget.org/packages/dotnext/5.2.0">DotNext 5.2.0</a>
* Added `Number.IsPrime` static method that allows to check whether the specified number is a prime number
* Fixed AOT compatibility issues
<a href="https://www.nuget.org/packages/dotnext/5.3.0">DotNext 5.3.0</a>
* Added `StreamSource.AsSharedStream` extension method that allows to obtain read-only stream over memory block which position is local for each consuming async flow or thread. In other words, the stream can be shared between async flows for independent reads.

<a href="https://www.nuget.org/packages/dotnext.metaprogramming/5.2.0">DotNext.Metaprogramming 5.2.0</a>
<a href="https://www.nuget.org/packages/dotnext.metaprogramming/5.3.0">DotNext.Metaprogramming 5.3.0</a>
* Updated dependencies

<a href="https://www.nuget.org/packages/dotnext.unsafe/5.2.0">DotNext.Unsafe 5.2.0</a>
<a href="https://www.nuget.org/packages/dotnext.unsafe/5.3.0">DotNext.Unsafe 5.3.0</a>
* Updated dependencies

<a href="https://www.nuget.org/packages/dotnext.threading/5.2.0">DotNext.Threading 5.2.0</a>
* Added specialized `IndexPool` data type that can be useful for implementing fast object pools
<a href="https://www.nuget.org/packages/dotnext.threading/5.3.0">DotNext.Threading 5.3.0</a>
* Improved performance of `IndexPool.Take` method

<a href="https://www.nuget.org/packages/dotnext.io/5.2.0">DotNext.IO 5.2.0</a>
<a href="https://www.nuget.org/packages/dotnext.io/5.3.0">DotNext.IO 5.3.0</a>
* Updated dependencies

<a href="https://www.nuget.org/packages/dotnext.net.cluster/5.2.0">DotNext.Net.Cluster 5.2.0</a>
* Fixed [226](https://github.com/dotnet/dotNext/issues/226)
* Fixed [221](https://github.com/dotnet/dotNext/issues/221)
<a href="https://www.nuget.org/packages/dotnext.net.cluster/5.3.0">DotNext.Net.Cluster 5.3.0</a>
* Smallish performance improvements of WAL

<a href="https://www.nuget.org/packages/dotnext.aspnetcore.cluster/5.2.0">DotNext.AspNetCore.Cluster 5.2.0</a>
* Fixed [226](https://github.com/dotnet/dotNext/issues/226)
* Fixed [221](https://github.com/dotnet/dotNext/issues/221)
<a href="https://www.nuget.org/packages/dotnext.aspnetcore.cluster/5.3.0">DotNext.AspNetCore.Cluster 5.3.0</a>
* Smallish performance improvements of WAL

Changelog for previous versions located [here](./CHANGELOG.md).

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Engines;
using BenchmarkDotNet.Order;
using System.Collections.Concurrent;

namespace DotNext.Collections.Concurrent;

[SimpleJob(runStrategy: RunStrategy.Throughput, launchCount: 1)]
[Orderer(SummaryOrderPolicy.Declared)]
public class IndexPoolBenchmark
{
private readonly ConcurrentBag<object> bag = new(CreateObjects());
private readonly object[] objects = CreateObjects();
private IndexPool pool = new();

[Benchmark(Description = "IndexPool Take/Return", Baseline = true)]
public int IndexPoolTakeReturn()
{
pool.TryTake(out var index);
var hashCode = objects[index].GetHashCode();

pool.Return(index);
return hashCode;
}

[Benchmark(Description = "ConcurrentBag Take/Return")]
public int ConcurrentBagTakeReturn()
{
bag.TryTake(out var obj);
var hashCode = obj.GetHashCode();

bag.Add(obj);
return hashCode;
}

private static object[] CreateObjects()
{
var result = new object[64];
Span.Initialize<object>(result);
return result;
}
}
2 changes: 1 addition & 1 deletion src/DotNext.IO/DotNext.IO.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<Authors>.NET Foundation and Contributors</Authors>
<Company />
<Product>.NEXT Family of Libraries</Product>
<VersionPrefix>5.2.0</VersionPrefix>
<VersionPrefix>5.3.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
<AssemblyName>DotNext.IO</AssemblyName>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
Expand Down
2 changes: 1 addition & 1 deletion src/DotNext.Metaprogramming/DotNext.Metaprogramming.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<ImplicitUsings>true</ImplicitUsings>
<IsTrimmable>false</IsTrimmable>
<Features>nullablePublicOnly</Features>
<VersionPrefix>5.2.0</VersionPrefix>
<VersionPrefix>5.3.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
<Authors>.NET Foundation</Authors>
<Product>.NEXT Family of Libraries</Product>
Expand Down
55 changes: 55 additions & 0 deletions src/DotNext.Tests/IO/StreamSourceTests.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System.Buffers;
using System.Runtime.CompilerServices;

namespace DotNext.IO;

using Buffers;
using Runtime.CompilerServices;

public sealed class StreamSourceTests : Test
{
Expand Down Expand Up @@ -602,4 +604,57 @@ static ValueTask WriteToBuffer(ReadOnlyMemory<byte> block, ArrayBufferWriter<byt
Equal(3, stream.Position);
Equal(content, writer.WrittenMemory.ToArray());
}

[Fact]
public static async Task SharedStreamConcurrentReadAsync()
{
byte[] expected = [10, 20, 30, 40, 50, 60];

await using var stream = StreamSource.AsSharedStream(new(expected));

var task1 = ReadStreamAsync(stream);
var task2 = ReadStreamAsync(stream);

await Task.WhenAll(task1, task2);

Equal(expected, task1.Result);
Equal(expected, task2.Result);

[AsyncMethodBuilder(typeof(SpawningAsyncTaskMethodBuilder<>))]
static async Task<byte[]> ReadStreamAsync(Stream source)
{
source.Position = 0L;
await Task.Yield();

var result = new byte[source.Length];
await source.ReadExactlyAsync(result);
return result;
}
}

[Fact]
public static void SharedStreamConcurrentRead()
{
byte[] expected = [10, 20, 30, 40, 50, 60];

using var stream = StreamSource.AsSharedStream(new(expected));

var thread1 = new Thread(ReadStream);
var thread2 = new Thread(ReadStream);

thread1.Start();
thread2.Start();

thread1.Join();
thread2.Join();

void ReadStream()
{
stream.Position = 0L;

var actual = new byte[stream.Length];
stream.ReadExactly(actual);
Equal(expected, actual);
}
}
}
22 changes: 12 additions & 10 deletions src/DotNext.Threading/Collections/Concurrent/IndexPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,19 +73,21 @@ public readonly bool TryPeek(out int result)
/// <seealso cref="Return(int)"/>
public bool TryTake(out int result)
{
ulong current, newValue = Volatile.Read(in bitmask);
do
{
result = BitOperations.TrailingZeroCount(current = newValue);
return TryTake(ref bitmask, maxValue, out result);

if (result > maxValue)
return false;
static bool TryTake(ref ulong bitmask, int maxValue, out int result)
{
var current = Volatile.Read(in bitmask);
for (ulong newValue; ; current = newValue)
{
newValue = current & (current - 1UL); // Reset lowest set bit, the same as BLSR instruction
newValue = Interlocked.CompareExchange(ref bitmask, newValue, current);
if (newValue == current)
break;
}

newValue = current ^ (1UL << result);
return (result = BitOperations.TrailingZeroCount(current)) <= maxValue;
}
while ((newValue = Interlocked.CompareExchange(ref bitmask, newValue, current)) != current);

return true;
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/DotNext.Threading/DotNext.Threading.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<ImplicitUsings>true</ImplicitUsings>
<IsTrimmable>true</IsTrimmable>
<Features>nullablePublicOnly</Features>
<VersionPrefix>5.2.0</VersionPrefix>
<VersionPrefix>5.3.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
<Authors>.NET Foundation and Contributors</Authors>
<Product>.NEXT Family of Libraries</Product>
Expand Down
2 changes: 1 addition & 1 deletion src/DotNext.Unsafe/DotNext.Unsafe.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<IsTrimmable>true</IsTrimmable>
<VersionPrefix>5.2.0</VersionPrefix>
<VersionPrefix>5.3.0</VersionPrefix>
<Features>nullablePublicOnly</Features>
<VersionSuffix></VersionSuffix>
<Authors>.NET Foundation and Contributors</Authors>
Expand Down
2 changes: 1 addition & 1 deletion src/DotNext/DotNext.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<Authors>.NET Foundation and Contributors</Authors>
<Company />
<Product>.NEXT Family of Libraries</Product>
<VersionPrefix>5.2.0</VersionPrefix>
<VersionPrefix>5.3.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
<AssemblyName>DotNext</AssemblyName>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
Expand Down
2 changes: 0 additions & 2 deletions src/DotNext/IO/ReadOnlyStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

namespace DotNext.IO;

using Buffers;

internal abstract class ReadOnlyStream : Stream
{
public sealed override bool CanRead => true;
Expand Down
88 changes: 88 additions & 0 deletions src/DotNext/IO/SharedReadOnlyMemoryStream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using System.Buffers;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace DotNext.IO;

using static Buffers.Memory;

internal sealed class SharedReadOnlyMemoryStream(ReadOnlySequence<byte> sequence) : ReadOnlyStream
{
// don't use BoxedValue due to limitations of AsyncLocal
private readonly AsyncLocal<StrongBox<SequencePosition>> position = new();

[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private SequencePosition LocalPosition
{
get => position.Value?.Value ?? sequence.Start;
set => (position.Value ??= new()).Value = value;
}

private ReadOnlySequence<byte> GetRemainingSequence(out SequencePosition start)
=> sequence.Slice(start = LocalPosition);

public override bool CanSeek => true;

public override long Length => sequence.Length;

public override long Position
{
get => sequence.GetOffset(LocalPosition);
set
{
ArgumentOutOfRangeException.ThrowIfGreaterThan((ulong)value, (ulong)sequence.Length, nameof(value));

LocalPosition = sequence.GetPosition(value);
}
}

public override async Task CopyToAsync(Stream destination, int bufferSize, CancellationToken token)
{
ValidateCopyToArguments(destination, bufferSize);

foreach (var segment in GetRemainingSequence(out _))
await destination.WriteAsync(segment, token).ConfigureAwait(false);

LocalPosition = sequence.End;
}

public override void CopyTo(Stream destination, int bufferSize)
{
ValidateCopyToArguments(destination, bufferSize);

foreach (var segment in GetRemainingSequence(out _))
destination.Write(segment.Span);

LocalPosition = sequence.End;
}

public override void SetLength(long value) => throw new NotSupportedException();

public override int Read(Span<byte> buffer)
{
GetRemainingSequence(out var startPos).CopyTo(buffer, out var writtenCount);
LocalPosition = sequence.GetPosition(writtenCount, startPos);
return writtenCount;
}

public override long Seek(long offset, SeekOrigin origin)
{
var newPosition = origin switch
{
SeekOrigin.Begin => offset,
SeekOrigin.Current => sequence.GetOffset(LocalPosition) + offset,
SeekOrigin.End => sequence.Length + offset,
_ => throw new ArgumentOutOfRangeException(nameof(origin))
};

if (newPosition < 0L)
throw new IOException();

ArgumentOutOfRangeException.ThrowIfGreaterThan(newPosition, sequence.Length, nameof(offset));

LocalPosition = sequence.GetPosition(newPosition);
return newPosition;
}

public override string ToString() => sequence.ToString();
}
12 changes: 12 additions & 0 deletions src/DotNext/IO/StreamSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ public static Stream AsStream(this ReadOnlySequence<byte> sequence)
public static Stream AsStream(this ReadOnlyMemory<byte> memory)
=> AsStream(new ReadOnlySequence<byte>(memory));

/// <summary>
/// Gets read-only stream that can be shared across async flows for independent reads.
/// </summary>
/// <remarks>
/// You need to set a position explicitly before using stream for each parallel async flow.
/// <see cref="Stream.SetLength(long)"/> is not supported to avoid different views of the same stream.
/// </remarks>
/// <param name="sequence">The sequence of bytes.</param>
/// <returns>The stream over sequence of bytes.</returns>
public static Stream AsSharedStream(this ReadOnlySequence<byte> sequence)
=> sequence.IsEmpty ? Stream.Null : new SharedReadOnlyMemoryStream(sequence);

/// <summary>
/// Returns writable synchronous stream.
/// </summary>
Expand Down
4 changes: 2 additions & 2 deletions src/DotNext/Numerics/Number.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ public static bool IsPrime<T>(T value)

return value == two;

// https://math.stackexchange.com/questions/2469446/what-is-a-fast-algorithm-for-finding-the-integer-square-root
// https://math.stackexchange.com/questions/2469446/what-is-a-fast-algorithm-for-finding-the-integer-square-root/4674078#4674078
static T Sqrt(T value)
{
var log2x = T.Log2(value) - T.One;
Expand Down Expand Up @@ -221,4 +221,4 @@ static bool TryGetFromTable(ReadOnlySpan<T> cachedPrimes, T value, out T result)
return success;
}
}
}
}
Loading

0 comments on commit faa56e3

Please sign in to comment.