September 28, 2009
Christopher Wright:

> > Certainly agreed on virtual calls: on my machine, I timed a simple example as executing 65 interface calls per microsecond, 85 virtual calls per microsecond, and 210 non-member function calls per microsecond. So you should almost never worry about the cost of interface calls since they're so cheap, but they are 3.5 times slower than non-member functions.

The main problem of virtual calls in D are the missed inlining opportunities.

------------

Andrei Alexandrescu:

> I seem to recall that interface dispach in D does a linear search in the interfaces list, so you may want to repeat your tests with a variable number of interfaces, and a variable position of the interface being used.

The following is a D port of the well known "Richards" benchmark. This specific version is object oriented, its classes are final (otherwise the code gets quite slower with LDC) and it has getters/setters. It contains an interface:
http://codepad.org/kO3MJK60

You can run it at the command line giving it 10000000.

On a Celeron 2 GHz if you replace the interface with an abstract class the running time goes from 2.16 to 1.58 seconds, compiled with:
ldc -O5 -release -inline

Compiled with DMD the running time seems about unchanged. I have no idea why. Maybe some of you can tell me.

In a day or two I'll release many more timings and tests about this Richards benchmark.

Bye,
bearophile
September 28, 2009
On 28/09/2009 15:28, Jeremie Pelletier wrote:
>>
>> here's a type-safe alternative
>> note: untested
>>
>> struct Vec3F {
>> float[3] v;
>> alias v[0] x;
>> alias v[1] y;
>> alias v[2] z;
>> }
>>
>> D provides alignment control for structs, why do we need to have a
>> separate union construct if it is just a special case of struct
>> alignment?
>
> These aliases won't compile, and that was only one out of many union uses.

what other use cases for unions exist that cannot be redesigned in a safer way?

>
>> IMO the use cases for union are very rare and they all can be
>> redesigned in a type safe manner.
>
> Not always true.
>
>> when software was small and simple, hand tuning code with low level
>> mechanisms (such as unions and even using assembly) made a lot of
>> sense. Today's software is typically far more complex and is way to
>> big to risk loosing safety features for marginal performance gains.
>>
>> micro optimizations simply doesn't scale.
>
> Again, that's a lazy view on programming. High level constructs are
> useful to isolate small and simple algorithms which are implemented at
> low level.

One way to define programming is "being lazy". You ask the machine to do your work since you are lazy to do it yourself.

your view above about simple algorithms which are implemented at low level is exactly the place where we disagree.

Have you ever heard of Stalin (i'm not talking about the dictator)?

I was pointing to a trade off at play here:
you can write low level hand optimized code that is hard to maintain and reason about (for example, providing formal proof of correctness). You gained some small, non scalable performance gains and lost on other fronts like proving correctness of your code.

the other way would be to write high level very regular code that can be maintained, easier to reason about and leave optimization to the tools. granted, there could be some initial performance hit compared to the previous approach but this is more portable:
hardware changes do not affect code, you just need to re-run the tool. new optimization techniques can be employed by running a newer version of the tool, etc.

I should also note that the second approach is already applied by compilers. unless you use inline ASM, the compiler will not use the entire ASM instruction set which contains special cases for performance tuning.

>
> These aren't just marginal performance gains, they can easily be up to
> 15-30% improvements, sometimes 50% and more. If this is too complex or
> the risk is too high for you then don't use a systems language :)

your approach makes sense if your are implementing say a calculator.
It doesn't scale to larger projects. Even C++ has overhead compared to assembly yet you are writing performance critical code in c++, right?

Java had a reputation of being slow yet today performance critical servers are written in Java and not in C++ in order to get faster execution.
September 28, 2009
On Mon, 28 Sep 2009 15:35:07 -0400, Jesse Phillips <jesse.k.phillips+d@gmail.com> wrote:

> language_fan Wrote:
>
>> Have you ever used functional languages? When you develop in Haskell or
>> SML, how often you feel there is a good change something will be
>> initialized to the wrong value? Can you show some statistics that show
>> how unsafe this practice is?
>
> So isn't that the question? Does/can "default" (by human or machine) initialization create an incorrect state? If it does, do we continue to work as if nothing was wrong or crash? I don't know how often the initialization would be incorrect, but I don't think Walter is concerned with it's frequency, but that it is possible.

It creates an invalid, non-compiling program.

It's simple:

If initialization doesn't make sense, don't use non-nullable type.

If initialization makes sense, use non-nullable type, initialize with the correct value.

In case 1, we are back to current behavior, no problem (in Walter's eyes).

In case 2, we eliminate any possible crash due to non-initialization.

The subtle difference is the *default*.  If non-null is the default, then you haphazardly write code like this:

Object o;

And you get a compile error "error, please initialize o or declare as Object? o".  It makes you look at the line and say "hm... does it make sense to initialize there?" and you either put an initializer or you change it to

Object? o;

And move on.

90% of the time, you write something like:

auto x = new Object();

and you don't even have to think about it.  The compiler tells you when you got it wrong, and usually you then get it right after a moment of thought.

At least, that has been my experience with C# (granted, it uses flow analysis, not non-nullable defaults).  And I very seldom have null exception errors in my C# programs (they do happen, but of course, I get a nice stack trace).  Compare that to D, where I build my program and get:

# ./program_that_I_just_spent_1_week_writing_and_getting_to_compile
Segmentation fault.
#

I'd rather spend an extra 5 minutes having D compiler complain about initialization than face the Segmentation fault error search.

The thing is, I don't want D to cater to the moronic programmers that say "what? I need to initialize, ok, um.. here's a dummy object".  I want it to cater to *me* and prevent *me* from making simple errors where I obviously should have known better, but accidentally left out the initializer.

It's like the whole allowing object == null problem (coincidentally, resulting in the same dreaded error).  Once Walter implemented the compiler that flagged them all, he discovered Phobos had several of those *obviously incorrect* statements.  Hm... maybe he should do the same with this...  Maybe someone who can hack the compiler can do it for him!  Any takers?

-Steve

P.S.  I never make the object == null mistake anymore.  The compiler has trained me :)
September 28, 2009
Hello Manfred_Nowak,

> BCS wrote:
> 
> [...]
> 
>> I wouldn't want to hire a programer that *habitually* (and
>> unnecessarily) hacks past a feature designed to prevent bugs.
>> 
> In the short time of an interview its not possible to test for habits
> (or necessarity) to hack past a feature designed to provent bugs.

Good point, I guess that all that is left would be to try and get a feel for what they think of that kind of practice (give them something ugly that works and ask "what do you think of this code?"). If they indicate they think that kind of hacking a bad idea, then at least you can say they lied if you have to get rid of them for that kind of things.


September 28, 2009
Mon, 28 Sep 2009 20:34:44 +0000, BCS thusly wrote:

> Hello Manfred_Nowak,
> 
>> BCS wrote:
>> 
>> [...]
>> 
>>> I wouldn't want to hire a programer that *habitually* (and unnecessarily) hacks past a feature designed to prevent bugs.
>>> 
>> In the short time of an interview its not possible to test for habits (or necessarity) to hack past a feature designed to provent bugs.
> 
> Good point, I guess that all that is left would be to try and get a feel for what they think of that kind of practice (give them something ugly that works and ask "what do you think of this code?"). If they indicate they think that kind of hacking a bad idea, then at least you can say they lied if you have to get rid of them for that kind of things.

At least in the companies I have worked in they briefly teach you their stuff in 1-7 days and want to see some preliminary results. If you have trouble writing any code, you have lost the job (there is a 6 month test period or something similar so it is perfectly legal to kick him out if he fails). Usually the schedules are tight so hiring a lazy bastard is not worth the effort. Other ways to control the learning are working with a more experienced pair (pair programming - ever heard of it?) and weekly meetings.
September 28, 2009
Mon, 28 Sep 2009 15:35:07 -0400, Jesse Phillips thusly wrote:

> language_fan Wrote:
> 
>> > Now if you really want to throw some sticks into the spokes, you would say that if the program crashes due to a null pointer, it is still likely that the programmer will just initialize/set the value to a "default" that still isn't valid just to get the program to continue to run.
>> 
>> Why should it crash in the first place? I hate crashes. You liek them? I can prove by structural induction that you do not like them when you can avoid crashes with static checking.
> 
> No one likes programs that crash, doesn't that mean it is an incorrect behavior though?
> 
>> Have you ever used functional languages? When you develop in Haskell or SML, how often you feel there is a good change something will be initialized to the wrong value? Can you show some statistics that show how unsafe this practice is?
> 
> So isn't that the question? Does/can "default" (by human or machine) initialization create an incorrect state? If it does, do we continue to work as if nothing was wrong or crash? I don't know how often the initialization would be incorrect, but I don't think Walter is concerned with it's frequency, but that it is possible.

Value types can be incorrectly initialized and nobody notices. E.g.

  int min;

  foreach(int value; list)
    if (value < min) min = value;

Oops, you forgot to define a flag variable or initialize to int.min (if that is what you want). Even Java IDEs spot this error, but not D. The flow analysis helps me in tremendous ways - I can fix the error statically and boom, the software is suddenly again error free.

Now I can tell you, in functional languages there is no other way. All initializations have to be correct, they are final, they are constants and they can be initialized incorrectly. But there are some tools that help in this. Functions can be automatically tested. Invariants, pre- and post-conditions can be set. Still, I can even bet they are much safer than D in every possible way. How is this possible?

It really depends on your subjective opinion whether you want a program to segfault or spot a set of errors statically, and have illegally behaving non-crashing programs. I say FFFFFFFFFFUUUUUUUUUUU every time I experience a segfault. My hobby programs at home are not that critical, and at work the critical code is *proven* to be correct so no need to worry there.
September 28, 2009
Yigal Chripun:

>Have you ever heard of Stalin (i'm not talking about the dictator)?<

Stalin accepts only a certain subset of Scheme, you can't use some of the nicest things.
And while ShedSkin is slow, Stalin is really slow, so slow that compiling largish programs becomes not handy (I think times like 100 seconds for 500 lines-long programs, I don't know if such timings have improved in the meantime, I hope so).


> the other way would be to write high level very regular code that can be maintained, easier to reason about and leave optimization to the tools.

Life is usually a matter of finding a balance. If you care of performance you don't use Scheme, you use a handy language that doesn't force the compiler to work a LOT, for example C#.

Bye,
bearophile
September 28, 2009
Mon, 28 Sep 2009 22:33:26 +0000, language_fan thusly wrote:

> Value types can be incorrectly initialized and nobody notices. E.g.
> 
>   int min;
> 
>   foreach(int value; list)
>     if (value < min) min = value;

> Now I can tell you, in functional languages there is no other way. All initializations have to be correct, they are final, they are constants and they can be initialized incorrectly. But there are some tools that help in this. Functions can be automatically tested. Invariants, pre- and post-conditions can be set. Still, I can even bet they are much safer than D in every possible way. How is this possible?

For instance if I use the example given above, I write it like this in a functional language:

find_min:: Ord a => [a] -> Maybe a
find_min [] = Nothing
find_min (h:t) = Just $ foldl min h t

You can then use quickcheck to verify the result in some fancy way.

I just cannot think of any way how you could crash programs written in this way. They are solid as a rock.
September 29, 2009
"language_fan" <foo@bar.com.invalid> wrote in message news:h9relp$1ebg$4@digitalmars.com...
> Mon, 28 Sep 2009 22:33:26 +0000, language_fan thusly wrote:
>
>> Value types can be incorrectly initialized and nobody notices. E.g.
>>
>>   int min;
>>
>>   foreach(int value; list)
>>     if (value < min) min = value;
>
>> Now I can tell you, in functional languages there is no other way. All initializations have to be correct, they are final, they are constants and they can be initialized incorrectly. But there are some tools that help in this. Functions can be automatically tested. Invariants, pre- and post-conditions can be set. Still, I can even bet they are much safer than D in every possible way. How is this possible?
>
> For instance if I use the example given above, I write it like this in a functional language:
>
> find_min:: Ord a => [a] -> Maybe a
> find_min [] = Nothing
> find_min (h:t) = Just $ foldl min h t
>
> You can then use quickcheck to verify the result in some fancy way.
>
> I just cannot think of any way how you could crash programs written in this way. They are solid as a rock.

I'm not particulary accustomed to that sort of syntax. Am I correct in my analysis that that essentially does something like this?:

// Assuming that:
// 1. Variables of type void could be declared and had value 'void'.
// 2. 'any(T,U,V)' was a "supertype" that can and must be one (and only one)
of T, U, or V.

immutable any(int,void) min(immutable any(int,void) a, immutable
any(int,void) b)
{
    static if( is(typeof(a) == void) && is(typeof(b) == void) )
        return void;
    else static if( is(typeof(a) == int) && is(typeof(b) == void) )
        return a;
    else static if( is(typeof(a) == void) && is(typeof(b) == int) )
        return b;
    else
        return a<b? a : b;
}

immutable any(int,void) findMin(immutable int[] list)
{
    static if(list.length == 0)
        return void;
    else
        return reduce!("min(a,b)")(list); // 'reduce' from phobos2
}


September 29, 2009
Mon, 28 Sep 2009 20:17:54 -0400, Nick Sabalausky thusly wrote:

> "language_fan" <foo@bar.com.invalid> wrote in message news:h9relp$1ebg$4@digitalmars.com...
>> Mon, 28 Sep 2009 22:33:26 +0000, language_fan thusly wrote:
>>
>>> Value types can be incorrectly initialized and nobody notices. E.g.
>>>
>>>   int min;
>>>
>>>   foreach(int value; list)
>>>     if (value < min) min = value;
>>
>>> Now I can tell you, in functional languages there is no other way. All initializations have to be correct, they are final, they are constants and they can be initialized incorrectly. But there are some tools that help in this. Functions can be automatically tested. Invariants, pre- and post-conditions can be set. Still, I can even bet they are much safer than D in every possible way. How is this possible?
>>
>> For instance if I use the example given above, I write it like this in a functional language:
>>
>> find_min:: Ord a => [a] -> Maybe a
>> find_min [] = Nothing
>> find_min (h:t) = Just $ foldl min h t
>>
>> You can then use quickcheck to verify the result in some fancy way.
>>
>> I just cannot think of any way how you could crash programs written in this way. They are solid as a rock.
> 
> I'm not particulary accustomed to that sort of syntax. Am I correct in my analysis that that essentially does something like this?:
> 
> // Assuming that:
> // 1. Variables of type void could be declared and had value 'void'. //
> 2. 'any(T,U,V)' was a "supertype" that can and must be one (and only
> one) of T, U, or V.
> 
> immutable any(int,void) min(immutable any(int,void) a, immutable
> any(int,void) b)
> {
>     static if( is(typeof(a) == void) && is(typeof(b) == void) )
>         return void;
>     else static if( is(typeof(a) == int) && is(typeof(b) == void) )
>         return a;
>     else static if( is(typeof(a) == void) && is(typeof(b) == int) )
>         return b;
>     else
>         return a<b? a : b;
> }
> 
> immutable any(int,void) findMin(immutable int[] list) {
>     static if(list.length == 0)
>         return void;
>     else
>         return reduce!("min(a,b)")(list); // 'reduce' from phobos2
> }

Well to be honest, I thought I knew how to read D, but this is starting to look a bit scary. It looks like it does almost the same. I just used lists instead of arrays since they are the basic data type in functional code. Second, the find_min accepted any type that implements the 'Ord' class, i.e. supports  the '<' relation, not only ints. I guess it could be solved by changing some pieces of code to look like this:

> immutable any(T,void) findMin(T)(immutable T[] list) {

My original idea was to just show that it is much harder to make similar kinds of errors with algebraic data types. I should have made a less generic :-)