1

Just want to highlight that I have been at this for about a week and am new to dealing with the heap and pointers and have not had to interact directly with it before. Could really use some help as I have not made any progress.

Problem statement: Need to access an object[] that I created on Blazor side from Javascript without the use of JSUnmarshalled call from Blazor to pass the data in.

Why? Using the JSUnmarshalled call is still too slow for how large the object[] is even though it only takes .25ms (yes .25ms) to actually create the entire array. The array takes 2 seconds to pass to JS. This is a problem because I am rapidly making adjustments based on user input and most time is spent on waiting for the data to be passed over which causes poor user experience.

What I am currently doing:

c# side

JSUnmarshalledRuntime.InvokeUnmarshalled<object[], object>("InitializeGeometry", RenderModel.GetCombined());

InitializeGeometry calls this JS function to process the object[]

JS side

function getInt32Heap(data) {
var m = data + 12;
var r = Module.HEAP32[m >> 2]
return Array.from(new Int32Array(Module.HEAP32.buffer, m + 4, r));}

This is working fine but is quite slow. What I have tried to find is a way to create a pointer for the object[] on the Blazor side and then pass it into JS. Then on the JS side figure out how to use that pointer to get the same data out that I am currently getting.

I have reached out to the .Net team and this is my original question. https://github.com/dotnet/aspnetcore/issues/41490

Here is what I have looked through https://github.com/dotnet/aspnetcore/blob/main/src/Components/Web.JS/src/Rendering/RenderBatch/SharedMemoryRenderBatch.ts

Also found this https://github.com/SteveSandersonMS/BlazorInputFile/blob/db10236b049ea05de28de9b7806447a3c84c8465/BlazorInputFile/wwwroot/inputfile.js#L113

And this https://blog.elmah.io/using-js-object-references-in-blazor-wasm-to-wrap-js-libraries/?msclkid=9826568bcf8d11ecbdab91d116441786

Again, I am new to this so the answers may be in here but I am not seeing them. I know this is due to my lack of understanding but so far I have not read anything that puts things into perspective for me. Any help would be greatly appreciated!

Mike Ruch
  • 11
  • 1
  • Alternatively, you could try and only send the parts of the huge object array that are necessary, or implement the `InitializeGeometry` method in C#/ WASM (or the pass the `RenderModel` to JS and let it `GetCombined()`), or debounce the input, so that even if the user is rapidly adjusting inputs the `RenderModel.GeCombined` is only called X (milli)seconds after the last user input (or move the 2s operation to be manually invoked). I don't think passing pointers around is a particularly good idea, as there's no (at least to my knowledge) way of pinning an object in C# memory – MindSwipe May 09 '22 at 14:25
  • Why not use JSON to pass the object and serialize it? `JsonConvert.SerializeObject(T);` then deserialize it on the js side? – MX313 May 09 '22 at 14:35
  • @MindSwipe already on this path. The big issue is the amount of data from the objects that don't change is so small it is in the kb. I am generating data for THREEJS to render 3D mesh data. So it is a parametric shape that changes every point in the main array. The reason I think the pointer would work is I am assuming that the c# memory would not change the pointer between the next control change happening by the user. Each control change I would send the updated pointer reference. Is my assumption correct? Live render is part of the selling point of the software. – Mike Ruch May 10 '22 at 01:30
  • 1
    @MX313 I am currently using the JSUnmarshalled call as this does not take the time to serialize it. If I seralize the call instead of passing the byte array the performance is about 10 times worse. – Mike Ruch May 10 '22 at 01:31
  • Your assumption is wrong. C# is a garbage collected language, and the garbage collector can do what it pleases, for all intents and purposes it's a black box. Which is a good thing. You could get a pointer to a location in memory, share it JS, but then mere milliseconds later the GC may decide to stop the execution of C# entirely and do a global garbage collection, and that could (most likely will) move objects around and as such invalidating your pointer – MindSwipe May 10 '22 at 06:04
  • P.S I was actually wrong, there are ways of pinning an object in memory, the [`fixed`](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/fixed-statement) statement and [`GCHandle.Alloc`](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.gchandle.alloc?redirectedfrom=MSDN&view=net-6.0#System_Runtime_InteropServices_GCHandle_Alloc_System_Object_) and the subsequent [`GCHandle.AddrOfPinnedObject`](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.gchandle.addrofpinnedobject?view=net-6.0) – MindSwipe May 10 '22 at 06:10
  • Serializing the object into a JSON string should be very fast. The performance issue I see here is around your JSU call. This is a design question in general then.. I would try using something like REDIS instead. Blazor JSON -> Redis -> Javascript. This should be lightening fast, I have transferred and read very large object structures at fast intervals in this manner. Your object structure itself may also need a redesign, especially if only small aspects are changing in each iteration. – MX313 May 10 '22 at 12:02
  • @MindSwipe this sounds like what I am looking for. I can't seem to find an example of passing a pointer to JS from c# and then finding it in a JS function. Do you have any reference or know how to do this by chance? – Mike Ruch May 10 '22 at 13:07
  • A quick google gave me [this](https://stackoverflow.com/a/44092498/9363973), especially the last paragraph is interesting. Can't help you more though, seeing as how I've never done anything with WASM, haven't used JS in years and don't have a local environment to test things out – MindSwipe May 10 '22 at 13:10

0 Answers0