May 10, 2017
On 5/10/17 8:28 AM, H. S. Teoh via Digitalmars-d wrote:
>> C++'s fundamental paradigm has always been "Premature-optimization
>> oriented programming". C++ promotes POOP.
>
> LOL!!
>
> Perhaps I'm just being cynical, but my current unfounded hypothesis is
> that the majority of C/C++ programmers don't use a profiler, and don't
> *want* to use a profiler, because they're either ignorant that such
> things exist (unlikely), or they're too dang proud to admit that their
> painfully-accumulated preconceptions about optimization might possibly
> be wrong.
>
> Or maybe my perceptions are just heavily colored by the supposedly
> "expert" C coders I've met, who wrote supposedly better code that I
> eventually realized was actually not better, but in many ways actually
> worse -- less readable, less maintainable, more error-prone to write,
> and at the end of the day arguably less performant because it ultimately
> led to far too much boilerplate and other sources of code bloat,
> excessive string copying, too much indirection (cache unfriendliness),
> and other such symptoms that C coders often overlook.

Just to add a different perspective - the people I work with is the kind of guys who know when not to trust the profiler and what to try if the profile is flat. There is no question raised that you should run it, it's just assumed you always do.

P.S. Can't wait to see "Are we fast yet?" graph for Phobos functions.

---
Dmitry Olshansky
May 10, 2017
On Wednesday, 10 May 2017 at 06:28:31 UTC, H. S. Teoh wrote:
> On Tue, May 09, 2017 at 09:19:08PM -0400, Nick Sabalausky (Abscissa) via Digitalmars-d wrote:
>> On 05/09/2017 08:30 PM, H. S. Teoh via Digitalmars-d wrote:
>> > 
>> > In this sense I agree with Walter that warnings are basically useless, because they're not enforced. Either something is correct and compiles, or it should be an error that stops compilation. Anything else, and you start having people ignore warnings.
>> > 
>> 
>> Not 100% useless. I'd much rather risk a warning getting ignored that NOT be informed of something the compiler noticed but decided "Nah, some people ignore warnings so I'll just look the other way and keep my mouth shut".  (Hogan's Compiler Heroes: "I see NUH-TING!!")
>
> I'd much rather the compiler say "Hey, you! This piece of code is probably wrong, so please fix it! If it was intentional, please write it another way that makes that clear!" - and abort with a compile error.
>
> This is actually one of the things I like about D. For example, if you wrote:
>
> 	switch (e) {
> 		case 1: return "blah";
> 		case 2: return "bluh";
> 	}
>
> the compiler will refuse to compile the code until you either add a default case, or make it a final switch (in which case the compiler will refuse the compile the code unless every possible case is in fact covered).
>
> Now imagine if this was merely a warning that people could just ignore.
>
> Yep, we're squarely back in good ole C/C++ land, where an unexpected value of e causes the code to amble down an unexpected path, with the consequent hilarity that ensues.
>
> IOW, it should not be possible to write tricky stuff by default; you should need to ask for it explicitly so that intent is clear.  Another switch example:
>
> 	switch (e) {
> 		case 1: x = 2;
> 		case 2: x = 3;
> 		default: x = 4;
> 	}
>
> In C, the compiler happily compiles the code for you.  In D, at least the latest dmd will give you deprecation warnings (and presumably, in the future, actual compile errors) for forgetting to write `break;`. But if the fallthrough was intentional, you document that with an explicit `goto case ...`. IOW, the default behaviour is the safe one (no fallthrough), and the non-default behaviour (fallthrough) has to be explicitly asked for.  Much, much better.
>
>
>> And then the flip side is that some code smells are just to pedantic to justify breaking the build while the programmer is in the middle of some debugging or refactoring or some such.
>> 
>> That puts me strongly in the philosophy of "Code containing warnings: Allowed while compiling, disallowed when committing (with allowances for mitigating circumstances)."
>
> I'm on the fence about the former.  My current theory is that being forced to write "proper" code even while refactoring actually helps the quality of the resulting code.   But I definitely agree that code with warnings should never make it into the code repo.  The problem is that it's not enforced by the compiler, so *somebody* somewhere will inevitably bypass it.
>
>
>> C/C++ doesn't demonstrate that warnings are doomed to be useless and "always" ignored. What it demonstrates is that warnings are NOT an appropriate strategy for fixing language problems.
>
> Point.  I suppose YMMV, but IME unless warnings are enforced with -Werror or equivalent, after a while people just stop paying attention to them, at least where I work.  It's entirely possible that it's a bias specific to my job, but somehow I have a suspicion that this isn't completely the case.  Humans tend to be lazy, and ignoring compiler warnings is rather high up on the list of things lazy people tend to do. The likelihood increases with the presence of other factors like looming deadlines, unreasonable customer requests, ambiguous feature specs handed down from the PTBs, or just plain having too much on your plate to be bothering with "trivialities" like fixing compiler warnings.
>
> That's why my eventual conclusion is that anything short of enforcement will ultimately fail. Unless there is no way you can actually get an executable out of badly-written code, there will always be *somebody* out there that will write bad code. And by Murphy's Law, that somebody will eventually be someone in your team, and chances are you'll be the one cleaning up the mess afterwards.  Not something I envy doing (I've already had to do too much of that).
>
>
> [...]
>> The moral of this story: Sometimes, breaking people's code is GOOD! ;)
>
> Tell that to Walter / Andrei. ;-)
>
>
> [...]
>> > (Nevermind the elephant in the room that 80-90% of the "optimizations" C/C++ coders -- including myself -- have programmed into their finger reflexes are actually irrelevant at best, because either compilers already do those optimizations for you, or the hot spot simply isn't where we'd like to believe it is; or outright de-optimizing at worst, because we've successfully defeated the compiler's optimizer by writing inscrutable code.)
>> 
>> C++'s fundamental paradigm has always been "Premature-optimization oriented programming". C++ promotes POOP.
>
> LOL!!
>
> Perhaps I'm just being cynical, but my current unfounded hypothesis is that the majority of C/C++ programmers don't use a profiler, and don't *want* to use a profiler, because they're either ignorant that such things exist (unlikely), or they're too dang proud to admit that their painfully-accumulated preconceptions about optimization might possibly be wrong.

The likelihood of a randomly picked C/C++ programmer not even knowing what a profiler is, much less having used one, is extremely high in my experience. I worked with a lot of embedded C programmers with several years of experience who knew nothing but embedded C. We're talking dozens of people here. Not one of them had ever used a profiler. In fact, a senior developer (now tech lead) doubted I could make our build system any faster. I did by 2 orders of magnitude. When I presented the result to him he said in disbelief: "But, how? I mean, if it's doing exactly the same thing, how can it be faster?". Big O? Profiler? What are those? I actually stood there for a few seconds with my mouth open because I didn't know what to say back to him.

These people are also likely to raise concerns about performance during code review despite having no idea what a cache line is. They still opine that one shouldn't add another function call for readability because that'll hurt performance. No need to measure anything, we all know calling functions is bad, even when they're in the same file and the callee is `static`.

I think a lot of us underestimate just how bad the "average" developer is. A lot of them write C code, which is like giving chainsaws to chimpanzees.

> (And meanwhile, the mere mention of the two letters "G C" and they instantly recoil, and rattle of an interminable list of

That's cognitive dissonance: there's not much anyone can do about that. Unfortunately, facts don't matter, feelings do.

Atila
May 10, 2017
On 05/09/2017 10:26 PM, H. S. Teoh via Digitalmars-d wrote:
> On Wed, May 10, 2017 at 01:32:33AM +0000, Jack Stouffer via Digitalmars-d wrote:
>> On Wednesday, 10 May 2017 at 00:30:42 UTC, H. S. Teoh wrote:
>>> 		strncpy(tmp, desc->data1, bufsz);
>>> 		if (fwrite(tmp, strlen(tmp), 1, fp) != 1)
>>> 		{
>>> 			fclose(fp);
>>> 			unlink("blah");
>>> 			return IO_ERROR;
>>> 		}
>>>
>>> 		strncpy(tmp, desc->data2, bufsz);
>>> 		if (fwrite(tmp, strlen(tmp), 1, fp) != 1)
>>> 		{
>>> 			fclose(fp);
>>> 			unlink("blah");
>>> 			return IO_ERROR;
>>> 		}
>>
>> I think you cause a memory leak in these branches because you forget
>> to free tmp before returning.
>
> Well, there ya go. Case in point.

I caught that too but I thought you were testing whether we were listening. ;)

> Eventually, the idiom that I (and others) eventually converged on looks
> something like this:
>
> 	int myfunc(blah_t *blah, bleh_t *bleh, bluh_t *bluh) {
> 		void *resource1, *resource2, *resource3;
> 		int ret = RET_ERROR;
>
> 		/* Vet arguments */
> 		if (!blah || !bleh || !bluh)
> 			return ret;
>
> 		/* Acquire resources */
> 		resource1 = acquire_resource(blah->blah);
> 		if (!resource1) goto EXIT;
>
> 		resource2 = acquire_resource(bleh->bleh);
> 		if (!resource1) goto EXIT;

Copy paste error! :p (resource1 should be resource2.)

>
> 		resource3 = acquire_resource(bluh->bluh);
> 		if (!resource1) goto EXIT;

Ditto.

>
> 		/* Do actual work */
> 		if (do_step1(blah, resource1) == RET_ERROR)
> 			goto EXIT;
>
> 		if (do_step2(blah, resource1) == RET_ERROR)
> 			goto EXIT;
>
> 		if (do_step3(blah, resource1) == RET_ERROR)
> 			goto EXIT;
>
> 		ret = RET_OK;
> 	EXIT:
> 		/* Cleanup everything */
> 		if (resource3) release(resource3);
> 		if (resource2) release(resource2);
> 		if (resource1) release(resource1);
>
> 		return ret;
> 	}

As an improvement, consider hiding the checks and the goto statements in macros:

    resource2 = acquire_resource(bleh->bleh);
    exit_if_null(resource1);

    err = do_step2(blah, resource1);
    exit_if_error(err);

Or something similar... Obviously, it requires certain standardization like functions never having a goto statement, yet all having an EXIT area, etc. It makes C code very uniform, which is a good thing as you notice nonstandard idioms quickly.

This safer way of needing to do everything in steps of two lines is one of the reasons why I was convinced that exceptions are superior to return codes.

Ali

May 10, 2017
On Tuesday, May 09, 2017 07:13:31 Walter Bright via Digitalmars-d wrote:
> On 5/8/2017 1:55 PM, John Carter wrote:
> > On Saturday, 6 May 2017 at 06:26:29 UTC, Joakim wrote:
> >> Walter: I believe memory safety will kill C.
> >
> > C/C++ has been granted an extension of life by the likes of valgrind and purify and *-sanitizer.
>
> I agree. But one inevitably runs into problems relying on valgrind and other third party tools:

> 2. it may not be available on your platform

The fact that it's not available on Windows is extremely annoying. Some tools do exist on Windows, but you have to pay for them, and in my experience, they don't work very well. And with my current job, they _definitely_ don't work, because we mix C++ and C# (via COM). Nothing seems to be able to handle that mixture properly, and it's _really_ hard to track down memory problems.

> 4. it's incredibly slow to run valgrind, so there are powerful tendencies to skip it

There are cases where you literally _can't_ run it, because it's simply too slow. For instance, when dealing with live video from a camera, the odds are very high that under valgrind, the program won't be able to keep up. And if you're doing something like streaming 16 cameras at once (which happens in the security industry all the time), there's no way that it's going to work.

Valgrind is a fantastic tool, but saying that valgrind is enough is like saying that dynamic type checking is as good as compile-time type checking. It isn't, and it can't be.

So, yes, valgrind can be a lifesaver, but having preventing the bugs that it would find from even being possible is _far_ more valuable.

That being said, with the push for @nogc and the allocators and whatnot, we're then once again stuck needing to valgrind D code to catch bugs. It's still not as bad as C/C++, because the problems are much more restricted in scope, but avoiding the GC comes at a real cost.

Atila commented at dconf that working with allocators in D code for the excel wrapper library he had worked on was like he was stuck in C++ again with all of the memory problems that he had. @safe and the GC have _huge_ value.

- Jonathan M Davis

May 10, 2017
On Wednesday, 10 May 2017 at 06:28:31 UTC, H. S. Teoh wrote:
> On Tue, May 09, 2017 at 09:19:08PM -0400, Nick Sabalausky
[...]
> Perhaps I'm just being cynical, but my current unfounded hypothesis is that the majority of C/C++ programmers ...

Just a nitpick, could we also please stop conflating C and C++ programmers? My experience is that C++ programmer are completely clueless when it comes to C programming? They think they know C but it's generally far away. The thing is, that C has evolved with C99 and C11 and the changes have not all been adopted by C++ (and Microsoft actively stalling the adoption of C99 in Visual C didn't help either).
May 10, 2017
On Wednesday, 10 May 2017 at 11:16:57 UTC, Atila Neves wrote:
[...]
>
> The likelihood of a randomly picked C/C++ programmer not even knowing what a profiler is, much less having used one, is extremely high in my experience. I worked with a lot of embedded C programmers with several years of experience who knew nothing but embedded C. We're talking dozens of people here. Not one of them had ever used a profiler.

I've worked 10 years in embedded (industry, time acquisition and network gears) and I can say that there is a good reason to that. It's nearly impossible to profile in an embedded system (nowadays it's often possible because of the generalization of Linux and gnu tools but at that time it wasn't). The tools don't exist or if they do, the instrumentation breaks the constraints of the controller. This was also one of the reason we chose our embedded CPU's very carefully. We always chose processors for which there existed mainstream desktop versions so that we could at least use the confortable tooling to test some parts of the code on a nice environment. We used Z80 (CP/M), 80186 (MS-C on DOS) and then 68030 (Pure-C on Atari TT).

TL;DR profiling for embedded is order of magnitudes harder than for nice OS environments.

May 10, 2017
On Wednesday, 10 May 2017 at 05:26:11 UTC, H. S. Teoh wrote:
> 	int myfunc(blah_t *blah, bleh_t *bleh, bluh_t *bluh) {
> 		void *resource1, *resource2, *resource3;
> 		int ret = RET_ERROR;
>
> 		/* Vet arguments */
> 		if (!blah || !bleh || !bluh)
> 			return ret;
>
> 		/* Acquire resources */
> 		resource1 = acquire_resource(blah->blah);
> 		if (!resource1) goto EXIT;
>
> 		resource2 = acquire_resource(bleh->bleh);
> 		if (!resource1) goto EXIT;
>
> 		resource3 = acquire_resource(bluh->bluh);
> 		if (!resource1) goto EXIT;
>
> 		/* Do actual work */
> 		if (do_step1(blah, resource1) == RET_ERROR)
> 			goto EXIT;
>
> 		if (do_step2(blah, resource1) == RET_ERROR)
> 			goto EXIT;
>
> 		if (do_step3(blah, resource1) == RET_ERROR)
> 			goto EXIT;
>
> 		ret = RET_OK;
> 	EXIT:
> 		/* Cleanup everything */
> 		if (resource3) release(resource3);
> 		if (resource2) release(resource2);
> 		if (resource1) release(resource1);
>
> 		return ret;
> 	}
>

In modern C and with GLib (which makes use of a gcc/clang extension) you can write this as:

gboolean myfunc(blah_t *blah, bleh_t *bleh, bluh_t *bluh) {
        /* Cleanup everything automatically at the end */
        g_autoptr(GResource) resource1 = NULL, resource2 = NULL, resource3 = NULL;
        gboolean ok;

        /* Vet arguments */
        g_return_if_fail(blah != NULL, FALSE);
        g_return_if_fail(bleh != NULL, FALSE);
        g_return_if_fail(bluh != NULL, FALSE);

	/* Acquire resources */
	ok = acquire_resource(resource1, blah->blah);
	g_return_if_fail(ok, FALSE);

        ok = acquire_resource(resource2, bleh->bleh);
	g_return_if_fail(ok, FALSE);

	ok = acquire_resource(resource3, bluh->bluh);
	g_return_if_fail(ok, FALSE);

        /* Do actual work */
	ok = do_step1(blah, resource1);
	g_return_if_fail(ok, FALSE);

	ok = do_step2(blah, resource1);
	g_return_if_fail(ok, FALSE);

	return do_step3(blah, resource1);
}

