Monday, February 4, 2019

Digest for comp.lang.c++@googlegroups.com - 20 updates in 4 topics

"Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com>: Feb 03 09:01PM -0800

On 2/3/2019 1:52 PM, Chris M. Thomasson wrote:
> semaphore has a mutex and condvar. You have two of them plus another
> mutex. That is:
 
> 3 mutexes and 2 condvars for a single queue?
 
Still need to examine it more carefully, but the following function is a
race-condition if it is concurrently called by more than one thread:
 
bool empty()
{
return m_count == 0;
}
 
You are allowing reads and writes to hit m_count at the same time in
this call.
"Öö Tiib" <ootiib@hot.ee>: Feb 04 01:32AM -0800

> My multi-threaded blocking queue implementation: https://blog.vorbrodt.me/?p=409
 
If you want to make std::queue pop blocking then one mutex and one
condition variable should be fine. Something like that:
 
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>

template <typename T>
class Queue
{
public:

T pop()
{
std::unique_lock<std::mutex> mlock(mutex_);
while (queue_.empty())
{
cond_.wait(mlock);
}
auto item = queue_.front();
queue_.pop();
return item;
}

void push(T item)
{
std::unique_lock<std::mutex> mlock(mutex_);
queue_.push(item);
mlock.unlock();
cond_.notify_one();
}

private:
std::queue<T> queue_;
std::mutex mutex_;
std::condition_variable cond_;
};
 
So blocking pop implemented, without need for boost or anything.
Now if you have some sort of improvement to it for some
special case then describe the case and show benefits of
your implementation with benchmarks.
mvorbrodt@gmail.com: Feb 04 05:08AM -0800

On Monday, February 4, 2019 at 4:32:34 AM UTC-5, Öö Tiib wrote:
> Now if you have some sort of improvement to it for some
> special case then describe the case and show benefits of
> your implementation with benchmarks.
 
your implementation grows indefinitely on push(). mine will eventually block when the queue fills up.
mvorbrodt@gmail.com: Feb 04 05:09AM -0800

On Monday, February 4, 2019 at 12:01:41 AM UTC-5, Chris M. Thomasson wrote:
> }
 
> You are allowing reads and writes to hit m_count at the same time in
> this call.
 
you are right. I modified the code to lock around the check for empty.
mvorbrodt@gmail.com: Feb 04 05:12AM -0800

On Sunday, February 3, 2019 at 4:52:41 PM UTC-5, Chris M. Thomasson wrote:
> semaphore has a mutex and condvar. You have two of them plus another
> mutex. That is:
 
> 3 mutexes and 2 condvars for a single queue?
 
semaphores for clarity of implementation and easy counting of empty and full slots.
"Öö Tiib" <ootiib@hot.ee>: Feb 04 06:19AM -0800

> > special case then describe the case and show benefits of
> > your implementation with benchmarks.
 
> your implementation grows indefinitely on push(). mine will eventually block when the queue fills up.
 
Then one more conditional variable is needed for blocking push,
still no need for boost or anything.
Bonita Montero <Bonita.Montero@gmail.com>: Feb 04 04:26PM +0100

> your implementation grows indefinitely on push(). mine will eventually block when the queue fills up.
 
When do you think a queue with limited capacity is necessary?
I think that's rather exotic.
Robert Wessel <robertwessel2@yahoo.com>: Feb 04 09:59AM -0600

On Mon, 4 Feb 2019 16:26:33 +0100, Bonita Montero
 
>> your implementation grows indefinitely on push(). mine will eventually block when the queue fills up.
 
>When do you think a queue with limited capacity is necessary?
>I think that's rather exotic.
 
 
Limited queue growth is not an uncommon requirement. In cases where
you have one set of processes generating work, and another performing
it, you often want to keep those decoupled and running independently
to make maximum use of resources, but you don't want unlimited numbers
of work items filling memory. Usually, though, I've done that by
placing an separate limit on the queue depth. You can often avoid the
blocking entirely if you can have the generating process execute the
work unit directly if the queue is full.
Bonita Montero <Bonita.Montero@gmail.com>: Feb 04 06:12PM +0100

> it, you often want to keep those decoupled and running independently
> to make maximum use of resources, but you don't want unlimited numbers
> of work items filling memory.
 
Why not? Memory is cheap.
 
> queue depth. You can often avoid the blocking entirely if you can
> have the generating process execute the work unit directly if the
> queue is full.
 
... and thereby block further processing in the producer.
Very smart!
Bonita Montero <Bonita.Montero@gmail.com>: Feb 04 06:17PM +0100

> Why not? Memory is cheap.
 
To be more precise: The data-structures which are handed through the
queue to the consumer are in memory anyway and are usually much larger
than a simple link-node in the queue.
David Brown <david.brown@hesbynett.no>: Feb 04 08:40PM +0100

On 04/02/2019 18:17, Bonita Montero wrote:
 
> To be more precise: The data-structures which are handed through the
> queue to the consumer are in memory anyway and are usually much larger
> than a simple link-node in the queue.
 
That means that the producer needs to use a lot more memory to make
these data structures - it can make sense to limit that by blocking the
producer.
 
And memory is not always cheap. Unlimited memory is almost always very
expensive.
 
(That does not mean having a specific limit to the size of the queue is
the best solution - it might be, but there may be other solutions.)
mvorbrodt@gmail.com: Feb 04 11:58AM -0800

On Monday, February 4, 2019 at 10:26:44 AM UTC-5, Bonita Montero wrote:
> > your implementation grows indefinitely on push(). mine will eventually block when the queue fills up.
 
> When do you think a queue with limited capacity is necessary?
> I think that's rather exotic.
 
it's used for throttling. sometimes you don't want it to grow indefinitely.
"Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com>: Feb 04 01:46PM -0800

>> mutex. That is:
 
>> 3 mutexes and 2 condvars for a single queue?
 
> semaphores for clarity of implementation and easy counting of empty and full slots.
 
Fair enough! :^)
"Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com>: Feb 04 01:59PM -0800

On 2/4/2019 1:46 PM, Chris M. Thomasson wrote:
 
>> semaphores for clarity of implementation and easy counting of empty
>> and full slots.
 
> Fair enough! :^)
 
Btw, there is a faster semaphore out there that can be implemented in C++11:
 
https://groups.google.com/d/topic/comp.lang.c++/60IpFwqbtu8/discussion
(read all if interested...)
 
Here is a direct link to a crude sample implementation:
 
http://pastebin.com/raw/Q7p6e7Xc
(no ads, raw text...)
 
Imvvho, this semaphore is nice, notice how it can skip waiting on a
mutex/condvar based semaphore?
"Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com>: Feb 04 02:14PM -0800


>> You are allowing reads and writes to hit m_count at the same time in
>> this call.
 
> you are right. I modified the code to lock around the check for empty.
 
That's fine. Now, if this empty check could be relaxed to a standard
form of "racy", then one could define m_count as a std::atomic type, and
read and write it with std::memory_order_relaxed. As is, it would be
fine using your current code. However, the data returned is dubious
because the mutex m_cs was not locked where you mutate m_count. If empty
is called a lot, then this type of racy check might be perfectly fine.
Keep in mind that there is a difference between racing using atomic
types vs raw variables. Fwiw, here is a massive race condition, yet it
is 100% perfectly standard compliant:
 
https://groups.google.com/d/topic/comp.lang.c++/7u_rLgQe86k/discussion
 
;^)
"Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com>: Feb 04 02:21PM -0800


>> When do you think a queue with limited capacity is necessary?
>> I think that's rather exotic.
 
> it's used for throttling. sometimes you don't want it to grow indefinitely.
 
Big time! Fwiw, if interested, check out my simple mutation to a very
nice _bounded_ FIFO queue:
 
https://groups.google.com/d/topic/lock-free/acjQ3-89abE/discussion
(read all if interested, please read all...)
"Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com>: Feb 04 02:39PM -0800

On 2/4/2019 11:40 AM, David Brown wrote:
 
> That means that the producer needs to use a lot more memory to make
> these data structures - it can make sense to limit that by blocking the
> producer.
 
Right. Or even give a flag to the producer such that it can possibly do
"other" work instead of wait right then and there. It can do some other
work, then check back at the queue to see if it is still in the full
state. Like a continuation sparked by a queue full condition. A producer
can do other work if possible, and check the queue again. A producer can
even queue work locally to a per-thread structure on a bounded full
condition, then, when the queue gets consumed a bit, well, it can send
all the local work in a single atomic shot.
 
> expensive.
 
> (That does not mean having a specific limit to the size of the queue is
> the best solution - it might be, but there may be other solutions.)
 
Fwiw, bounded queues are fairly normal, in my experience at least.
Horizon68 <horizon@horizon.com>: Feb 04 01:19PM -0800

Hello..
 
 
About SC and TSO and RMO hardware memory models..
 
I have just read the following webpage about the performance difference
between: SC and TSO and RMO hardware memory models
 
I think TSO is better, it is just around 3% ~ 6% less performance
than RMO and it is a simpler programming model than RMO. So i think ARM
must support TSO to be compatible with x86 that is TSO.
 
Read more here to notice it:
 
https://infoscience.epfl.ch/record/201695/files/CS471_proj_slides_Tao_Marc_2011_1222_1.pdf
 
About memory models and sequential consistency:
 
As you have noticed i am working with x86 architecture..
 
Even though x86 gives up on sequential consistency, it's among the most
well-behaved architectures in terms of the crazy behaviors it allows.
Most other architectures implement even weaker memory models.
 
ARM memory model is notoriously underspecified, but is essentially a
form of weak ordering, which provides very few guarantees. Weak ordering
allows almost any operation to be reordered, which enables a variety of
hardware optimizations but is also a nightmare to program at the lowest
levels.
 
Read more here:
 
https://homes.cs.washington.edu/~bornholt/post/memory-models.html
 
 
Memory Models: x86 is TSO, TSO is Good
 
Essentially, the conclusion is that x86 in practice implements the old
SPARC TSO memory model.
 
The big take-away from the talk for me is that it confirms the
observation made may times before that SPARC TSO seems to be the optimal
memory model. It is sufficiently understandable that programmers can
write correct code without having barriers everywhere. It is
sufficiently weak that you can build fast hardware implementation that
can scale to big machines.
 
Read more here:
 
https://jakob.engbloms.se/archives/1435
 
 
Thank you,
Amine Moulay Ramdane.
Cholo Lennon <chololennon@hotmail.com>: Feb 04 05:34PM -0300

On 2/3/19 7:55 AM, Alf P. Steinbach wrote:
 
> "With C++/WinRT, you can author and consume Windows Runtime APIs using
> any standards-compliant C++17 compiler."
 
> Has anyone tried to use MinGW g++?
 
Not me, but it would be a good exercise in order to test the Microsoft
marketing :-)
 
--
Cholo Lennon
Bs.As.
ARG
woodbrian77@gmail.com: Feb 03 08:31PM -0800


> I'm wondering if the next problem after this will be that
> it's building on Windows 10 and my makefile refers to Windows
> 7 libs. At least the Appveyor gui isn't totally lame.
 
I decided to use an appveyor.yml file rather than the gui.
My appveyor.yml is based on the one in Premake's repo:
 
os: Visual Studio 2017
 
platform:
- Win32
- x64
 
configuration:
- Debug
- Release
 
before_build:
- cmd: git clean -ffxd
 
build_script:
- cmd: call "%VS140COMNTOOLS%\..\..\VC\vcvarsall.bat"
- cmd: nmake -f makefile.vs MSDEV=vs2017 windows PLATFORM=%PLATFORM% CONFIG=%CONFIGURATION%
 
--------------------------------------------------------
 
But something about it or the makefile:
https://github.com/Ebenezer-group/onwards/blob/master/makefile.vs
 
isn't working right. And I'm not sure if I've run out of something
with my free Appveyor account, but previously I could get some
info about what was failing. After approximately 50 builds, I'm no
longer able to figure out how to get more info, but it still says
it failed. Anyway, I'm thankful for this yml file. It's not
as crazy as the other yml files I've seen. It's almost reasonable.
Tia.
 
 
Brian
Ebenezer Enterprises - "For G-d so loved the world, that He gave
His only Son, that whoever believes in Him should not perish but
have eternal life." John 3:16
 
http://webEbenezer.net
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: