|
Totally agree. Machines aren't what they were. Take the hit. The code is already managed.
Besides, also I'd rather something slower and regular than something faster that spikes here and there if i needed raw performance. Consistency in streaming data is usually a bit more important than raw throughput but YMMV depending on the scenario and all of course, simply my opinion. I think it applies to running code as well.
When I was growin' up, I was the smartest kid I knew. Maybe that was just because I didn't know that many kids. All I know is now I feel the opposite.
|
|
|
|
|
User the right tool for the right job.
|
|
|
|
|
That's why I use C++ for this.
But it would be nice to have other options.
When I was growin' up, I was the smartest kid I knew. Maybe that was just because I didn't know that many kids. All I know is now I feel the opposite.
|
|
|
|
|
|
codewitch honey crisis wrote: universal garbage collection in .NET Now caught on camera[^]
Somehow that was the first thing that came to mind
|
|
|
|
|
seems accurate.
it even kinda looks like HAL
When I was growin' up, I was the smartest kid I knew. Maybe that was just because I didn't know that many kids. All I know is now I feel the opposite.
|
|
|
|
|
You might want to look at what Unity is doing with what they're calling High Performance C#. AIUI a big part is limiting themselves to stack allocations and language features that don't do heap allocations in the background to get C++ish equivalent performance/consistency by avoiding ever triggering GCs. You probably don't need the verifiably vectorized capabilities they've also built into their toolchain; but borrowing an off the shelf solution might be easier than doing the parts you need on your own.
C++, C# and Unity
Did you ever see history portrayed as an old man with a wise brow and pulseless heart, weighing all things in the balance of reason?
Is not rather the genius of history like an eternal, imploring maiden, full of fire, with a burning heart and flaming soul, humanly warm and humanly beautiful?
--Zachris Topelius
Training a telescope on one’s own belly button will only reveal lint. You like that? You go right on staring at it. I prefer looking at galaxies.
-- Sarah Hoyt
|
|
|
|
|
what what?
ooooh. I'll definitely check it out. it sounds interesting.
When I was growin' up, I was the smartest kid I knew. Maybe that was just because I didn't know that many kids. All I know is now I feel the opposite.
|
|
|
|
|
Let us know what you find. From what I've read I think they're just limiting what language features they use to control memory allocations, but I've just read a few blogs and never tried anything.
Did you ever see history portrayed as an old man with a wise brow and pulseless heart, weighing all things in the balance of reason?
Is not rather the genius of history like an eternal, imploring maiden, full of fire, with a burning heart and flaming soul, humanly warm and humanly beautiful?
--Zachris Topelius
Training a telescope on one’s own belly button will only reveal lint. You like that? You go right on staring at it. I prefer looking at galaxies.
-- Sarah Hoyt
|
|
|
|
|
They appear to have some new syntax, among them "#include". Not sure if it's limited to a preprocessor or not, but they're talking about rendering to machine code, not IL
When I was growin' up, I was the smartest kid I knew. Maybe that was just because I didn't know that many kids. All I know is now I feel the opposite.
|
|
|
|
|
Being thoroughly experienced in C++, C#, and near-real-time programming, I've always been bemused by the complaints of performance issues related to garbage collection in .NET. I've never had a performance concern in my C# code that I could attribute to the GC taking over the machine. It's always been thread contention between resources, poorly thought-out locks, misuse of .NET facilities, or something similar.
My opinion is that if your performance constraints are that tight, you shouldn't be using a garbage-collected language anyway. You need C or C++ and native threading constructs so that your control is as close to the bare metal as possible.
Software Zen: delete this;
|
|
|
|
|
Oh if you've ever tried to realtime audio apps you'll find out real quick.
You can't even play midi on your own using midiout/send at anything 96ppm or above without GC forcing you to drop frames or lag periodically.
suspend the GC like 4+ allows you to, the problem disappears, but you have to reserve reams of heap to do it.
I think your lack of running into the problem may be more due to the lack of trying to do anything crazy like that with C#?
I wouldn't have even tried, *precisely because* of my experience with C++ and RTOS apps except i was looking into the GC issue and looking at how realistic .NET 4+ suspend GC/critical region feature was to use in practice, so i developed some realtime code to test it. something sensitive. Ears are more time sensitive than eyes, so I wrote a midi player.
I've tested the results. I *could* post the project here just to settle the point, but it seems a lot of work just to do this.
When I was growin' up, I was the smartest kid I knew. Maybe that was just because I didn't know that many kids. All I know is now I feel the opposite.
|
|
|
|
|
The folks & Unity have been working a lot on a performance-oriented subset of C#. I don't know exactly how tailored their solution is to real-time application, but since a game engine has to draw 60 frames a second without skipping too many of them, you may find something interesting there.
|
|
|
|
|
codewitch honey crisis wrote: I've always been kind of bummed about the universal garbage collection in .NET because you can't do realtime coding with a GC running in the background. Windows is not a real-time OS.
codewitch honey crisis wrote: *gasp* Delphi, which is costlier/more time consuming to write solid code with. I find .NET more verbose than Delphi.
Want realtime, check out QNX.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
"If you just follow the bacon Eddy, wherever it leads you, then you won't have to think about politics." -- Some Bell.
|
|
|
|
|
i should have qualified that. everyone is getting on me about that.
I don't care about RTOS stuff.
I care about being able to play live music. Realtime enough for that.
When I was growin' up, I was the smartest kid I knew. Maybe that was just because I didn't know that many kids. All I know is now I feel the opposite.
|
|
|
|
|
You will never have true realtime. There are always unexpected delays, from cache misses to virtual memory paging operations. The systems getting closest to RT are the old ones: The first machine I laid my hands on was a 1976 design 16-bit mini (i.e. the class of PDP-11): It had 16 hardware interrupt levels - that is, 16 complete sets of hardware registers, so when an interrupt occured at a higher level than the one currently executing, the register bank was switched in an the first instruction executed 900 ns after the interrupt signal was received. That was certainly impressive in 1976, but the CPU was hard logic throughout, with no nasty pipelines to be emptied, no microcode loops that had to terminate, usually you had no paging or virtual memory. That got you close to RT. You won't see anything that compares today, even without GC.
In those days, people got shivers from the though of writing any system software in any high level language. Lots of people shook their heads in disbelief over Unix written in K&R C: It could never work, could never give good enough performance. I have saved a printout of a long discussion from around 1995, when NetNews was The Social Media: This one guy who insisted, at great length, that high level languages was nothing but a fad that would soon go away; they will never give high enough performance. (In 1995, he didn't get much support from the community, but he never gave in to the pressure.)
Using a high level language takes you one step away from RT. Using a machine with virtual memory is another step (even if your program is fixed in memory - the OS may be busy managing memory for other processes, with interrupts disabled). Dependency on pipelines and cache hit rates is yet another step. If you require really hard RT, you must stay away from such facilities - probably from any modern CISC CPU.
You probably do not have hard RT requirements; you can live with a cache miss, or the OS updating page table for other processes. You should design your code to be able to handle as large random delays as possible. Networking guys know the techniques, like window mechanisms and elastic buffers. If you do things the right way, I am not willing to believe that a modern multi-GHz six-core CPUs ability to run a VST plugin sufficiently close to RT is ruined by the CRT GC!
I grew up with high level languages, but knowing that RT "had to" be done in assembly. But soon I also learned how smart optimizing compilers can be, and gave up my belief in assembly. I much longer thought that I would have to manage the heap myself, if performance was essential. I switched to C#, where it isn't an option, and then came across a fairly good description of the CLR CG (in "CLR via C#"). Again and again I said to myself: Hey, that's a neat trick! or: I never though of that in my heap management! ... Twenty years earlier I concluded that a compiler is far smarter than I am in generating code. Now Irealized that CLR is far smarter than I am in managing memory. And I never had any performance problems in C#.
You sure can manage memory yourself, even in C#. As a graduate student I was a TA helping freshmen through their first programming course. The "hard" engineering disciplines still used Fortran, the rest had switched to Pascal. Exercises were common to both languages, except for one: The Pascal version was a linked list problem. One EE student was offended: Why can't we learn about this pointer stuff, like all the others? I gave her an introduction to pointers ... and next week she returned with a solution to linked list problem, written in Fortran, the heap as a big 2D array, pointers being integer indexes of the next object (i.e. array row) in the list.
You can do something similar: At program startup, allocate a large byte or integer array, cast any slice of the array into any class when referencing an object, cast from an arbitrary object to an array slice when allocating or modifying an object. As if you were writing your own memory manager in assembly language (except then you wouldn't have to do all that casting). I am sure that you could find a lot of smart strategies in the description of the CLR memory management
For being a little more serious: I would very much like to see a C++ solution and a C# solution side-by-side, everything being identical with the exception of the heap management. And then have a demonstration that the C# solution has untolerable hickups every time the GC makes a round of cleanup. I doubt that I will ever have that experience.
What probably would happen is that in the rewriting from C# to C++, experience from the C# work allows you to write better, more efficient code in C++ - improvements that might have been carried back to the C# version, but it isn't. So there are far more differences in the two alternatives than just the heap management.
One example showing this effect: When IBM developed its first RISC chip, the 801, at a conference they presented a paper on the speed increase made possible by the large register file of the 801, requiring new compiler optimization techniques. Then, more or less as a side remark, the presenter told that they had carried back those optimzation methods to the compiler for the 360/370 architecture, and gained a 30% speed increase ... without any extended register file. But that was only an informal experiment, so for the rest of the presentation, all performance gains on the RISC was ascribed to the RISC architecture, including the 30% which could be obtained on a quite different CISC architecture.
So if a C++ implementation with "handwritten" heap management runs more smoothly than a C# one with automatic heap management, I would suspect that it has more to do with "new optimization techniques" until I have compared the two, line by line, as well as the compiler options.
|
|
|
|
|
See my other reply where i said in this case i don't care about it being technically an RTOS
I care about being able to play live music with it
When I was growin' up, I was the smartest kid I knew. Maybe that was just because I didn't know that many kids. All I know is now I feel the opposite.
|
|
|
|
|
All I am saying is: If you can't play your music, it is not the fault of the GC. I am not willing to believe that.
|
|
|
|
|
LOL
yeah dude, it is.
i've tested it.
and i've tested it again by using the critical region feature of .NET 4 to suspend the GC
so you can believe whatever you want.
I'll believe my tests, thanks.
When I was growin' up, I was the smartest kid I knew. Maybe that was just because I didn't know that many kids. All I know is now I feel the opposite.
|
|
|
|
|
|
Yes. As a matter of fact. I'm saying that. It's one of the reasons I abandoned it and went back to writing them in C++
Maybe if you have an 8 core monster the lag on the GC won't kill it. But on my little I5-2700 it certainly does
When I was growin' up, I was the smartest kid I knew. Maybe that was just because I didn't know that many kids. All I know is now I feel the opposite.
|
|
|
|
|
Hi!
Let me share with you the techniques we have used to go around some of the drawbacks you describe.
Our solution is consuming a LARGE amount of UDP traffic (MPEG2 streams) - over 200Mbps of data. It is also performing some heavy DSP processing and it taxes the garbage collector heavily.
Being UDP and with no retransmission protocol available - we are very sensitive to reading the data on time if we want to keep the quality of the video streams high. Reading the UDP traffic with managed code was a disaster.
Basically, we are taking advantage of the fact that an unmanaged thread is NOT suspended during garbage collection.
1. We used C++/CLI to write the multicast UDP reader code inside a non-managed class. We ensure that the CLR support is disabled for the .cpp file implementing the code that we don't want to be interrupted during garbage collection. This makes sure that the code is compiled to native code, otherwise it may get compiled to IL code and the unmanaged thread may be blocked once it transitions to IL space. Even if using the unmanaged pragma, the compiler creates managed thunks around the unmanaged code and we want to avoid IL completely for this generated code. The unmanaged thread that is reading the UDP traffic (using RIO for higher performance) runs inside this class. Note that unfortunately we can't take advantage of the .Net framework in this class, so we rely on Boost.
2. We keep the unmanaged code as simple as possible, basically we loop reading UDP packets and enqueue them. However, we make sure that we use a lockless queue for this purpose (we use a Boost lockless queue). This is vital because there will be a managed thread consuming the queue, bridging the data onto the managed world. This consumer thread will be suspendend during GC activity and we don't want the thread to be suspended while holding a lock for the queue (otherwise the unmanaged thread may block contending for the lock). Another plus is that using a lockless queue, we become immune to thread priority inversion, so we can boost the producer thread priority to the highest level possible.
3. Using C++/CLI we produce a .net friendly class. This class owns and instantiates the unmanaged class, and also implements the managed queue consumer thread. (It can seamlessly consume the Boost lockless queue and expose the unmanaged memory (the udp packets) in a managed friendly way. Now, no matter if the managed world is suspended, the unmanaged thread will keep filling the lockless queue.
We also tuned some relevant NIC parameters, and by using these techniques, we can proudly say we are losing less than 0.001% of the packets (when we were losing around 8% of the packets with our initial, purely-managed solution).
I think I may actually write an article (my first) on this technique. It may be useful to others.
Feel free to contact me if you want to try this approach!
|
|
|
|
|
Yeah, that's the Microsoft recommended way I think, either that or using classic unmanaged C++ and just calling into that but marshalling can be a problem depending on performance needs, so mixed mode/managed may be the way to go. The other option would probably be to write a custom marshaller for those methods, if you really don't want to mix manage/unmanaged code in the same assembly
I'd share it, because people may not be aware of the technique.
As far as the UDP I'm curious if you use any sort of QOS method on your UDP streaming?
uTP does, but to the opposite ends and goals as yours, although using something like it might smooth playback during network traffic spikes
When I was growin' up, I was the smartest kid I knew. Maybe that was just because I didn't know that many kids. All I know is now I feel the opposite.
|
|
|
|
|
so I took someone's advice here about the way i declare tuples. Redid everything using named tuples and now my code only works with .NET core.
FML.
Luckily i won't be releasing officially for like a year.
When I was growin' up, I was the smartest kid I knew. Maybe that was just because I didn't know that many kids. All I know is now I feel the opposite.
|
|
|
|
|
I just checked, and once I set my project to .NET 4.7.2 instead of 4.6.2 it compiled and worked perfectly:
private void FrmMain_Load(object sender, EventArgs e)
{
if ((ModifierKeys & Keys.Shift) == 0)
{
this.LoadLocation();
}
var loc = GetLocation("some address");
Console.WriteLine($"Lat: {loc.x}, Long: {loc.y}");
}
private (double x, double y) GetLocation(string address)
{
return (100, 100);
}
Sent from my Amstrad PC 1640
Never throw anything away, Griff
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|