Some random example of this style of coding: https://github.com/flatpak/flatpak/blob/master/common/flatpak-db.c

May 10, 2017
On Wednesday, 10 May 2017 at 12:18:40 UTC, Patrick Schluter wrote:
> On Wednesday, 10 May 2017 at 11:16:57 UTC, Atila Neves wrote:
> [...]
>>
>> The likelihood of a randomly picked C/C++ programmer not even knowing what a profiler is, much less having used one, is extremely high in my experience. I worked with a lot of embedded C programmers with several years of experience who knew nothing but embedded C. We're talking dozens of people here. Not one of them had ever used a profiler.
>
> I've worked 10 years in embedded (industry, time acquisition and network gears) and I can say that there is a good reason to that. It's nearly impossible to profile in an embedded system (nowadays it's often possible because of the generalization of Linux and gnu tools but at that time it wasn't). The tools don't exist or if they do, the instrumentation breaks the constraints of the controller. This was also one of the reason we chose our embedded CPU's very carefully. We always chose processors for which there existed mainstream desktop versions so that we could at least use the confortable tooling to test some parts of the code on a nice environment. We used Z80 (CP/M), 80186 (MS-C on DOS) and then 68030 (Pure-C on Atari TT).
>
> TL;DR profiling for embedded is order of magnitudes harder than for nice OS environments.

That doesn't mean they shouldn't know what a profiler is. The response would then be (assuming they're competent) "I wish I could use a profiler, but I can't because...", not "how can two programs output the same thing in different amounts of time".

Also, there's a good way around this sort of thing and it applies to testing as well: run the tools on a development machine (and the tests). Write portable standards-compliant code, make a thin wrapper where needed and suddendly you can write tests easily, run valgrind, use address sanitizer, ...

There's no good reason why you can't profile pure algorithms: C code is C code and has specified semantics whether it's running on a dev machine or a controller. The challenge is to write mostly pure code with thin IO wrappers. It's always a win/win though.

Atila
May 10, 2017
On Wednesday, 10 May 2017 at 11:50:32 UTC, Jonathan M Davis wrote:
> On Tuesday, May 09, 2017 07:13:31 Walter Bright via Digitalmars-d wrote:
>> On 5/8/2017 1:55 PM, John Carter wrote:

> Atila commented at dconf that working with allocators in D code for the excel wrapper library he had worked on was like he was stuck in C++ again with all of the memory problems that he had. @safe and the GC have _huge_ value.
>
> - Jonathan M Davis

Actually, it was worse than being back in C++ land: there I can use valgrind and address sanitizer. With D's allocators I was lost.

I'd forgotten how much "fun" it was to print pointer values to the terminal to track down memory bugs. It's especially fun when you're on Windows, your code is in a DLL loaded by a program you don't control and DebugViewer is your only friend.

Atila


May 10, 2017
On Wednesday, 10 May 2017 at 01:19:08 UTC, Nick Sabalausky (Abscissa) wrote:
> The moral of this story: Sometimes, breaking people's code is GOOD! ;)

I don't get the hate that compiler warnings get in the D community.

Sure you can disable them if you don't care, but then don't complain about C being inherently unsafe and bug-prone while praising D for breaking things.

Uninitialized variables is an example that I think does not need to be a language feature: If the compiler can prove the usage is sound, everything is fine.  The compiler has much deeper knowledge about the concrete case than static language rules.  If analysis fails, issue a warning.  Usually the problematic code is far from obvious and refactoring is a good idea.  If the programmer still thinks that no action is needed, just suppress that warning with a pragma.