-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for Memory<T>, Span<T>, ReadOnlySequence<T>, IBufferWriter<T> #9
Comments
@neuecc Expressed some concern about Unity support being compromised if we support this. |
Unity does not have a mechanism like NuGet's dependency solution. However, it is possible to add support for |
Thanks for the heads up. I'm going to experiment with it. I agree, such support goes to the very core of the library and thus would only make sense if it would work on all supported platforms. I'd very much like to see how perf compares before and after such a change on .NET Framework and .NET Core. If it's comparable, it should theoretically maintain higher throughput because there is even less garbage collection on account of not reallocating and copying ever-growing buffers for large data graphs. It sounds like there's nothing fundamentally incompatible with Unity... at worst it would just add an extra dependency that folks would have to chain in. At least, so long as .NET Standard / net45 were supported on that Unity platform. It wouldn't work well (if at all) on .NET 2.0 era. |
I just needed to support Add Span support to MessagePackBinary. static class MessagePackBinary
{
#if! UNITY
public int WriteInt32 (Span <T> ...) {}
# endif
} Also add ref struct |
I'm nearly done with |
Is this so that writer/reader methods don't have to all take the same parameters, but rather can just read from fields?
This problem I've already solved by accepting |
You can see my For now, just to get it to work (which is partially does) I've not taken efforts to keep Unity working (and I don't know how to test it anyway). But I think everything I've done can likely work on Unity as well, in theory. And my changes eliminated some redundant code, so it should help keep things simpler going forward. For example, we no longer have dual paths for handling |
Thanks, amazing design! I've tried minimum version of your impl in Unity, it works(only in Editor, should check on IL2CPP) https://gist.github.com/neuecc/a52979d033a89a4d2c648ca548eba68a This modify has a large breaking change, so if this version mark to 2.*...
It can be the same source code both Core and Unity. |
Yay! And wow, it's amazing how small a minimal version can be. :)
Yes, I expect this to go into a 2.x version due to it including breaking API changes.
My employment at Microsoft will help here, only as far as it helps me learn the ins and outs of NuGet and how to workaround its limitations. I won't need their permission to do fancy stuff with the packaging. But yes, my hope is that we can find a way to make Unity consumption convenient while still working well with the other runtimes.
It's true, .NET Core has performance tuning for I'm down to only 72 test failures out of your 502 unit tests. Working through the rest of the issues may take another day or two, as I have to slot this into my evening time. |
@neuecc Can you check out my
Here are my notes:
What's odd about this is even when the exception is thrown, I can see that the output buffer, trimmed to 511 bytes, correctly has the uncompressed original contents. So why does LZ4 fail? |
If allows to include NuGet dll to unitypackage, I've checked By the way, current your branch is too difficult to build. I solved delete And Nerdbank.GitVersioning shows error,
I've comment out all I have a question,
one more, |
@neuecc , if somebody will create voting issue and more votes will be for instance. Would you agree with that? |
@dzmitry-lahoda |
All good points. It is a little more work to use an instance method than a static method, I agree. But as the serializer is configurable (it used to be configured via a bunch of static fields, which is a non-starter design-wise since it means it can't be used by two consumers with different configuration requirements), we need it to be configurable safely. This can be done either by passing all configuration in as arguments to static methods (which doesn't scale well and undermines the usability you're going for) or it can be on instance fields on the class, which scales much better and conforms to published design guidelines. Also, since you have multiple serializers (regular, LZ4) having two static classes means that every single use of the serializer has to know whether compression should be used since it has to call the right static method. That's not good. Folks should be able to serialize without knowing what they're serializing to and whether it's compressed. Instance methods allow different serializer instances to be provided to this code so that it's compressed or not based on their caller, making it more loosely coupled (another design guideline). So my proposal is to keep the primary serializer API as instance methods, and use type derivation wherever possible to allow polymorphism and loose coupling. We can regain the API ease of static methods by also having a static class, configured with reasonable defaults, that also offer basic serialization functionality. Consider Newtonsoft.Json as a case study here: JsonSerializer is an instance class, but the JsonConvert class is also available with static methods and reasonable defaults (and even extra parameters to allow configuration via those static methods). So it's a nice blend of the two worlds. |
@neuecc Thanks. Passing in 511 instead of 512 indeed fixed the issue. But what's that all about?? I did not expect Anyway, since you record the uncompressed size when writing the compressed data, I can certainly fix this, but is LZ4 itself actually so limited, or is it just this implementation that somehow has this requirement? |
The error you mentioned looks like your git database is incomplete. Did you do a shallow fetch/clone? What does
Pinning the .NET Core SDK is the recommended approach, especially if you have Visual Studio Preview installed on your machine, so that the SDK used to build the repo is fixed, without regard to what exact version of VS or latest .NET Core SDK is installed on your machine. It's very good for servicing some release long after you shipped it because the version of the SDK used to build it back then is used to build the servicing branch no matter how old it is. Otherwise, you end up with a servicing branch you can't build a year or two later because the SDK has changed in a breaking way. |
I have all tests passing! |
@neuecc How do I regenerate |
Never mind. I figured it out. And sent a PR to fix your .tt files: MessagePack-CSharp#370 |
I don't know where are the static fields. I also emphasized "maximizing performance by default". Only When creating an extension point as a framework and making the serializer configurable, It is a problem that the abstraction related to LZ4 can not be solved. Initially I do not think that Newtonsoft.Json's approach is good.
LZ4 has frame format and block format.
I've pulled
Okay, I understand, it is good. Test passed! Great! |
Yes, IIRC it's the
You can't make an app's performance better than the app author does for themselves. Even if you allocate 0 memory, you can't make the app fast if the app itself is allocating memory everywhere. Reuse of the serializer can be the app author's responsibility. If they want to reduce GC pressure they can cache and reuse the serializer if they wish. A single, small allocation for the serializer will not hurt them. If they have a very particular scenario where they make many top-level calls into the serializer at once (which seems like an unlikely requirement), and if GC pressure is holding back their perf, then they can certainly cache the serializer at their option. That's a very small fraction of your (potential) user base however, so we should not optimize the API toward their use case. We can enable them to be efficient (which is simply by their caching of their own serializer instance), but only after we make the API easy to use in a safe and flexible way.
I agree that best design of an app probably only uses your Also, you had 6 copies of your serializer's public API: LZ4, !LZ4, LZ4+NonGeneric, !LZ4+NonGeneric, LZ4+Typeless, !LZ4+Typeless. Because these are all static types, they have to be maintained separately (and I found a few bugs in that regard while consolidating as many as I could into one base class), and it means that every user has to call the right static class instead of simply using an instance class that it may be provided by its caller.
Newtonsoft.Json internally costs a lot in terms of GC pressure, and your MessagePack library has some excellent ways to avoid allocating memory. Some of this took a lot of work (e.g. dynamic code gen, AOT code gen) and I admire your efforts and their results.
Thanks for that.
Yes, I agree it's a bit inconvenient at first. That's why I filed dotnet/sdk#2546 so the .NET Core SDK team can consider improving this.
A reasonable request. However I hesitate to do that because getting |
I don't agree with that. Moreover, such many small improvement has a big meaning. (By the way, Also, since such an API requires a detailed understanding of whether this instance can be cached or not, However, I agree to divide it into an instance and static. In that case, we should give most intuitive api to static api.
And LZ4 defines streaming api.
Assembly binding in .NET is still complicated and difficult to solve. I understand the concern, but I think copying is better. In Unity, the size of the library is very important. |
Is serialize method use
But maybe il emit have to rewrite. |
You know it's funny you mention that. I was just thinking about that yesterday as well. That will avoid a copy of potentially large structs. I'll try to incorporate that. |
Let's leverage the latest types for high performance.
The text was updated successfully, but these errors were encountered: