Monday, August 5, 2019

Digest for comp.lang.c++@googlegroups.com - 7 updates in 1 topic

Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk>: Aug 05 06:32PM +0100

On 05/08/2019 02:32, Sam wrote:
> that all those supercomputers are just a bunch of machines with optimized
> networking tying hundreds of thousands threads together. Yes, threads.
 
> Gee whiz, how can that possibly happen?
 
epoll(...), dear.
 
/Flibble
 
--
"Snakes didn't evolve, instead talking snakes with legs changed into
snakes." - Rick C. Hodgin
 
"You won't burn in hell. But be nice anyway." – Ricky Gervais
 
"I see Atheists are fighting and killing each other again, over who
doesn't believe in any God the most. Oh, no..wait.. that never happens." –
Ricky Gervais
 
"Suppose it's all true, and you walk up to the pearly gates, and are
confronted by God," Bryne asked on his show The Meaning of Life. "What
will Stephen Fry say to him, her, or it?"
"I'd say, bone cancer in children? What's that about?" Fry replied.
"How dare you? How dare you create a world to which there is such misery
that is not our fault. It's not right, it's utterly, utterly evil."
"Why should I respect a capricious, mean-minded, stupid God who creates a
world that is so full of injustice and pain. That's what I would say."
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?
Bart <bc@freeuk.com>: Aug 05 08:19PM +0100

On 05/08/2019 09:42, 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())?
 
I don't get coroutines either. Since I have also implemented languages,
I sometimes put in features I've heard of which sound neat, although
that's often just for box-ticking as I then never use the feature again.
 
But coroutines, I wouldn't even bother ticking a box for.
 
However.... with 'generators', I do understand how they can be useful,
and which are something I've been thinking about adding. And generators,
I understand, can be implemented with coroutines.
 
(Although I would just create whatever I can see will be necessary. If I
end up inventing something like a coroutine, then that would be cool
too, even if I couldn't tell you what they are.)
 
Anyway, 'generators' might be one easy-to-understand application of
'coroutines'.
Christian Gollwitzer <auriocus@gmx.de>: Aug 05 10:26PM +0200

Am 05.08.19 um 10:42 schrieb Juha Nieminen:
> in such a manner that it continues from where it "returned" last time,
> rather than always from the beginning (which would happen with lambdas).
 
> Is that correct?
 
Similar to my understanding of it.
 
> 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())?
 
When you yield() from inside a loop. With coroutines, you can implement
generators - a functor which returns a value from a set, one at a time -
similar to iterators. You can write your code *as if* the yielding would
be a simple function call.
 
For example, consider a function which prints the time as hours:minutes
from midnight to midnight:
 
void printtime() {
for (int hour=0; hour<24; hour++) {
for (int min=0; min<60; min++) {
std::cout<<hour<<":"<<min<<std::endl;
}
}
}
 
Now you want a functor that returns the formatted string, one at a time,
for each call. Coroutine solution:*
 
void printtime() {
for (int hour=0; hour<24; hour++) {
for (int min=0; min<60; min++) {
std::ostringstream os;
os<<hour<<":"<<min;
co_yield(os.str());
}
}
}
 
 
The only significant change is print -> yield. If you had to write this
thing manually, you would need to unroll the loops and correctly switch
the state when the minutes surpass 60.
 
With two for loops, that is still practically doable, albeit
inconvenient - but now consider to implement an iterator for
breadth-first traversal of a tree. Trivial algorithm for printing:
 
void bftraverse(Tree* root) {
std::cout<<root->content;
if (root->left) bftraverse(root->left);
if (root->right) bftraverse(root->right);
}
 
Now please unroll this into a functor which produces one value at a
time. Good luck getting this right at the first time, basically that
would be reinventing an iterative algorithm from TAOCP.
 
Coroutine solution:
 
void bftraverse(Tree* root) {
co_yield(root->content);
if (root->left) bftraverse(root->left);
if (root->right) bftraverse(root->right);
}
 
voilá.
 
If you're patient, search for videos from Gor Nishanov, he has provided
some examples in C++ (tree traversal is one of them).
 
Christian
 
 
(*) I haven't tried C++ coroutines, so the syntax may be off. I'm using
them in Python and Tcl only
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Aug 05 11:09PM +0200

On 05.08.2019 22:26, Christian Gollwitzer wrote:
>    if (root->right) bftraverse(root->right);
> }
 
> voilá.
 
As I understand it this generates a new coroutine instance, with
dynamically allocated state, for each call.
 
It needs to do that (modulo optimization) to represent the call stack
for the recursive calls.
 
Thus it's convenient, but possibly/probably inefficient.
 
 
 
>     Christian
 
> (*) I haven't tried C++ coroutines, so the syntax may be off. I'm using
> them in Python and Tcl only
 
Same for me, except I'm only using my head. That's not intelligent,
because the C++20 coroutines are modeled on the Visual C++ coroutines,
except they've changed the keywords to more unreadable and tried to make
the documentation like impenetrable machine code. I wish I was more
intelligent, at least enough to just use the VC compiler I have...
 
Anyway, to avoid the dynamic allocation inefficiency I'd probably do
something like this:
 
 
auto bftraverse( Node* root )
-> optional<Data>
{
stack<Node*> nodes;
nodes.push( root );
while( not nodes.empty() ) {
const auto p = nodes.top();
nodes.pop();
if( p == nullptr ) {
co_return {};
}
co_yield p->data;
if( p->right ) { nodes.push( p->right ); }
if( p>left ) { nodes.push( p->left ); }
}
}
 
 
Except that from cppreference's page it seems that the trailing return
type syntax isn't supported for coroutines. If so then that's an
inconsistency that will probably be fixed later.
 
I guess that at some point someone is going to publish a guideline,
"Recursive coroutines considered harmful". For efficiency. Then someone
else is going to counter that with ""Recursive coroutines considered
harmful" considered harmful", for convenience and development time. And
then there will maybe be some discussion. In forums of the mainly
willfully ignorant those who post either naturally recursive or
unnaturally iterative coroutines will sometimes get blasts of anonymous
downvotes. For not following the perceived best practice. And so on. :)
 
 
Cheers!,
 
- Alf
"Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com>: Aug 05 02:34PM -0700

On 8/4/2019 11:36 PM, Paavo Helde wrote:
> creation is a pretty heavyweight operation. So it's strange that IOCP is
> mentioned as an example of "Threads can work fairly well with IO", it's
> rather the opposite, at least on Windows.
 
Yeah. IOCP is the way to do things on Windows wrt creating scaleable
servers. I am fond of the following function:
 
https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-transmitfile
 
It works well with IOCP. Also,
 
https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nc-mswsock-lpfn_transmitpackets
 
There is a neat way to wait on IOCP that can dequeue several events in
one shot:
 
https://docs.microsoft.com/en-us/windows/win32/fileio/getqueuedcompletionstatusex-func
 
Iirc, certain Windows os's limited the number of TransmitFile functions
to a max of two concurrent inflight calls at any time. The server
versions of the OS would allow for as many as the system could handle at
a time.
 
 
 
"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.
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: