C++ at Google: Here Be Dragons
blog.llvm.org
blog.llvm.org
These are the kinds of mistakes I made all the time when working in C, and the kind of thing that made coding extremely tedious...it feels like magic when the compiler catches them with such clear and concise warnings. For whatever reason, I didn't use lint very much back then, as I guess I always assumed I knew what I was doing and that the compiler would catch mistakes. Having this capability in the compiler is pretty cool and brings C/C++ a small step closer to working in higher level languages, is what I think I'm trying to say here.
Your getting over those longings will probably coincide with when you start working with C++ again.
C++0x is another (much bigger, in my opinion) step that makes C++ a lot easier to program. It's not quite as easy as higher level languages, but far closer than before, and the performance gains over most other languages make it worth using.
Of course writing unit tests helps too, but in C++ the compiler catches these things easily. And I haven't had the desire to use a variant in years so the advantages of having a 'variable' that can be of any type is quite minimal, imo. The auto keyword in C++0x will make a large portion of the tedious parts of strong typing in C++ go away, too.
IMO thinks like type hinting in PHP are absolutely steps in the right direction. There is room for both 'scripting' and 'compiled' languages, but good support for indicating and checking the expected or required types in scripting languages helps tremendously in proactively validating programs.
Really? I'd argue the exact opposite -- with F# and Scala (and some others) on the rise, I think there are plenty of people who are fed up with weak/dynamic typing and want to take advantage of building programs with strong type systems.
At the very least, it seems that the programming world is becoming much more polarized. Anecdotally, it seems that every developer I know is either a proponent of strong/static typing or weak/dynamic typing -- but I don't know anyone that's just sitting on the fence.
I think this provides a useful upgrade path, because type verification is a hassle with small projects, but becomes increasingly valuable (IMHO) as the program grows.
The problem with current languages is that you have to decide upfront if you want a language optimized for quick development or strong verification. But often in the real world, programs start out as quick prototypes, and then grow into large applications.
http://en.wikipedia.org/wiki/Qi_(programming_language)#Type_...
For fun hobby stuff, I love dynamic languages where I can produce quite a bit of functionality in a very short (if dangerous) time and rely on a set of tests to keep it all managed.
I do see a lot of people getting all hung up on one side or the other though; and it could be that the rest of the world you are seeing is just that group "over there".
Perhaps predictably, as I get older (in my 40's now), I am leaning more towards the strong type system languages, but I do enjoy my dalliances with a dynamic language, at least for small things.
a value
a number
a rational number
a rational number between [0.0 - 1.0)
a rational number between [0.0 - 1.0), and I don't need any more than 30 bits of precision
a rational number between [0.0 - 1.0), and I don't need any more than 30 bits of precision, and please try to optimize for minimum memory usage instead of performance
I'm interested in developing such systems for Clojure and have actively researching something considerably more powerful than Chambers/Chen predicate dispatch.
> a rational number between [0.0 - 1.0)
Those are representation errors.
The hard problems that I run into rarely have much to do with representation. The hard problems are knowing when it's okay to add apples and oranges and when it isn't.
That said..., I have an uneasy feeling that trying to do it would end up with a PL/1 type of situation where, depending on your background, you write it with whatever baggage you bring with you, and the language would get large to the point of different types of programmers writing in their own familiar subset of the language.
Or, I'm completely wrong. =)
subset Filename of Str where { $_ ~~ :f };
Here you define the Filename type as a string that represents an existing path. This particular example is a horrendous hack of course, but the tool looks very interesting. Not sure if this can be used for performance tuning in existing P6 implementations.Perl 6 does have 'gradual typing' though. You can define a variant:
my $foo;
or a typed variable: my Int $foo;
or a subtype with arbitrary restrictions subset OneToTen of Int where { 1 <= $^n <= 10 };
my OneToTen $foo;
Or... (http://rosettacode.org/wiki/Define_a_primitive_data_type#Per...) subset Prime of Int where { $^n > 1 and $^n %% none 2 .. sqrt $^n };
Perl 6 scares me.It would be an amazing research project to take a couple different, large Google C++ programs and port them to Haskel and Erlang to see how they compre.
* Why does it have to be such a common word for it's name? Just means it's name, for all practical purposes, is "Go Language".
long kMaxDiskSpace = 10 << 30; // Thirty gigs ought to be enough for anybody.
10<<30 is ten gigs, not thirty gigs.
The integer literals we are shifting are of 'int' type, and the shift occurs at that type (based on the usual arithmetic conversions). There is stack overflow question with explanations and a good blog post here about it:
http://stackoverflow.com/questions/836544/usual-arithmetic-c...
http://blogs.msdn.com/b/oldnewthing/archive/2004/03/10/87247...
Also, you can look through the C++98 standard to understand all the details. Relevant sections are [expr]p9 and [expr.shift].
All I know for sure is I compiled my codebase with Clang for the first time yesterday and the compilation time was absurdly short. I thought the compiler was broken. And it enables excellent tools like clang_complete for Vim code completion.
Over time, as Clang gets more mature, it will become more and more on par (or better) than GCC.
I've observed no such 10-20% slowdown on iOS, which is more CPU-constrained than most realms. Typically, LLVM-generated code is equivalent to or faster than gcc. Sometimes it's much faster.
What is an example of an optimization that a JIT compiler can make that a AOT compiler cannot?
If the developer is able to profile the application on typical end-user workloads, don't profile-guided optimizations provide the same benefit as JIT runtime profiling?
Why can't an AOT compiler just consider every path a "hot" path?
Last but not least: Got any benchmarks?
Wikipedia gives a few more[2]: runtime profile-guided optimizations and pseudo-constant propagation
In the case of non-dynamic languages like C and C++ that clang generally targets, are there other examples of where JIT would make things possible that are not possible in AOT?
Optimizing for the specific processor you're running on, as opposed to being forced to compile for a lowest common denominator.
A whole bunch of other small things like that.
For example, you might see that branch X is always taken. So you assume that X will always be true, and add a guard just in case which triggers a recompilation. You reoptimized the function on the basis of your new (speculative) information about X. This could improve register allocation, allow you remove lots of code (other branches maybe), inline functions, etc.
Java JITs have been known to inline hundreds of functions deep with this.
Profile-guided optimizations only work on the next run, and, when used by the developer, do not work for cases where there are widely different usage profiles for a single program. For example, most users would have data sets that fit in memory, but others will have ones that do not.
http://gcc.gnu.org/releases.html#timeline http://www.gnu.org/philosophy/pragmatic.html (see part about Objective C)
I hear there's this kernel called Linux that depends heavily on GCC.
http://lists.cs.uiuc.edu/pipermail/cfe-dev/2010-October/0117...
"Quite frankly, I'd like there to be more competition in the open source compiler game, and that might cause some upheavals, but on the whole, gcc actually does a pretty damn good job."
int foo = 0;
But got changed into a non-integral type: Thing foo = 0;
Turns out Thing had a copy constructor roughly like: Thing::Thing(Thing* old)
: field(old->field)
{
}
Needless to say, my program was not very happy. int i = NULL;
Because of C++'s conflation of NULL and 0 (and now C++0x's nullptr).I have a passing acquaintance with both, and I'm almost certain both would have caught the three bugs listed on that page.
A lot of the static analyses we've looked into (and I'm hoping for more detailed blog posts about that in the future) find plenty of bugs, but also find lots of non-bugs. Combine that with being too slow to run during the normal build, and you can't break the build when such a bug is found.
I think one of the most interesting aspects of this is how we catch the bugs early, and force developers to fix them immediately by breaking the build.
Plus the compiled code is pretty fast. So if you're feeling the need to reduce your workload take a look at it, you might be surprised.
However, there is a small collection of oss Ada libraries out there.
Anyway, Ada has some other amazingly cool features. The concurrency primitives it offers are very cool, lets you make some much stronger guarantees about the interactions between threads than any other language I've seen. For example, you can define rendezvous sections, which if memory serves, are pieces of code that are guaranteed to only be run once both threads participating in the rendezvous and neither thread can leave the section until both are ready.
You're right about the concurrency. Ada has a bunch of stuff like that built into the language since 1983.
The particularly cool toys I like are SPARK (a formal verifier tool) and stackcheck - tells you exactly how deep in the stack your code can possibly go. (Yes you have to annotate cycles.)
Take a look again, with an eye towards large scale long lived critical systems. You'll find that explicitness a feature, as is the very strict static typing.
Oh, and they got pointers right the first time.
EDIT: Forgot to mention the coolness of the type system - you can declare ranges and other type information and the compiler will hold that requirement strictly. for example type direction is range 0..359; Declares the obvious, but now you can have the compiler error if you assign a direction type with a value that might be outside that range. Even if you don't set that option you can always use the foo'valid attribute to check that foo is within bounds. This is used to check for stray cosmic radiation (seriously!).
Yes it seems like overkill. But when you come back to 20 year old code that's still running you'll smile.
We still develop using GCC - for our use case Clang performance isn't there yet - but have our continuous integration system perform Clang builds (as well as other platform/compiler variants, with unit+regression tests, etc.) so we miss out on the immediate build breakage that you mention, but do find out within ~30 mins if someone slipped up.