In my experience range checks did affect the speed, but range check alone may not be to blame. It might well be that code pieces became a tad bit larger than what a compiler would automatically inline, and those snow-balled.
In case you have such a benchmark handy would appreciate a link 'cause fast is good, but fast and correct is way better.
The realities of internet exposure put an end to this recklessness for casual^H^H^H^H^H "enterprise" software development.
I love C A Hoare's comments, reference in this link (http://en.wikipedia.org/wiki/Bounds_checking) about "some languages" (cough cough - C)
Interesting how the Go language now includes array bounds checking. While 2 of the main designers are ex Bell labs, 1 of them is from U of Zurich. (I'm assuming he would have had some Pascal/Modula exposure there)
James:
I liked your article on range checking.
I'm very keen on assertions, for example here:
https://github.com/chkoreff/Fexl/blob/fresh/base/src/buf.c#L...
It's still fast as greased lightning. I buffer up 2.6 MB here:
https://github.com/chkoreff/Fexl/blob/fresh/test/src/run.c#L...
It's too fast to notice. I can bump it up to 260 MB and try that:
$ ./build && time ../bin/run
: Buffering 260000000 bytes
length = 260000000
real 0m1.751s
user 0m1.608s
sys 0m0.140s
What the heck, let's try an even 2 GB: $ ./build && time ../bin/run
Compile run
Link run
: Buffering 2000000000 bytes
length = 2000000000
real 0m13.350s
user 0m12.049s
sys 0m1.288s
Ah, but what if I remove my range check? $ ./build && time ../bin/run
Compile buf
Link run
: Buffering 2000000000 bytes
length = 2000000000
real 0m13.103s
user 0m11.781s
sys 0m1.312s
Repeated testing of both ways shows no significant difference.I think I'll leave in the range check.
Range checks also support one of my favorite programming maxims:
No Silent Failure!Thing is, they're a list thing, and there's more than just "is this index valid?" that can go wrong. One of the things I appreciate about C++ is that iterator invalidation is very precisely specified. Sadly, that's where it stops: it's just specified, but the onus is still on you to catch the errors. It'd be great to have that same thing: immediate errors when you use an invalidated iterator. (in vectors, it might just skip an element (some deletes) or see the same element twice (some inserts); Python warns about this during some cases with dicts:
RuntimeError: dictionary changed size during iteration
which is nice, but I think you can still slip by that.) I don't believe Java or Python specify what happens to iterators when the collection changes, which to me, is a bit sad.Thing is, to implement this, the collections would probably need to know about all outstanding iterators, so as to figure out where they are and whether they should be invalidated at all. Most operators then become O(number of active iterators + whatever the normal cost of this op is); I'd argue this might still be close to O(1 + ...) since the number of active iterators on a collection is probably usually 0 or 1. But there's a memory cost, a CPU cost, and if you have something like:
if(weird condition) {
use_that_iterator_i_just_accidentally_invalidated()
}
Then your bug only gets caught if `weird_condition` is true. Is it worth it?Leave in all range checks. If the compiler can determine that the range check will never be hit, or can partially optimize it (hoisting it out of a loop, etc) great. Otherwise? It gets checked at runtime, which is typically not that expensive an operation anyways.