March 14, 2014
On Thursday, 13 March 2014 at 13:47:13 UTC, Steven Schveighoffer wrote:
> On Thu, 13 Mar 2014 09:37:51 -0400, Dicebot <public@dicebot.lv> wrote:
>
>> On Thursday, 13 March 2014 at 13:16:54 UTC, Daniel Murphy wrote:
>>> "Steven Schveighoffer"  wrote in message news:op.xcnu55j2eav7ka@stevens-macbook-pro.local...
>>>
>>>> > The worst breaking change in D2, by far, is the prevention
>>>> > > of
>>>> array stomping.
>>>>
>>>> What is your use case(s), might I ask? Prevention of array stomping, I thought, had a net positive effect on performance, because it no longer has to lock the GC for thread-local appends.
>>>
>>> I would guess they're setting length to zero and appending to re-use the memory.
>>
>> Exactly. So far looks like upon transition to D2 almost all arrays used in our code will need to be replaced with some variation of Appender!T
>
> I think you might find that it will run considerably faster in that case. In the old mechanism of D1, the GC lock was used on every append, and if you had multiple threads appending simultaneously, they were contending with the single element cache to look up block info. Appender only needs to look up GC block info when it needs more memory from the GC.

We don't use threads.
March 14, 2014
On 14 March 2014 08:51, 1100110 <0b1100110@gmail.com> wrote:
> On 3/14/14, 3:02, Walter Bright wrote:
>>
>> On 3/14/2014 12:34 AM, 1100110 wrote:
>>>
>>> ...And code duplication everywhere!
>>
>>
>> Actually, very little of that.
>
> I don't know what you'd call this then...
> Exact same bit of code, repeated multiple times for versions which could be
> OR'd together.
>
>
>     version (X86)
>     {
>         enum RTLD_LAZY = 0x00001;
>         enum RTLD_NOW = 0x00002;
>         enum RTLD_GLOBAL = 0x00100;
>         enum RTLD_LOCAL = 0x00000;
>     }
>     else version (X86_64)
>     {
>         enum RTLD_LAZY = 0x00001;
>         enum RTLD_NOW = 0x00002;
>         enum RTLD_GLOBAL = 0x00100;
>         enum RTLD_LOCAL = 0x00000;
>     }
>     else version (MIPS32)
>     {
>         enum RTLD_LAZY = 0x0001;
>         enum RTLD_NOW = 0x0002;
>         enum RTLD_GLOBAL = 0x0004;
>         enum RTLD_LOCAL = 0;
>     }
>     else version (PPC)
>     {
>         enum RTLD_LAZY = 0x00001;
>         enum RTLD_NOW = 0x00002;
>         enum RTLD_GLOBAL = 0x00100;
>         enum RTLD_LOCAL = 0;
>     }
>     else version (PPC64)
>     {
>         enum RTLD_LAZY = 0x00001;
>         enum RTLD_NOW = 0x00002;
>         enum RTLD_GLOBAL = 0x00100;
>         enum RTLD_LOCAL = 0;
>     }
>     else version (ARM)
>     {
>         enum RTLD_LAZY = 0x00001;
>         enum RTLD_NOW = 0x00002;
>         enum RTLD_GLOBAL = 0x00100;
>         enum RTLD_LOCAL = 0;
>     }
>     else version (AArch64)
>     {
>         enum RTLD_LAZY = 0x00001;
>         enum RTLD_NOW = 0x00002;
>         enum RTLD_GLOBAL = 0x00100;
>         enum RTLD_LOCAL = 0;
>     }
>
> Yeah there are a few differences, but it would be trivial to collapse this down...
>
> Just for funsies:
>
>     version (X86 || X86_64)
>     {
>         enum RTLD_LAZY = 0x00001;
>         enum RTLD_NOW = 0x00002;
>         enum RTLD_GLOBAL = 0x00100;
>         enum RTLD_LOCAL = 0x00000;
>     }
>     else version (MIPS32)
>     {
>         enum RTLD_LAZY = 0x0001;
>         enum RTLD_NOW = 0x0002;
>         enum RTLD_GLOBAL = 0x0004;
>         enum RTLD_LOCAL = 0;
>     }
>     else version (PPC)
>     {
>         enum RTLD_LAZY = 0x00001;
>         enum RTLD_NOW = 0x00002;
>         enum RTLD_GLOBAL = 0x00100;
>         enum RTLD_LOCAL = 0;
>     }
>     else version (PPC64 || ARM || AArch64)
>     {
>         enum RTLD_LAZY = 0x00001;
>         enum RTLD_NOW = 0x00002;
>         enum RTLD_GLOBAL = 0x00100;
>         enum RTLD_LOCAL = 0;
>     }
>
>
> Oh wait, isn't 0x00000 the same as 0? (I honestly don't know if that matters, but assuming it doesn't...)
>
>     version (X86 || X86_64 || PPC || PPC64 || ARM || AArch64)
>     {
>         enum RTLD_LAZY = 0x00001;
>         enum RTLD_NOW = 0x00002;
>         enum RTLD_GLOBAL = 0x00100;
>         enum RTLD_LOCAL = 0x00000;
>     }
>     else version (MIPS32)
>     {
>         enum RTLD_LAZY = 0x0001;
>         enum RTLD_NOW = 0x0002;
>         enum RTLD_GLOBAL = 0x0004;
>         enum RTLD_LOCAL = 0;
>     }
>
> Huh, for not having any code duplication it sure is a hell of a lot shorter when combined...


This is exactly the problem I wanted to avoid in druntime.  Someone needs to pull their finger out and decide how we are going to tackle the porting chasm we are heading into.
March 14, 2014
On Fri, 14 Mar 2014 08:51:05 -0000, 1100110 <0b1100110@gmail.com> wrote:
>
>      version (X86 || X86_64 || PPC || PPC64 || ARM || AArch64)
>      {
>          enum RTLD_LAZY = 0x00001;
>          enum RTLD_NOW = 0x00002;
>          enum RTLD_GLOBAL = 0x00100;
>          enum RTLD_LOCAL = 0x00000;
>      }
>      else version (MIPS32)
>      {
>          enum RTLD_LAZY = 0x0001;
>          enum RTLD_NOW = 0x0002;
>          enum RTLD_GLOBAL = 0x0004;
>          enum RTLD_LOCAL = 0;
>      }

Walter's point, I believe, is that you should define a meaningful version identifier for each specific case, and that this is "better" because then you're less concerned about where it's supported and more concerned with what it is which is/isn't supported.

Maintenance is very slightly better too, IMO, because you add/remove/alter a complete line rather than editing a set of || && etc which can in some cases be a little confusing.  Basically, the chance of an error is very slightly lower.

For example, either this:

version(X86) version = MeaningfulVersion
version(X86_64) version = MeaningfulVersion
version(PPC) version = MeaningfulVersion
version(PPC64) version = MeaningfulVersion
version(ARM) version = MeaningfulVersion
version(AArch64) version = MeaningfulVersion

version(MeaningfulVersion)
{
}
else version (MIPS32)
{
}

or this:

version (X86) version = MeaningfulVersion
version (X86_64) version = MeaningfulVersion
version (PPC) version = MeaningfulVersion
version (PPC64) version = MeaningfulVersion
version (ARM) version = MeaningfulVersion
version (AArch64) version = MeaningfulVersion

version (MIPS32) version = OtherMeaningfulVersion

version (MeaningfulVersion)
{
}
else version (OtherMeaningfulVersion)
{
}

Regan

-- 
Using Opera's revolutionary email client: http://www.opera.com/mail/
March 14, 2014
On Thursday, 13 March 2014 at 19:28:59 UTC, Walter Bright wrote:
> On 3/13/2014 1:43 AM, Don wrote:
>> The worst breaking change in D2, by far, is the prevention of array stomping.
>>
>> After that change, our code still runs, and produces exactly the same results,
>> but it is so slow that it's completely unusable. This one of the main reasons
>> we're still using D1.
>
> I didn't know this. I'd like more details - perhaps I can help with how to deal with it.

Our entire codebase assumes that stomping will happen. Simplest example:

T[] dupArray(T)(ref T[] dest, T[] src)
{
    dest.length = src.length;
    if (src.length) {
        dest[] = src[];
    }
    return dest;
}

This is equivalent to dest = src.dup, but if dest was already long enough to contain src, no allocation occurs.

Sure, we can add a call to "assumeSafeAppend()" everywhere. And I mean *everywhere*. Every single instance of array creation or concatentation, without exception. Almost every array in our codebase is affected by this.
March 14, 2014
On Thu, 13 Mar 2014 21:42:43 -0000, Walter Bright <newshound2@digitalmars.com> wrote:

> On 3/13/2014 1:09 PM, Andrei Alexandrescu wrote:
>> Also let's not forget that a bunch of people will have not had contact with the
>> group and will not have read the respective thread. For them -- happy campers
>> who get work done in D day in and day out, feeling no speed impact whatsoever
>> from a virtual vs. final decision -- we are simply exercising the brunt of a
>> deprecation cycle with undeniable costs and questionable (in Walter's and my
>> opinion) benefits.
>
> Also,
>
>      class C { final: ... }
>
> achieves final-by-default and it breaks nothing.

Yes.. but doesn't help Manu or any other consumer concerned with speed if the library producer neglected to do this.  This is the real issue, right?  Not whether class *can* be made final (trivial), but whether they *actually will* *correctly* be marked final/virtual where they ought to be.

Library producers range in experience and expertise and are "only human" so we want the option which makes it more likely they will produce good code.  In addition we want the option which means that if they get it wrong, less will break if/when they want to correct it.


Final by default requires that you (the library producer) mark as virtual the functions you intend to be inherited from.  Lets assume the library producer has a test case where s/he does just this, inherits from his/her classes and overrides methods as they see consumers doing.  The compiler will detect any methods not correctly marked.  So, there is a decent chance that producers will get this "right" w/ final by default.

If they do get it wrong, making the change from final -> virtual does not break any consumer code.


Compare that to virtual by default where marking everything virtual means it will always work, but there is a subtle and unlikely to be detected/tested performance penalty.  There is no compiler support for detecting this, and no compiler support for correctly identifying the methods which should be marked final.  In fact, you would probably mark them all final and then mark individual functions virtual in order to solve this.

If they get it wrong, making the change from virtual -> final is more likely to break consumer code.


I realise you're already aware of the arguments for final by default, and convinced it would have been the best option, but it also seems to me that the "damage" that virtual by default will cause over the future lifetime of D, is greater than a well controlled deprecation path from virtual -> final would be.

Even without a specific tool to aid deprecation, the compiler will output clear errors for methods which need to be marked virtual, granted this requires you compile a program which "uses" the library but most library producers should have such a test case already, and their consumers could help out a lot by submitting those errors directly.

Regan.

-- 
Using Opera's revolutionary email client: http://www.opera.com/mail/
March 14, 2014
On 3/14/14, 4:58, Regan Heath wrote:
>
> Maintenance is very slightly better too, IMO, because you
> add/remove/alter a complete line rather than editing a set of || && etc
> which can in some cases be a little confusing.  Basically, the chance of
> an error is very slightly lower.
>
> For example, either this:
>
> version(X86) version = MeaningfulVersion
> version(X86_64) version = MeaningfulVersion
> version(PPC) version = MeaningfulVersion
> version(PPC64) version = MeaningfulVersion
> version(ARM) version = MeaningfulVersion
> version(AArch64) version = MeaningfulVersion
>
> version(MeaningfulVersion)
> {
> }
> else version (MIPS32)
> {
> }
>
> or this:
>
> version (X86) version = MeaningfulVersion
> version (X86_64) version = MeaningfulVersion
> version (PPC) version = MeaningfulVersion
> version (PPC64) version = MeaningfulVersion
> version (ARM) version = MeaningfulVersion
> version (AArch64) version = MeaningfulVersion
>
> version (MIPS32) version = OtherMeaningfulVersion
>
> version (MeaningfulVersion)
> {
> }
> else version (OtherMeaningfulVersion)
> {
> }
>
> Regan
>


...I can't even begin to describe how much more readable the OR'd version is.
March 14, 2014
Another option is using pure functions and static if's.

