Sunday, October 20, 2019

Digest for comp.lang.c++@googlegroups.com - 14 updates in 3 topics

"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Oct 20 03:40PM -0700

Fwiw, this is just a little experiment. I got some free time and decided
to go back into lock-free a bit. This code, in Relacy:
 
https://github.com/dvyukov/relacy
 
is basically C++11 so its easy to convert to a standalone C++11 program.
I like to code up in Relacy to run things through its race detector.
Anyway, this early stage does not use any locks to insert new nodes into
random places within a LIFO. However, it does not destroy any nodes
during the test. The fun part is that I can add lock-free delete using
something like RCU, or a proxy collector in pure C++11 to allow for safe
and dynamic deallocation of nodes. The meat is in struct
ct_strange_stack. Humm... I think I might be able to get rid of one of
the acquire membars in:
 
ct_strange_stack::iterate
ct_strange_stack::find_random_node
ct_strange_stack::dump
 
Humm... Anyway, here is the code:
________________________________________
/*
Lock-Free Insert Experiment
by: Chris M. Thomasson
_____________________________________________________*/
 
 
//#define RL_DEBUGBREAK_ON_ASSERT
//#define RL_MSVC_OUTPUT
//#define RL_FORCE_SEQ_CST
//#define RL_GC
 
 
#include <relacy/relacy_std.hpp>
#include <iostream>
#include <vector>
#include <algorithm>
 
#define CT_READERS 4
#define CT_WRITERS 3
#define CT_THREADS (CT_WRITERS + CT_READERS)
 
 
struct ct_strange_stack
{
struct node
{
std::atomic<node*> m_next;
};
 
std::atomic<node*> m_head;
 
ct_strange_stack() : m_head(nullptr) {}
 
~ct_strange_stack()
{
dump(); // finally, deallocate... ;^)
}
 
void push_head(node* n)
{
push_node(m_head, n);
}
 
 
void push_node(std::atomic<node*>& head, node* n)
{
node* cmp = m_head.load(std::memory_order_relaxed);
 
do
{
n->m_next.store(cmp, std::memory_order_relaxed);
 
} while (!m_head.compare_exchange_weak(
cmp,
n,
std::memory_order_release
));
}
 
 
void iterate()
{
node* head = m_head.load(std::memory_order_acquire);
 
while (head)
{
node* next = head->m_next.load(std::memory_order_acquire);
 
rl::yield(1, $);
 
head = next;
}
}
 
 
node* find_random_node()
{
node* head = m_head.load(std::memory_order_acquire);
 
while (head)
{
node* next = head->m_next.load(std::memory_order_acquire);
 
unsigned long humm = rl::rand(2);
 
if (humm == 1)
{
return head;
}
 
head = next;
}
 
return nullptr;
}
 
 
void dump()
{
node* head = m_head.exchange(nullptr, std::memory_order_acquire);
 
while (head)
{
node* next = head->m_next.load(std::memory_order_acquire);
 
delete head;
 
head = next;
}
}
};
 
 
 
struct ct_experiment
: rl::test_suite<ct_experiment, CT_THREADS>
{
ct_strange_stack g_stack;
 
 
// reader
void reader(unsigned int tidx)
{
g_stack.iterate();
}
 
 
// writer
void writer(unsigned int tidx)
{
for (unsigned long i = 0; i < 3; ++i)
{
ct_strange_stack::node* n = new ct_strange_stack::node();
 
g_stack.push_head(n);
}
 
unsigned long i = 3;
ct_strange_stack::node* n = nullptr;
 
while ((n = g_stack.find_random_node()) && --i != 0)
{
// insert a node wrt n
ct_strange_stack::node* new_node = new
ct_strange_stack::node();
g_stack.push_node(n->m_next, new_node);
}
}
 
 
void thread(unsigned int tidx)
{
if (tidx < CT_WRITERS)
{
writer(tidx);
}
 
else
{
reader(tidx);
}
}
};
 
 
 
// Test away... Or fly? Humm...
int main()
{
{
rl::test_params p;
 
p.iteration_count = 10000000;
//p.execution_depth_limit = 33333;
//p.search_type = rl::sched_bound;
//p.search_type = rl::fair_full_search_scheduler_type;
//p.search_type = rl::fair_context_bound_scheduler_type;
 
rl::simulate<ct_experiment>(p);
}
 
return 0;
}
________________________________________
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Oct 20 03:54PM -0700

On 10/20/2019 3:40 PM, Chris M. Thomasson wrote:
> ct_strange_stack::dump
 
> Humm... Anyway, here is the code:
> ________________________________________
[...]
> ________________________________________
 
Humm... This reminds me of a trick I can use when getting around to
deleting arbitrary nodes:
 
https://groups.google.com/d/topic/lock-free/X3fuuXknQF0/discussion
 
https://pastebin.com/raw/TgTcfYtR
Soviet_Mario <SovietMario@CCCP.MIR>: Oct 20 01:24AM +0200

On 19/10/2019 17:02, Paavo Helde wrote:
>> "sparse", or not ?
 
> I was comparing std::string to std::vector<char>, not to
> std::vector<std::string>.
 
oh ! Now I got the point.
I had always taken for granted that the "content" (of a
std::string) could not be broken in slices spread over and
linked somewhat.
 
for vector<char> I did not know, but later I read the same
features stated here in the 3D
 
 
> character data of all those strings were contiguous or even
> in order in memory, typically they would be all scattered
> around.
 
Okay. It seemed a too memory restrictive approach
 
> character data of the string. So the character arrays of two
> different strings cannot be contiguous, there must be at
> least one zero byte between them.
 
ah, nice ! So inkoking c_str() does not imply any copying !
Faster, for sure.
 
in Basic (gambas) passing strings to C routines alas implies
copying (as a basic string is zero-indifferent and stores
pointer and size).
 
 
 
--
1) Resistere, resistere, resistere.
2) Se tutti pagano le tasse, le tasse le pagano tutti
Soviet_Mario - (aka Gatto_Vizzato)
Paavo Helde <myfirstname@osa.pri.ee>: Oct 20 10:29AM +0300

On 20.10.2019 2:24, Soviet_Mario wrote:
> ah, nice ! So inkoking c_str() does not imply any copying ! Faster, for
> sure.
 
Yes, see e.g. "https://en.cppreference.com/w/cpp/string/basic_string/c_str":
 
"c_str() and data() perform the same function. (since C++11)"
 
Initially the language designers wanted to leave room for clever
optimizations where c_str() indeed might have involved some copying and
zero terminator appending, but this did not quite work out. The rules
about when iterators and pointers might get invalidated were getting
unwieldy and with the raise of multithreading era optimizations like COW
started to appear more like pessimizations, so in C++11 they decided to
go the other way, making the invalidation rules simple and unsurprising.
The main change was the requirement that no call of operator[]() may
invalidate any iterators, references and pointers obtained earlier via
e.g. data() or c_str().
 
This effectively restricted any possible basic_string implementations to
always use a simple unshared contiguous zero-terminated character array.
And thus data() and c_str() became the same noexcept trivial operations.
 
