Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice

Allocation-free JSON UnityWebRequest uploads

Discussion in '5.3 Beta' started by kae, Oct 5, 2015.

  1. kae

    kae

    Joined:
    Sep 27, 2012
    Posts:
    12
    We'd like to be able to upload data to a server with as little performance impact on our application as possible and I see an opportunity to do this with the new JsonUtility and UnityWebRequest, given that they're implemented natively. The goal is to take a serializable C# object and send it to a web server with as few C# heap allocations as possible, preferably none, but just avoiding the payload allocation will be a big step. I believe the current APIs are almost there, they just need a couple of tweaks.

    I'll start with the ultimate API for us, which would be simple but I recognize that there are some things about it you probably won't like.
    void UnityWebRequest.Post(string uri, object obj, DownloadHandler handler, System.Action<long/* status code */, string/* error */> onDone);
    Serializes the object with JsonUtility, POSTs it to the url and calls the provided callback with the result without any C# heap allocations. The error string is only allocated when an error occurs.

    So I'll also provide a few suggestions to tweak the current APIs in less drastic ways.

    The new JSON serialization API goes a long way towards eliminating allocations and could be entirely allocation-free with a simple change to the API.
    I suggest adding the following overloads to the JsonUtility.ToJson method.
    int JsonUtility.ToJson(object obj, bool prettyPrint, byte[] buffer, int offset, int maxOutputBytes);
    with corresponding *Async versions. The output should be UTF8 encoded. Returns 0 if the buffer is not large enough.
    A System.IO.Stream API would also be welcome of course.

    This will allow an application to reuse C# buffers for storing the JSON data, allowing it to be sent over the network or serialized to disk without any allocations.


    The UnityWebRequest API should provide a way of supplying POST and header data from byte[] or char[] arrays with an offset and count.

    Could be as simple as adding an overload to Post
    UnityWebRequest UnityWebRequest.Post(string uri, byte[] buffer, int offset, int count);

    But another thing that would be great would be to replace or build upon the UploadHandler API to be able to supply an array with offset and count. Would mean it has to be opened up for extension by users too.
    The current byte[] data property API requires an allocation for each upload. I suggest replacing with/adding
    1. System.IO.Stream API for reading data, allowing streaming off files too.
    2. virtual byte[] GetData(out int offset, out int byteCount); -- could fallback to the current property API if it returns null.

    With these changes I'd be able to POST arbitrary C# objects and only allocate the UnityWebRequest and AsyncOperation on the heap.
     
    mh114 likes this.
  2. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,658
    Tighter integration between JsonUtility and WebRequest is definitely on the list :)

    Wouldn't a more natural choice be for us to offer specialised JsonUploadHandler and JsonDownloadHandler classes which use JsonUtility under the hood (but all in C++, so no GC heap allocations)? So you'd do something like:

    Code (csharp):
    1.  
    2. var req = UnityWebRequest.Post("http://my.service");
    3. req.uploadHandler = new UploadHandlerJson(obj); // maybe created at startup and reused instead
    4. req.Send();
    5.  
    Possibly it would make sense to add a convenience method like PostJson() for combining the first two lines (and eliminating the need to allocate an UploadHandlerJson on the managed heap), but even with the above you'd have changed the overhead into a small constant amount per request instead of it being a function of the object size.
     
    polytropoi likes this.
  3. kae

    kae

    Joined:
    Sep 27, 2012
    Posts:
    12
    Great to hear.

    It's of course great to have the JsonUploadHandler & JsonDownloadHandler, but it will only work for sending JSON with UnityWebRequest. There are many use cases where sending JSON makes sense, like over XMPP or any other custom protocol and it'd be a shame if there's no allocation-free access to the results for these.

    Yep it's a great improvement and I think allocation-free APIs are something that Unity should be thinking hard about going into the future. On that note, another solution is to support recycling the managed heap objects.

    Supply a reset function to discard internal native state and making the instance usable for sends again.
    void UnityWebRequest.Reset();

    This is a solution that can be used for all UnityWebRequests and doesn't just reduce allocations in this specific JSON case.
     
  4. Prodigga

    Prodigga

    Joined:
    Apr 13, 2011
    Posts:
    1,123
    +1