Tuesday, August 6, 2019

Digest for comp.lang.c++@googlegroups.com - 21 updates in 5 topics

Sam <sam@email-scan.com>: Aug 05 08:14PM -0400

Scott Lurndal writes:
 
 
> >> Gee whiz, how can that possibly happen?
 
> >epoll(...), dear.
 
> How does epoll help with high volume disk I/O?
 
Now look what you've done. You confused him with facts. Shame on you.
"Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com>: Aug 05 11:43PM -0700

On 8/5/2019 1:42 AM, Juha Nieminen wrote:
 
> Is there a simple example of a situation where this is significantly
> beneficial compared to a lambda or an explicitly written functor
> (ie. a class with member variables and an operator())?
 
Damn. I thought they were trying to get something like a standard
getcontext/setcontext, or fibers.
leigh.v.johnston@googlemail.com: Aug 06 04:13AM -0700

On Monday, August 5, 2019 at 6:48:18 PM UTC+1, Scott Lurndal wrote:
 
> >> Gee whiz, how can that possibly happen?
 
> >epoll(...), dear.
 
> How does epoll help with high volume disk I/O?
 
How do threads help with high volume disk I/O, dear?
leigh.v.johnston@googlemail.com: Aug 06 04:23AM -0700


> > >epoll(...), dear.
 
> > How does epoll help with high volume disk I/O?
 
> How do threads help with high volume disk I/O, dear?
 
Of course by that I actually meant how does *lots* of threads help with high volume disk I /O, dear?
Bonita Montero <Bonita.Montero@gmail.com>: Aug 06 01:58PM +0200

> One way to think about coroutines is like threads, but
> cooperatively multitasking instead of preemptive multitasking.
 
It's not really multitasking since the corutine-context can only
live as long as the calling function. With cooperative multitasking
like with fibers in Windows, a fiber can end without stopping other
contexts.
David Brown <david.brown@hesbynett.no>: Aug 06 03:06PM +0200

On 05/08/2019 14:20, Juha Nieminen wrote:
 
> In other words, it sounds to me like coroutines are, essentially,
> lambda functions, or stateful functor objects, with a jump at the
> beginning of the function to the position in the code that last yielded.
 
The explanation here calls them "stackless":
 
<https://en.cppreference.com/w/cpp/language/coroutines>
 
But really, they are only "stackless" in the sense that C++ does not
actually need a stack. They have a local state - you can have normal
local variables, and a normal call chain of normal functions. This will
typically end up on a heap somewhere, but is effectively a local stack
for the coroutine execution state. It is entirely possible, given
simple enough coroutines and smart enough compilers, for this all to be
boiled down to a single allocatable block.
David Brown <david.brown@hesbynett.no>: Aug 06 03:18PM +0200

On 06/08/2019 15:06, David Brown wrote:
> for the coroutine execution state. It is entirely possible, given
> simple enough coroutines and smart enough compilers, for this all to be
> boiled down to a single allocatable block.
 
After reading some of Alf's posts in this thread, it looks like I was
wrong here and C++ coroutines will be more restricted than general
coroutines. If they can only yield from within the coroutine function
itself, not arbitrary external functions, then the compiler can figure
out the size of the local stack frame needed and generate it as a single
block, thus avoiding any variable sized stack.
 
 
A simple and lightweight version of this has been in use in embedded
systems in C for long time. For those that think Duff's device is cool,
macros are a joy, and the non-structured nature of "switch" is a great
idea, have a look at this:
 
<https://en.wikipedia.org/wiki/Protothread>
<http://dunkels.com/adam/pt/>
scott@slp53.sl.home (Scott Lurndal): Aug 05 05:48PM

>> networking tying hundreds of thousands threads together. Yes, threads.
 
>> Gee whiz, how can that possibly happen?
 
>epoll(...), dear.
 
How does epoll help with high volume disk I/O?
Manfred <noname@add.invalid>: Aug 06 04:24PM +0200

Excellent explanation, thanks!
 
On 8/5/2019 1:11 AM, Alf P. Steinbach wrote:
"Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com>: Aug 05 02:44PM -0700

On 8/5/2019 2:51 AM, Martijn van Buul wrote:
> a threaded solution would offer no parallelisation, so using threads here
> only serves to simplify the implementation of the consumer or producer -
> performancewise it's detrimental. [...]
 
Actually, single producer, single consumer queues are pretty nice. The
consumer thread can work on things without bothering the procuder
thread, and vise versa. The sync can be implemented without using any
atomic RMW.
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Aug 06 10:19PM +0100

On Tue, 6 Aug 2019 04:23:44 -0700 (PDT)
 
> > How do threads help with high volume disk I/O, dear?
 
> Of course by that I actually meant how does *lots* of threads help with
> high volume disk I /O, dear?
 
No, you have fallen victim to a classic misdirection play by Scott. The
claim made in response to a posting of mine, originally by someone by
the name of "Sam", was that native OS threads on linux (but not
windows) can deal equally well with the kind of i/o things that
coroutines are good at, and are easier to use. Both these propositions
are in my opinion wrong, but it stands as the argument.
 
High volume disk i/o is irrelevant to this issue, because coroutines
aren't well suited to high volume disk i/o, nor are poll/select/epoll.
Native threads on the other hand are reasonably well suited to this work
load because operations on high volume disks tend to be cpu-bound and
not i/o-bound. And apart from anything else, block devices representing
hard disks are always signalled as ready (at any rate, they are with
select and poll, I do not know about epoll, so poll and select are
useless with them).
 
The real answer to Scott is "how do coroutines help with high volume
disk i/o"? I agree with what seems to be his suggestion - that they
don't - so they are irrelevant to your posting about epoll.
"Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com>: Aug 06 03:19PM -0700

On 8/6/2019 2:19 PM, Chris Vine wrote:
 
> The real answer to Scott is "how do coroutines help with high volume
> disk i/o"? I agree with what seems to be his suggestion - that they
> don't - so they are irrelevant to your posting about epoll.
 
AIO should be fine, its POSIX but oh well:
 
http://man7.org/linux/man-pages/man7/aio.7.html
 
https://pubs.opengroup.org/onlinepubs/009695399/basedefs/aio.h.html
 
IOCP can be used with files, even memory mapped ones.
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Aug 07 12:52AM +0200

On 07.08.2019 00:38, Stefan Ram wrote:
 
> A coroutine is a function that contains
> either a coroutine-return-statement or
> an await-expression or a yield-expression.
 
You mean, "A C++20 coroutine is...".
 
General implementations of coroutines do not have that or corresponding
restriction.
 
Even if the restriction is a special case of coroutines, I think it can
be argued that "coroutine" is a misnomer for the C++20 thing. :-)
Because it implies much that isn't there.
 
---
 
By the way, I learned a bit more, by looking around at tutorials, and
I'm appalled at all the machinery it seems one must define for the
return type of even the simplest C++20 coroutine.
 
It's akin to the infamous 600+ line Microsoft OLE technology "Hello,
world!"...
 
Hopefully they'll get around to provide some default machinery, before
it's accepted in the standard.
 
 
Cheers!,
 
- Alf
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Aug 07 12:01AM +0100

On Tue, 6 Aug 2019 15:19:48 -0700
"Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com>
wrote: [snip]
> AIO should be fine, its POSIX but oh well:
 
> http://man7.org/linux/man-pages/man7/aio.7.html
 
> https://pubs.opengroup.org/onlinepubs/009695399/basedefs/aio.h.html
 
Yes as I understand it AIO works for asynchronous i/o with block devices
representing hard disks, even though you can't use select/poll and I
think epoll for them. But AIO is not my area and I have to say that I
can't really understand the point of it with disks: the issue with such
block devices is that in polling terms they are always available to
read or write in the absence of an error. For reading in particular,
you don't do it for the fun of it: you do it to process the data you
obtain, which is likely to require use of the CPU. So why not just use
threads without AIO? You are not dealing with network latency.
 
I notice incidentally that the documentation for glibc's implementation
of AIO says: "This has a number of limitations, most notably that
maintaining multiple threads to perform I/O operations is expensive
and scales poorly". All the more reason to do that particular work
synchronously I would have thought. The alternative seems to be to use
signals.
 
Do you have any experience of AIO? (As I say, I don't.)
ram@zedat.fu-berlin.de (Stefan Ram): Aug 06 10:38PM

>I'll address the conceptual only.
 
Not wanting to contradict anything,
just as an extension:
 
A coroutine is a function that contains
either a coroutine-return-statement or
an await-expression or a yield-expression.
Tim Rentsch <tr.17687@z991.linuxsc.com>: Aug 06 09:31AM -0700

Manfred writes:
 
> [C++20 to have "concepts"]
 
> To this point I think that, even if the feature itself is really good,
> the name "concept" is just a bad choice.
 
Me too. "Concept" is a terrible choice of name for this
construct.
 
> feature). Personally I would have preferred a name more directly
> coupled to "requirement" as in fact they are (we already have typedef,
> typereq might have been something clearer).
 
The name "constraint" isn't quite right because a C++ "concept"
doesn't yet constrain anything; it is more like what might be
called a "constraint-in-waiting".
 
The name "requirement" isn't quite right for much the same kinds
of reasons: by itself a C++ "concept" doesn't require anything.
We might call it a "statement of requirements", if that phrase
weren't so cumbersome. Also there are parts of C++ "concepts"
that use the keyword 'requires' and might themselves be called
"requirements", which is confusing.
 
A name I am starting to warm up to is "property". It fits with
lots of the examples on the cppreference page: the "Hashable"
property, the "Incrementable" property, the "Addable" property,
etc. When attaching a 'requires' constraint to a type T, we can
say "T must have the Hashable property", which to my ears sounds
better than "T must satisfy the Hashable requirements".
 
Just my thoughts, for what that might be worth.
Fran <flitterio@gmail.com>: Aug 06 04:05PM -0400

On Tue, 06 Aug 2019 09:31:00 -0700, Tim Rentsch
>lots of the examples on the cppreference page: the "Hashable"
>property, the "Incrementable" property, the "Addable" property,
>etc.
 
Those words sound like "capabilities" to me, which seems appropriate
to the suffix "...able" on those names.
--
Fran
David Brown <david.brown@hesbynett.no>: Aug 06 02:32PM +0200

On 05/08/2019 11:56, Öö Tiib wrote:
 
> #pragma GCC optimize "-fno-wrapv"
 
> The proposal does leave those optimizations beyond scope to keep
> itself simple.
 
There is a /huge/ difference between a flag that /adds/ semantics to the
C++ standard language, and one that /removes/ semantics.
 
Currently, gcc with an appropriate -std flag is quite close to standards
compliant for a variety of C++ (and C) standards. It is not perfect,
but AFAIK its as close as any other mainstream compiler. This is the
case regardless of -fwrapv or -fno-wrapv (the default). A user can
choose to add extra semantics to the language with -fwrapv if they find
it useful and are willing to pay the price in lost optimisations and
poorer safety and bug-catching tools.
 
But if C++20 /requires/ wrapping semantics, then there is no longer a
choice. If the compiler allows "-fno-wrapv", then (in that mode) it
will no longer be a compliant C++20 compiler. Code written correctly to
C++20 could fail to work correctly - and it could easily be silent
failures. (If you are using special modes, like "-no-exceptions", code
that relies on exceptions will have loud compile-time failures.) Such
silent non-compliance would not be acceptable.
 
 
> basically turning bad behavior that does who knows what into reliable
> bad behavior that raises signals, throws exceptions, breaks or
> terminates.
 
You misunderstand me. For debugging purposes, consistent bad behaviour
is useful. If the program crashes or does something weird in the same
place each test run, it is a lot easier to find and fix the problem.
 
But when you are testing your 16-bit MS-DOS apple cart program, and you
add your 32,768th apple, it doesn't matter if the program prints out the
balance as -32,768 apples or if it fails to print out anything - you can
find the problem.
 