One can argue that the interface of basic_string is just too broad and
diffuse to allow for certain kind of optimizations like discontiguous
data buffers. There is a non-standard extension std::rope provided by
some implementations to solve such issues, but this is not in the
official C++ standard (I guess it's considered too specialized).
"Öö Tiib" <ootiib@hot.ee>: Oct 20 02:30AM -0700

On Sunday, 20 October 2019 10:29:44 UTC+3, Paavo Helde wrote:
> data buffers. There is a non-standard extension std::rope provided by
> some implementations to solve such issues, but this is not in the
> official C++ standard (I guess it's considered too specialized).
 
AFAIK the rope was in STL of SGI (but was not in STL of HP) already
before C++ standardization. It wasn't incorporated into C++ standard
library.
 
Both the name "STL" and usage of "std" namespace by those libraries
has caused confusion forever.
With boost it is lot easier, the differences for example between
boost::unordered_set and std::unordered_set are lot simpler to
discuss than the differences between "std::"hash_set of SGI STL
and std::unordered_set.
Melzzzzz <Melzzzzz@zzzzz.com>: Oct 20 10:45AM


> AFAIK the rope was in STL of SGI (but was not in STL of HP) already
> before C++ standardization. It wasn't incorporated into C++ standard
> library.
 
Why? What was said against it?
 
 
 
--
press any key to continue or any other to quit...
U ničemu ja ne uživam kao u svom statusu INVALIDA -- Zli Zec
Na divljem zapadu i nije bilo tako puno nasilja, upravo zato jer su svi
bili naoruzani. -- Mladen Gogala
"Öö Tiib" <ootiib@hot.ee>: Oct 20 11:32AM -0700

On Sunday, 20 October 2019 13:46:01 UTC+3, Melzzzzz wrote:
> > before C++ standardization. It wasn't incorporated into C++ standard
> > library.
 
> Why? What was said against it?
 
I have not seen any relevant WG21 discussion notes on strings and ropes.
 
The templates themselves were quite revolutionary back then and so heavily
opposed. The EC++ for example kicked templates out despite it is clear how
good are templates in embedded development. Committee's focus was on standardizing what was most urgently needed and most ready
to ship. Anyway some weak things slipped in (like valarray or
vector<bool>) but overall it was great that C++ got standardized with
library.
Melzzzzz <Melzzzzz@zzzzz.com>: Oct 20 06:44PM


> The templates themselves were quite revolutionary back then and so heavily
> opposed. The EC++ for example kicked templates out despite it is clear how
> good are templates in embedded development.
 
In that time embedded computers where really limiting, so no wander. Now
embedded devices have 512MB+...
 
Committee's focus was on standardizing what was most urgently needed and most ready
> to ship. Anyway some weak things slipped in (like valarray or
> vector<bool>) but overall it was great that C++ got standardized with
> library.
 
So how we got queue and deque but not rope? Strange....
 
--
press any key to continue or any other to quit...
U ničemu ja ne uživam kao u svom statusu INVALIDA -- Zli Zec
Na divljem zapadu i nije bilo tako puno nasilja, upravo zato jer su svi
bili naoruzani. -- Mladen Gogala
"Öö Tiib" <ootiib@hot.ee>: Oct 20 01:24PM -0700

On Sunday, 20 October 2019 21:45:02 UTC+3, Melzzzzz wrote:
> > good are templates in embedded development.
 
> In that time embedded computers where really limiting, so no wander. Now
> embedded devices have 512MB+...
 
So what? I think the primary issue with opponents was (and still is) that
they consider templates something space consuming and inefficient.
Something with shortcomings of containers of Simula or Smalltalk or
with generics of Ada. However that is not the case. Templates of
C++ tend let compilers to generate as time- and space-efficient code
as best specialist will do by hand.
 
 
> > vector<bool>) but overall it was great that C++ got standardized with
> > library.
 
> So how we got queue and deque but not rope? Strange....
 
Back then it felt good that we at least got more-or-less
passable standard string. Otherwise each library had its own astring,
bstring, cstring, ... qstring ... wxstring.
 
.
Paavo Helde <myfirstname@osa.pri.ee>: Oct 20 11:33PM +0300

On 20.10.2019 21:44, Melzzzzz wrote:
>> good are templates in embedded development.
 
> In that time embedded computers where really limiting, so no wander. Now
> embedded devices have 512MB+...
 
Templates take a lot of resources only in the compiler, not in the
target environment.
 
It is true that one can instantiate a template for 10 different types
and get 10x code size with bad optimizers, but if the embedded
developers do not realize how many different templates they instantiate
they do not deserve their paychecks.
 
>> vector<bool>) but overall it was great that C++ got standardized with
>> library.
 
> So how we got queue and deque but not rope? Strange....
 
Deque is a very fine container, compared to some other like list which
only got standardized because of their name while having almost no usage
cases.
Melzzzzz <Melzzzzz@zzzzz.com>: Oct 20 09:16PM

> with generics of Ada. However that is not the case. Templates of
> C++ tend let compilers to generate as time- and space-efficient code
> as best specialist will do by hand.
 
I think that primary reason for ditching templates is that "write once
get errors everywhere" was main problem with them.
 
 
> Back then it felt good that we at least got more-or-less
> passable standard string. Otherwise each library had its own astring,
> bstring, cstring, ... qstring ... wxstring.
 
Yeah, that's really annoying, having to use this string with that
library....
 
 
--
press any key to continue or any other to quit...
U ničemu ja ne uživam kao u svom statusu INVALIDA -- Zli Zec
Na divljem zapadu i nije bilo tako puno nasilja, upravo zato jer su svi
bili naoruzani. -- Mladen Gogala
Melzzzzz <Melzzzzz@zzzzz.com>: Oct 20 09:18PM

> and get 10x code size with bad optimizers, but if the embedded
> developers do not realize how many different templates they instantiate
> they do not deserve their paychecks.
 
This is case now. Not when EC++ was postulated. Other thing is that
exceptions were also out of question...
 
 
--
press any key to continue or any other to quit...
U ničemu ja ne uživam kao u svom statusu INVALIDA -- Zli Zec
Na divljem zapadu i nije bilo tako puno nasilja, upravo zato jer su svi
bili naoruzani. -- Mladen Gogala
David Brown <david.brown@hesbynett.no>: Oct 21 12:21AM +0200

On 20/10/2019 23:18, Melzzzzz wrote:
>>> embedded devices have 512MB+...
 
>> Templates take a lot of resources only in the compiler, not in the
>> target environment.
 
There are many things about EC++ that made no sense at all. It also
banned namespaces - which take no resources on the target, and
negligible resources on the compiler end. It is not a surprise that it
never really caught on, and is now completely dead.
 
>> and get 10x code size with bad optimizers, but if the embedded
>> developers do not realize how many different templates they instantiate
>> they do not deserve their paychecks.
 
Modern tools will often reduce this extra code by combining identical
functions - though this was not the case when EC++ was thrown together.
 
 
> This is case now. Not when EC++ was postulated. Other thing is that
> exceptions were also out of question...
 
EC++ banned exceptions, and also multiple inheritance - many modern C++
standards (especially for embedded programming) do that. There are
arguments for and against this (unlike many of EC++'s restrictions, for
which there are no good justifications).
Jorgen Grahn <grahn+nntp@snipabacken.se>: Oct 20 10:18PM

On Wed, 2019-10-16, Öö Tiib wrote:
> exceptions to report errors presumably detectable before the
> program executes, such as violations of logical preconditions
> or class invariants."
 
I.e. bugs.
 
> Seems they assume that something can detect coherency of all
> input data before program is ran.
 
But there is no such thing. The way I read this thread (after
snipping, reconstruction and a weekend) std::logic_error is for bugs,
and std::stoi() misuses it. And that's also what you argue. Right?
 
 
> No, you snipped it. ;) You wanted to see only first sentence and
> not to read the context. See:
 
>> [snip]
 
Sorry. What happened was I was too tired to read it all (and to read
the std::stoi documentation), but not too tired to read the sentence I
commented on and believed that, seen in isolation, I didn't agree with
it. So I complained about that isolated part, added the disclaimer,
and snipped because I didn't want to imply I had read all of it.
 
> input is coming from corrupt input data? Also for preventing
> the inconsistency its caller has possibly to do most of what
> stoi does itself."
 
Ok, so std::stoi() is new with C++11, tries to grab an int from a
string, and (among other things) throws std::out_of_range if the
string's number doesn't fit in an int.
 
That does seem a bit useless.
 
> whose contents I know before program run into ints run-time
> using std::stoi feels even worse logic error. I can instead
> write those ints directly into code.
 
I agree. I haven't used std::stoi, but I imagine if I /did/ use it
I would have to guard individual calls in try...catch, which to me
misses the point with exceptions.
 
/Jorgen
 
--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
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: