SceneGate / Yarhl

Framework for the implementation of format converters like game assets or media files
https://scenegate.github.io/Yarhl/
MIT License
60 stars 10 forks source link

Add Stream argument constructor to DataStream and remove IStream interface #170

Closed pleonex closed 2 years ago

pleonex commented 2 years ago

Description

Remove the interface IStream used for custom stream implementations that works with DataStream. Now every Stream implementation can be used directly with a DataStream. This includes DataStream itself as a sub-stream as it was before. We are adding a new constructor that takes Stream directly, this simplifies the implementation and usage.

As a indirect consequence, the performance increases in some cases as there isn't anymore an indirect interface or base class (StreamWrapper) layer to use regular streams.

There is a small fix where the methods Read and Write of DataWriter now throw an exception if the cout and index of the buffer are out of bound even if the count is zero.

We keep the StreamWrapper layer to facilitate the implementation of custom streams like RecyclableMemoryStream and LazyFileStream. This wrapper layer is just a basic implementation of Stream that forwards every method to the base stream methods unless they are overwritten in the implementation. The StreamWrapper now is abstract as it didn't make sense to use without an implementation on top.

Migration

Unless you have implemented a custom stream that works with DataStream, you don't need to do anything. Otherwise, replace the implementation of IStream with Stream.

Example

Now we can create a DataStream object directly from a Stream. These methods are equivalent.

var baseStream = new MemoryStream();
var stream1 = DataStreamFactory.FromStream(baseStream);
var stream2 = new DataStream(baseStream);

We can also do the same as before from another DataStream (as it inherits from Stream). Create a custom implementation of Stream and using it with a DataStream is easier as there isn't anymore the IStream.

Performance

In general there is an increase of performance using DataStream.

BenchmarkDotNet=v0.12.1, OS=fedora 33 Intel Core i7-4720HQ CPU 2.60GHz
(Haswell), 1 CPU, 8 logical and 4 physical cores .NET Core SDK=5.0.206 [Host] :
.NET Core 5.0.9 (CoreCLR 5.0.921.41201, CoreFX 5.0.921.41201), X64 RyuJIT
DefaultJob : .NET Core 5.0.9 (CoreCLR 5.0.921.41201, CoreFX 5.0.921.41201), X64
RyuJIT

Before:

Method StreamType DataStreamWrapper Mean Error StdDev
ReadByte MemoryStream False 5.618 ns 0.0286 ns 0.0253 ns
Read MemoryStream False 50.936 ns 0.3792 ns 0.3547 ns
WriteByte MemoryStream False 7.722 ns 0.0892 ns 0.0834 ns
Write MemoryStream False 66.885 ns 0.3188 ns 0.2826 ns
ReadByte MemoryStream True 35.917 ns 0.2609 ns 0.2313 ns
Read MemoryStream True 104.139 ns 0.4752 ns 0.4445 ns
WriteByte MemoryStream True 42.643 ns 0.4005 ns 0.3550 ns
Write MemoryStream True 106.719 ns 0.7025 ns 0.5484 ns
ReadByte RecyclableStream False 19.478 ns 0.1310 ns 0.1225 ns
Read RecyclableStream False 69.449 ns 1.1175 ns 1.0453 ns
WriteByte RecyclableStream False 22.947 ns 0.1235 ns 0.1031 ns
Write RecyclableStream False 90.525 ns 0.5216 ns 0.4879 ns
ReadByte RecyclableStream True 51.016 ns 0.3553 ns 0.3150 ns
Read RecyclableStream True 114.910 ns 0.9471 ns 0.8859 ns
WriteByte RecyclableStream True 56.484 ns 0.3262 ns 0.3052 ns
Write RecyclableStream True 131.142 ns 0.5610 ns 0.4684 ns
ReadByte FileStream False 1,195.551 ns 9.4209 ns 8.8123 ns
Read FileStream False 1,421.273 ns 6.9803 ns 5.8288 ns
WriteByte FileStream False 1,926.757 ns 17.4820 ns 16.3526 ns
Write FileStream False 1,987.028 ns 34.7790 ns 30.8307 ns
ReadByte FileStream True 1,235.733 ns 7.1921 ns 6.7275 ns
Read FileStream True 1,422.329 ns 28.1334 ns 32.3985 ns
WriteByte FileStream True 1,940.800 ns 13.5741 ns 12.6972 ns
Write FileStream True 2,085.611 ns 11.8179 ns 11.0545 ns

After:

Method StreamType DataStreamWrapper Mean Error StdDev
ReadByte MemoryStream False 5.615 ns 0.0310 ns 0.0274 ns
Read MemoryStream False 50.931 ns 0.3233 ns 0.2866 ns
WriteByte MemoryStream False 7.599 ns 0.0470 ns 0.0440 ns
Write MemoryStream False 67.658 ns 0.4350 ns 0.3856 ns
ReadByte MemoryStream True 26.734 ns 0.0775 ns 0.0725 ns
Read MemoryStream True 82.511 ns 0.3274 ns 0.3063 ns
WriteByte MemoryStream True 36.855 ns 0.2390 ns 0.1996 ns
Write MemoryStream True 91.804 ns 0.4821 ns 0.4509 ns
ReadByte RecyclableStream False 19.254 ns 0.1268 ns 0.1186 ns
Read RecyclableStream False 69.515 ns 0.5252 ns 0.4656 ns
WriteByte RecyclableStream False 23.000 ns 0.1206 ns 0.1128 ns
Write RecyclableStream False 90.470 ns 0.2843 ns 0.2374 ns
ReadByte RecyclableStream True 43.524 ns 0.4597 ns 0.4075 ns
Read RecyclableStream True 97.164 ns 0.5199 ns 0.4609 ns
WriteByte RecyclableStream True 54.035 ns 0.2104 ns 0.1865 ns
Write RecyclableStream True 114.332 ns 0.8987 ns 0.7967 ns
ReadByte FileStream False 1,195.836 ns 8.2167 ns 6.8613 ns
Read FileStream False 1,413.611 ns 26.9350 ns 38.6294 ns
WriteByte FileStream False 5,187.820 ns 149.4479 ns 440.6504 ns
Write FileStream False 5,506.622 ns 193.9094 ns 571.7463 ns
ReadByte FileStream True 1,220.803 ns 8.2913 ns 7.3500 ns
Read FileStream True 1,362.205 ns 9.1546 ns 8.1154 ns
WriteByte FileStream True 5,288.271 ns 129.1510 ns 380.8046 ns
Write FileStream True 5,683.529 ns 163.6561 ns 479.9751 ns