Comment by seanmcdirmid

13 years ago

> I always considered frameworks like WPF (or Flex, for that matter) 'intern code' - not that interns necessarily wrote them, but they reek of not-experienced-enough engineers trying to solve problems by writing a bunch of new code, instead of fixing existing code.

This is unfair and unfounded accusation. If you look at the time frame, all the major platform were working on their first hardware accelerated UI toolkits and went through the similar teething problems (Cocoa anyone?). WinForms was a dead end, there was no fixing to do. WPF has turned out well enough, and WinRT has evolved into something very efficient (e.g. by using ref counting rather than GC).

> Plus, say what you want about GDI (there's a lot wrong with it at this point), but it's still a surprisingly efficient and flexible way to do 2D rendering

Not anymore. I get it that some love to use antique APIs and computer systems just for the sake of being retro, but when every computer ships these days with a GPU, using GDI is not even close to pragmatic.

> ...WinRT has evolved into something very efficient (e.g. by using ref counting rather than GC).

Ah you mean by causing cache stalls when doing increments/decrements after each assignment and allowing for cyclic references?

  • If the ref counting is manual or optimized, it doesn't happen after every assignment, only ones which change object ownership. If you're really nuts about avoiding cache hits, you can pack most ref counts into the low bits of the class pointer, since allocations tend to be 16-byte aligned, including allocations for class objects. Assuming you use the object at least once following a change of ownership, the overall cost in cache misses becomes 0. Or you can store the references in a global table with better cache properties (IIRC this is what ObjC does). Or you can rely on the fact that cache lines are typically large enough to grab a few words at a time, so fetching the class pointer will automatically fetch the refcount (again with the assumption that you use objects at least once per assignment). Honestly, I'm having difficulty imagining how cache behavior could become a problem unless you wanted it to.

    As for not allowing cycles, I consider that a feature. The headache of memory management doesn't go away with GC. You still have to avoid inadvertently keeping references to objects through the undo stack & so on. Unintentional strong refs creep through GC code just as easily as memory leaks creep through code with manual memory management. Almost universally, I find that GC'd projects large enough to have memory management best practices implicitly do away with this freedom at the first opportunity by calling for acyclic or single-parent object ownership graphs. These restrictions primarily make it easier to think about object lifecycle -- the fact that they allow refcounting to suffice for memory management is icing on the cake.

    • > If the ref counting is manual or optimized, it doesn't happen after every assignment, only ones which change object ownership.

      The Rust compiler does this. Even so, 19% of the binary size in rustc is adjusting reference counts.

      I am not exaggerating this. One-fifth of the code in the binary is sitting there wasted adjusting reference counts. This is much of the reason we're moving to tracing garbage collection.

      All of those optimizations that you're talking about (for example, packing reference counts into the class pointer) will make adjusting reference counts take even more code size.

      > As for not allowing cycles, I consider that a feature. The headache of memory management doesn't go away with GC.

      The fact that memory management is still an issue doesn't mean that we shouldn't make it work as well as we can.

      In large software projects, cycles are a persistent issue. Gecko had to add a backup cycle collector over its reference counting framework, and observed memory usage decreased significantly once it was implemented.

      1 reply →

    • Very nice explanation, except you are forgetting WinRT is actually an extension of COM.

      This means each increment/decrement is a virtual method call with the corresponding invocation costs.

      This is why the .NET runtime caches the WinRT objects, and even re-uses them instead of making a 1:1 use like C++/CX does.

  • The caching misbehavior of referencing counting has been greatly exaggerated, especially in the context of UI where responsiveness is much more important than raw CPU speed. Also, the ref counting tradeoff seems to work better for device (e.g. all the cool kids [1] are doing it).

    [1] https://developer.apple.com/library/mac/documentation/Genera...

    .NET still does GC, it is only the WinRT APIs (something like COM) that manage resources through ref counting. There is some cool interop magic that makes this somewhat transparent to the programmer.

    • > The caching misbehavior of referencing counting has been greatly exaggerated, especially in the context of UI where responsiveness is much more important than raw CPU speed. Also, the ref counting tradeoff seems to work better for device (e.g. all the cool kids [1] are doing it).

      Apple's ARC is a compiler hack. It only works if you happen to use set of Cocoa libraries that are recognized by the compiler and allow for some magic incantation.

      You cannot make any Objective-C library ARC aware.

      ARC is a good solution for Objective-C, because Apple never managed to have their GC working safely in all situations, given the underlying C type system.

      > .NET still does GC, it is only the WinRT APIs (something like COM) that manage resources through ref counting. There is some cool interop magic that makes this somewhat transparent to the programmer.

      True, but C++ does not and you still have the performance penalty of reference counting.

      The only way to have reference counting with good performance is do what Parasol or Objective-C do, by removing unnecessary increment/decrement operations thanks to dataflow analysis.

      Which is something that you cannot do just by using the WinRT runtime and need to rely on the cleverness of the compilers.

      Actually, are you aware that reference counting tends to be presented on the first chapter of many CS books about garbage collection as poor man's GC?

      11 replies →

GDI has been GPU accelerated literally forever. Vista may have dropped hardware acceleration for GDI, but it was promptly brought back in Windows 7.

Since the Win32 UI stack uses GDI, it was hardware accelerated before WPF even existed.

  • Not the same thing! This is like comparing pre-DX8/programmable shader hardware acceleration to the fixed-function crud we had to deal with a long time ago. Ya, you can emulate fixed functions with programmable shaders, but its a poor way to make use of a modern GPU.

    Here is a good discussion of the topic:

    http://msdn.microsoft.com/en-us/library/windows/desktop/ff72...

    Excerpts:

    > When the GDI DDI was first defined, most display acceleration hardware targeted the GDI primitives. Over time, more and more emphasis was placed on 3D game acceleration and less on application acceleration. As a consequence the BitBlt API was hardware accelerated and most other GDI operations were not.

    > In order to maintain compatibility, GDI performs a large part of its rendering to aperture memory using the CPU. In contrast, Direct2D translates its APIs calls into Direct3D primitives and drawing operations. The result is then rendered on the GPU. Some of GDI?s rendering is performed on the GPU when the aperture memory is copied to the video memory surface representing the GDI window.

    > Existing GDI code will continue to work well under Windows 7. However, when writing new graphics rendering code, Direct2D should be considered, as it takes better advantage of modern GPUs.