const bool ProgrammingSections = getProgrammingSections;
const bool PasteBinFullSection = getPasteBinFullSection;

pure bool getProgrammingSections() {
	if (CompleteSite) {
		return true;
	} else {
		return false;
	}
}

pure bool getPasteBinFullSection() {
	if (CompleteSite || ProgrammingSections) {
		return true;
	} else {
		return false;
	}
}

static if (ProgrammingSections) {
    // yay programming
}


static if (PasteBinFullSection) {
    // Ooo pastebin!
}


Also works rather well through different modules.
March 14, 2014
"Walter Bright"  wrote in message news:lfsv9r$263r$1@digitalmars.com...

> Nope. It's a very pragmatic decision.

Errr...

> I've seen some of those hoops programmers have done to get that behavior in D, and it resulted in just what I predicted - confusing bugs due to wacky dependencies between modules.
>
> Bluntly, if your code requires more than version(Feature) you are doing it wrong.
>
> I haven't yet seen an example of boolean version expressions that made the code clearer, simpler, or more maintainable than version(Feature).

I agree!  Writing code this way pretty much always leads to better organisation and encourages separating things by feature instead of irrelevant things like host platform.

HOWEVER - forcing this on everybody all the time is not a good thing.  Not all code is for a long term or large project.

A similar example is the common rule that functions should be short and do one thing.  This is a great rule to keep things sane - but if the compiler errored when my functions got too long it would just be a huge pain in my ass.

Why don't we ban goto?  It can certainly be used to write confusing and unmaintainable code!  So can switch!  Operator overloading can be awful too!

Most of the time, D gives me all the power and lets me decide how I use it. If I wanted those choices to be taken away from me, I'd be using go.

> I've seen endless examples of boolean version expressions that are a rat's nest of unmaintainable, buggy, confusing garbage. I've made a major effort to remove all that garbage from dmd's source code, for example, and am very pleased with the results. There's still some in druntime that I wish to get refactored out.

Of course, DMD is still full of this.  DDMD has to use static if instead of version because of this.

> And yes, I'm pretty opinionated about this :-)

:-)  That's fine, and I think you're right about it.  But like "don't use goto" a compile error is the wrong place to enforce this. 

March 14, 2014
On Friday, 14 March 2014 at 10:30:47 UTC, Rikki Cattermole wrote:
> Another option is using pure functions and static if's.
>
> const bool ProgrammingSections = getProgrammingSections;
> const bool PasteBinFullSection = getPasteBinFullSection;
>
> pure bool getProgrammingSections() {
> 	if (CompleteSite) {
> 		return true;
> 	} else {
> 		return false;
> 	}
> }
>
> pure bool getPasteBinFullSection() {
> 	if (CompleteSite || ProgrammingSections) {
> 		return true;
> 	} else {
> 		return false;
> 	}
> }
>
> static if (ProgrammingSections) {
>     // yay programming
> }
>
>
> static if (PasteBinFullSection) {
>     // Ooo pastebin!
> }
>
>
> Also works rather well through different modules.

Or for more advanced usage that could be quite useful in phobos:

pure bool isVersionDefinedAnd(ARGS...)() {
	foreach(name; ARGS) {
		mixin("version(" ~ name ~ ") {} else return false;");
	}
	return true;	
}

enum isLinux1And = isVersionDefinedAnd!("linux");
enum isLinux2And = isVersionDefinedAnd!("linux", "Posix");

pragma(msg, isLinux1And);
pragma(msg, isLinux2And);


pure bool isVersionDefinedOr(ARGS...)() {
	foreach(name; ARGS) {
		mixin("version(" ~ name ~ ") return true;");
	}
	return false;	
}

enum isMainPlatform = isVersionDefinedOr!("linux", "Windows", "Posix", "Android", "OSX");

pragma(msg, isMainPlatform);
March 14, 2014
"Walter Bright"  wrote in message news:lfu74a$8cr$1@digitalmars.com...

> > No, it doesn't, because it is not usable if C introduces any virtual methods.
>
> That's what the !final storage class is for.

My mistake, I forgot you'd said you were in favor of this.  Being able to 'escape' final certainly gets us most of the way there.

!final is really rather hideous though.