Closed Andrew900460 closed 3 years ago
From looking at the code, I would suggest taking the CharInfo array and only having 1 allocation of it. So if there is another class which holds "characters", "foregroundColors", and "backgroundColors", put the CharInfo Array in that class as well. Because that will ultimately represent the "final buffer" that is sent to the Windows API.
I'm not sure how you use the Write function outside of the ConsoleWriting.cs file. I'm assuming you would call it once per frame. If that's the case, you are always doing "new CharInfo[]" and allocating a new array every time. So however you choose to do it, you should only allocate that array once. Then you can transfer the characters and colors to that already existing array.
It's kinda odd that WriteConsoleOutput would allocate a string??? In theory WriteConsoleOutput should give best performance, of course. Unless there was another function I've overlooked 1000+ times! But I'm sure that's the one you should use if you want to write blocks of character data. I could run my own performance tests, but I'm very busy with my own project. But If I have a chance I would test it.
Maybe Rider thinks its a String because of the char member variable in CharInfo??? That's all I could imagine.
Anyway, I hope you can get it working, I'm sure there is something up, and this will lead to some nice performance boosts.
Also, from looking at the image you attached. I see a function called "ConsoleWriting.Write", but the first parameter is an array of chars[]. So I am also wondering if Rider thought that was the String?
I don't see that version of the function in the repo so idk.
After some looking, that looks like the same function it's just that you added arguments.
Before I go to bed here finally :P I noticed you just have your arrays of chars and color structs in the renderer class. So you could also just create a CharInfo array there as well and pass that as a parameter, and hopefully, that fixes the allocation issue.
After some time of thinking, I am also wondering If the allocations were caused by the "marshaling" stuff that is going on internally?
I'm wondering if you could use the Span
https://docs.microsoft.com/en-us/dotnet/api/system.span-1?view=net-5.0
// Create a span from native memory.
// Should probably use Marshal.SizeOf( typeof( MY_TYPE ) );
var native = Marshal.AllocHGlobal(100); // change 100 to total bytes that make up the array ( sizeof(type) * arrayLength )
Span<byte> nativeSpan;
unsafe {
nativeSpan = new Span<byte>(native.ToPointer(), 100); // change 100 to the total elements in array buffer ( arrayLength )
}
// access data through
// nativeSpan[index];
Marshal.FreeHGlobal(native);
The only thing is that you would have to alter the function binding so you don't get c# compiler errors. But as long as the underlying data is the same, it should be fine. I mean, at its core, [In] CharInfo[] lpBuffer
is actually just a pointer to the actual array. The difference is that instead of that array being held in "managed c#", it's held in "unmanaged c/c++"
And maybe that will eliminate the slowdowns. Because maybe inside of c# it has to convert the "c# array to a c array".
Anyway!!! Hope this helps.
I ran a benchmark test in my own source code.
I generated 1000 frames of a 64x64 buffer To add some CPU load to each frame, I would randomly generate characters and FG/BG colors for the buffer, before calling the Windows API. I've only created the CharInfo array once beforehand and then reused it each frame. I also ran in release mode in Visual Studio, so it executed with optimized code.
I managed to get 88.6 FPS but that is with a small buffer size (64x64)
This also considers that I am still using the C# array. And it could be the case that it is more performant to use an array that is in unmanaged memory.
EDIT: ok I'm getting way too deep into this
So I did another benchmark, but instead of altering each "buffer pixel" on each frame (because that would be 4096 operations), I instead just altered 1 pixel on each frame. So the visual effect would be that each frame gains a new random colored pixel. So there is less work being done.
That time around I got 311.17 FPS
So it appears based on this result that altering that array of 4096 pixels was very slow for some reason. But WriteConsoleOutput by itself isn't that slow. But maybe I'm wrong. I also just made the buffer have random colors and characters on the first frame, but no changes on the next frames. And it still went at 88.6 FPS. Even though it wasn't doing any new work on each frame, just using the same buffer. But then I just tried sending an empty buffer, all black. And I got super fast FPS. So I think WriteConsoleOutput performs differently when the buffer has more "data variety"??? Which is odd...
interesting. An actually usable version of n8engine is about 99% complete, so when that all gets finished I'll do another fork that I'll experiment with. Thank you for taking the time to do this!
Idk if you saw that, but ignore those "closed and reopened" I misclicked. Anyway.
Also, I wanted to say your welcome, I hope this engine really shines.
Also, I spoke with some other programmers who have a lot more "low-level knowledge" regarding c#. And they said it might've been that C# was doing extra work behind the scenes when the function was being called. So, because the "WriteConsoleOutput" function used a "C# array" that was passed in for the buffer. C# had to do "marshaling", which included allocating an array in the unmanaged memory. Copying all the data, and then substituting for that.
But that can be solved by using C#'s namespace for interacting with native memory.
https://docs.microsoft.com/en-us/dotnet/standard/memory-and-spans/
And you'll probably end up using the "Memory
Only thing is that you need to make sure your project is using either .Net 5.0/higher or .Net Core 2.1/higher.
Awesome! This already uses .NET 5.0 so we're good. The engine is finished enough, so feel free to make another PR if you're still interested in doing this.
hope this helps!