With undefined behaviour on overflow, rather than wrapping behaviour,
there is a solid chance you will get consistent effects from any
particular compilation - but the details may change with other changes
to the code or other compilation options. What you don't get is
/predictable/ effects - but that doesn't matter for debugging. If you
had been predicting effects properly, you wouldn't have the bug in the
first place!
 
And with overflow being undefined behaviour, and therefore always an
error, you can use tools like gcc's sanitizer to give you a clear,
consistent and helpful debug message when the problem occurs.
 
 
Any tool or feature that makes it easier to find mistakes as early as
possible, is a good tool in my book. Having integer overflow as
undefined behaviour makes this easier - that is the most important
reason I have for wanting it. Wrapping overflow makes bugs /harder/ to
find. It increases the chance that the errors will go unnoticed, by
quietly continuing with invalid results.
 
>> settings to disable the warning? Isn't that a little inconsistent?
 
> Yes, wrapping feature makes logical defects to behave more predictably
> and Yes, I consider it good.
 
That makes no sense. You prefer the behaviour of your defects to be
predictable? To be useful, that would mean you would have to know your
code has a defect - and in that case, surely you would fix the code
rather than wanting to run it with predictable errors?
 
The nearest use would be for debugging, where you want to work backwards
from the bad effect you get to figure out what caused it.
Predictability is sometimes useful then, but two's complement wrapping
is not nearly as helpful as trap on overflow behaviour - which would be
impossible when you have wrapping as part of the language definition.
 
> Yes, wrapping feature is sometimes useful
> also on its own.
 
Occasionally, yes. It happens often enough that it is important the
language has this capability. It happens rarely enough that it is not a
problem to have somewhat ugly code to do it. For all platforms where
you have two's complement representation of signed integers, you can
handle this with conversions to and from unsigned types. It's not hard
to wrap it all in a simple class if you want neater code.
 
> Yes, there are compiler intrinsic functions so I can
> live without the feature.
 
Unsigned arithmetic is not a compiler intrinsic. You don't need to
detect overflow to get wrapping behaviour.
 
(Having said that, I would like to see some standard library classes
that cover the features of many common intrinsics, such as gcc and
clang's overflow builtins.)
 
> Yes, I would still like warnings. Yes, I can
> live without warnings. Yes, way to disable warnings can be good. Yes,
> way to enable non-wrapping optimizations can be good.
 
If wrapping behaviour is required for the language standard, you won't
get these.
 
> in rest. I am not sure how it all is inconsistent. There just are
> priorities what I favor more and these priorities are likely bit
> different for all people.
 
What is inconsistent is to want a feature that is almost always an
indication of incorrect code.
 
 
> That would be even better indeed, but what the proposal suggested
> was simpler to implement and to add to standard leaving that possible
> but beyond scope of it.
 
The proposal suggests something that I am convinced is a huge step
backwards (making wrapping behaviour required), while failing to provide
a simple, standardised way to let programmers make choices.
 
(I note the proposal suggests, as you do, that compilers could still
have flags like "-fno-wrapv". To me, this shows that the proposal
authors are making the same mistake you are about the consequences of
changing the semantics of the language.)
 
 
>> Again, what do you think does not work with -fwrapv?
 
> I have used it rarely and experimentally. It did sometimes optimize
> int i loops when it should not.
 
That tells me nothing, I am afraid.
 
> to 10% performance difference on extreme case but that is again about
> requiring some "-fno-wrapv" to allow compiler to do that optimization
> not other way around. When currently "-fwrapv" is defective and
 
Without further references than a vague memory of something that wasn't
quite what you expected, I will continue to assume that "-fwrapv" is
/not/ defective and works exactly as it says it will. Show me examples,
bug reports, or something more definite and I will change that
assumption. As I have said before, no one claims gcc is bug-free.
 
> std::numeric_limits<int>::is_modulo is false then it is valid
> to weasel out of each such case by saying that it is not a bug.
 
In gcc, is_modulo is false because ints are not guaranteed to be
wrapping. They /might/ have wrapping behaviour, depending on the flags
and the code in question, but won't necessarily have it.
 
Should the value of is_modulo be dependent on whether -fwrapv is in
force or not? I don't think so - I think making these features
dependent on compiler settings would open a large can of worms.
Remember, you are free to change the -fwrapv setting in the middle of a
file using pragmas, or with function attributes - I would not like to
see is_modulo changing to fit. It is better to keep it at the
pessimistic "false" setting. (That is, at least, my opinion on this
particular matter - but I can see why some people could think differently.)
 
> It reduces effort of finding and fixing when these defects behave more
> uniformly. Usage of various debugging tools is good idea that helps
> to reduce that effort too but is orthogonal to it and not in conflict.
 
I accept what you are saying, but I think you are wrong. I disagree
that wrapping overflow is useful for debugging, as I explained earlier.
 
However, compilers like gcc give you the choice. Add "-fwrapv" when you
find it helps with debugging - just as you might add sanitizer options
and warning options. It does not have to be part of the language, which
would reduce choice and options and limit your debugging tools.
 
 
> Thanks, you have point there. If people will start to use that wrapping
> behavior a lot for to achieve various effects then diagnosing it will
> become more and more of false positive for those people.
 
Indeed.
 
A key problem with relying on wrapping behaviour is that it is very
subtle - it is typically invisible in the code, without studying it in
depth.
 
> I suspect that people will use it only on limited but important cases
> (like for self-diagnosing or for cryptography).
 
Cryptography would typically use unsigned types.
 
Having library features for detecting overflow would be clearer, safer,
and more portable than relying on wrapping behaviour for those that want
to check for problems after they have done their arithmetic that might
overflow. I really dislike the concept of running into the traffic and
then checking if you have been run over, instead of looking first and
crossing when it is safe. It would be better to have types similar to
std::optional which track the validity of operations - letting you
happily perform arithmetic and at the end check for any failures.
 
A big issue I have with the whole concept is that currently we have
/types/ for which overflow is defined (unsigned types) and types for
which it is not defined. But overflow behaviour should be part of the
operations, not the types.
 
 
> Other possible option
> would to standardize compiler intrinsic functions for those cases.
 
I would say making a standard library section that has the required
behaviour - compilers can implement this using intrinsics or builtins if
they like.
 
> to mark or to rewrite questionable places to suppress false positive
> diagnostics about well-defined code. I likely miss some depth of it
> or am too naive about something else and it is hard to predict future.
 
Predictions are hard, especially about the future!
David Brown <david.brown@hesbynett.no>: Aug 06 02:54PM +0200

On 31/07/2019 10:14, Bonita Montero wrote:
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0907r0.html
 
I've done a little digging, and there are several revisions to that
paper. The latest (AFAICS) is:
 
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0907r4.html>
 
 
One change in later revisions that is relevant to some parts of this
thread is:
 
"""
*Status-quo* If a signed operation would naturally produce a value that
is not within the range of the result type, the behavior is undefined.
The author had hoped to make this well-defined as wrapping (the
operations produce the same value bits as for the corresponding unsigned
type), but WG21 had strong resistance against this.
"""
 
It seems I am not the only one who thinks this way.
 
I think this revision will be a lot less controversial - it mostly
documents the way current compilers work on current hardware, and
simplifies the wording in the standard to match.
 
 
 
I found another related paper:
 
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1236r1.html>
 
I think the two papers cover the same ideas, but with different wording.
I don't know what will actually end up in C++20.
M Powell <forumsmp@gmail.com>: Aug 05 05:22PM -0700

On Sunday, August 4, 2019 at 12:02:06 AM UTC-4, Alf P. Steinbach wrote:
 
> Anyway...
 
> If the network has sufficient bandwidth then I don't see what the
> problem is.
 
Got it. Thanks Alf
M Powell <forumsmp@gmail.com>: Aug 05 05:24PM -0700

On Sunday, August 4, 2019 at 8:57:40 AM UTC-4, Paavo Helde wrote:
> especially the variations in its performance. And the 10 kHz number is
> not something special, similar problems are there always when you
> require something to happen during some fixed time.
 
Got it. Thanks Paavo
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page.
To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com.

No comments: