4 ms·
Agreed. Sure in scala there are a couple things you can't do like pointer manipulation, so you are going to incur more allocations but from what I've found in
by fleitz 16y ago
Agreed.
Sure in scala there are a couple things you can't do like pointer manipulation, so you are going to incur more allocations but from what I've found in C# is, if you code it like a C app it performs like a C app.
The biggest key to performance I've found in managed code is to not allocate memory. It reduces stress on the GC, and improves your cache hit rate.
- barrkel 16y agoI can't agree with the non-allocation of memory. What you don't want to do is create garbage that doesn't die until after a collection has already occurred - this is the mid-life crisis problem. But you shouldn't be unduly afraid of allocating memory which won't last long. I wrote up a quick test sample: http://pastebin.com/YmbT08Uy http://pastebin.com/YmbT08Uy All it does is fill in an array with values from 0 to 511, then sum them up. The catch is that it zeros out the array before filling in the values, in two different ways: one (P), by re-using an array, and secondly (Q) by reallocating the array and letting the garbage collector zero it out. Here are test run timings: P: 8.749 Q: 7.070 Result: 0 And in reversed order, to show that order of compilation didn't make a difference: Q: 7.081 P: 8.746 Result: 0 The code using the GC to implicitly zero out the array is still faster than the code that reuses the array, even though the GC is having to collect a large fraction of the 10 million 2048-byte arrays allocated by Q. P, on the other hand, only allocates 100 of them. Test machine: i7 920 @ 3.6GHz, 12GB memory, .NET 4 on Win7 64-bit. Don't forget that time spent in GC is proportional to the amount of live data in the generation(s) being collected, not the amount of garbage. Collecting garbage is actually free; it's finding out what's garbage that costs. If you don't keep references around to memory (i.e. increase your live set), GC is very cheap.
- runT1ME 16y agoVery well said, and I believe what makes the G1 so brilliant is it will be able to tackle the mid-life crisis issue.
- JoachimSchipper 16y agoI'm by no means an expert on the .Net platform, but are these equivalent? You generate ~20GB of garbage, about half of which could comfortably fit in your memory; more importantly, it appears that .Net, in some modes, does garbage collection in a different thread than the one running the program. And it's clear that using a different thread to zero memory is faster... Also note that I was talking about object creation, which may be a lot heavier than array creation.
- barrkel 16y agoThe memory usage never climbs above 15MB - that's for the whole process, the .NET runtime, code, system DLLs, etc. all in. With object creation, reuse is far harder whereas with an array, you can just zero it out. In other words, the argument in favour of reuse is even weaker for objects than it is for arrays. I would say that if you have large arrays - large enough to go into .NET's large object heap, at about 70K - you're better off reusing those, because they only get collected during expensive gen2 collections, and they aren't compacted. As to the fact that the GC can zero memory on a different thread, that's not a problem - it's a benefit, as it's guaranteed correct concurrent zeroing, as opposed to the contortions you'd have to do to reuse. Again, an argument in favour of GC.
- JoachimSchipper 16y agoI was never arguing in favour of reuse; I was arguing in favour of the C approach (keep everything in local variables and build a state machine.) And yes, concurrent zeroing is nice. I don't dispute that: I just wanted to point out that the speedup you demonstrate above (partially) comes from using additional resources (CPU time on another processor), not from using existing resources more efficiently.
- barrkel 16y agoIf the problem can be solved with a finite state machine, you'd be silly to allocate an amount proportional to the input size, for sure. I wasn't arguing against the C approach; when I write compilers, I implement lexical analyzers as state machines all the time. (In my day job, I write C most of the time.) And even when state is in object fields, I copy them out into local variables before doing work, and copy them back in to fields after work, because it's much easier for a compiler to enregister locals than fields. What I was actually arguing against is the idea that it's a good idea, in a GC environment, to avoid allocations when trying to improve performance. Allocations are extremely cheap in a GC environment; and automatic memory management by a precise copying collector is asymptotically faster than manual memory allocation on a per-item basis. (If you use pools or zones, you can get better manual memory management performance again, but then you lose allocation granularity.) It's not clear that the code I posted uses more resources. It uses some time from the other processor (i7 920 has 8 logical cores, CPU usage as Windows measures it never exceeds about 17%) but it also completes sooner. The collector also has opportunity to use more optimized zeroing strategies, whereas I coded the manual array zeroing with an explicit loop.