On 12/6/2013 3:39 PM, H. S. Teoh wrote:
1. D knows when data is immutable. C has to always make worst case
assumptions, and assume indirectly accessed data mutates.

Does the compiler currently take advantage of this, e.g., in aliasing
analysis?

I'm pretty sure dmd does, don't know about others.


2. D knows when functions are pure. C has to make worst case assumptions.

Does the compiler currently take advantage of this?

dmd does.


3. Function inlining has generally been shown to be of tremendous
value in optimization. D has access to all the source code in the
program, or at least as much as you're willing to show it, and can
inline across modules. C cannot inline functions unless they appear
in the same module or in .h files. It's a rare practice to push many
functions into .h files. Of course, there are now linkers that can
do whole program optimization for C, but those are kind of herculean
efforts to work around that C limitation of being able to see only
one module at a time.

To be frank, dmd's current inlining capabilities are rather
disappointing. However, gdc IME has shown better track record in that
area, so it looks promising.

This is about inherent language opportunities, not whether current implementations fall short or not.



4. C strings are 0-terminated, D strings have a length property. The
former has major negative performance consequences:

     a. lots of strlen()'s are necessary

Yeah, this is a common C bottleneck that most obsessively-optimizing C
coders overlook. Many a time profiling of my "optimal" code turned up
surprising bottlenecks, like fprintf's in the wrong place, or strlen's
in inner loops. D's strings may be "heavier" (in the sense that they
require a length field as opposed to a mere pointer -- y'know, the kind
of stuff obsessively-optimizing C coders lose sleep over), but it comes
with so many advantages that it's more than worth the price.

Yup.


     b. using substrings usually requires a malloc/copy/free sequence

Yeah, string manipulation in C (and to a great extent C++) is a royal
pain. It actually drove me to use Perl for non-performance critical
string manipulating code for quite a good number of years. The heavy
syntax required for using a regex library in C, the memory management
nightmare of keeping track of substrings, all contribute to fragile
code, increased development times, and poorer performance. Only
obsessively-optimizing C coders can fail to see this (and I used to be
among them). ;-)

I failed to recognize the problem for what it was for years, too. I think the 0 terminated string issue is a severe fault in C, and it was carried over into C++'s std::string. C++ missed a big opportunity there.


6. D's array slicing coupled with GC means that many
malloc/copy/free's normally done in C are unnecessary in D.

It does mean we need to get our act together and improve the GC's
performance, though. ;-)

The best memory allocation algorithm is one that doesn't do allocations at all. While this may sound trite, I think it is a crucial insight. The mere existence of the GC enables many allocations to not be done. The other thing is that D's ranges can also be used to eliminate a lot of low level allocations.


But this is a major advantage in array manipulation (esp. string
manipulation) in D. In equivalent C code, best practices dictate that
slicing should always be done by allocating a new array and copying,
because otherwise you introduce intricate dependencies between pointers
and your code either becomes wrong (leaks memory / has dangling
pointers), or extremely convoluted (reinvent the GC with reference
counting, etc.). Being able to freely slice any array you want without
needing to keep track of every single pointer, is a big savings in
development time, not to mention better runtime performance because you
don't have to keep allocating & copying. All that copying does add up!

Exactly.


There's also another aspect to this: being built into the language, D
slices allows different pieces of code to freely exchange them without
needing to worry about memory management. In equivalent C code, an
efficient implementation of slices would require the use of custom
structs (as a pair of pointers, or a pointer + length ala D), or
specific function parameter conventions (e.g. always pass pointer +
length). Since everyone will implement this in a slightly different way,
it makes interoperating different bits of code a pain. One function
takes a pointer + length, another function takes a pointer pair, a third
function takes a custom struct that encapsulates the foregoing, and a
fourth function takes *another* custom struct that does the same thing
but with a different implementation -- so now to make the code work
together, you have to insert intermediate layers of code to convert
between the representations. It's a big development time sink, and yet
another corner for bugs to hide in. In D, since slices are built-in,
everybody takes a slice, no interconversion is necessary, and everybody
is happy.

Yup.

Reply via email to