Sunday, September 19, 2021

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

Bonita Montero <Bonita.Montero@gmail.com>: Sep 19 06:13PM +0200

I just wanted to test if my compiler recognizes ...
atomic<pair<uintptr_t, uintptr_t>
... and uses DCASes on it or if it supplements the pair with a usual
lock. So I first wrote a minimal program with a single noinline-function
which does a compare and swap on a atomic pair. The compiler generated
a lot of code for this which I didn't understand, and I didn't saw any
LOCK-prefixed instructions in that. I was curisous about whether the
implementation places a lock within the atomic and printed a sizeof
of the above atomic pair: 24 on a 64-bit-platform, so obviously without
a lock.
At last I wrote a program which increments both portions of a single
atomic pair by all the threads my system has (Ryzen Threadripper 64
core, Win10, SMT off) a predefined number of times. Then I calculated
the time for each increment in nanoseconds. The time is rather high,
about 20.000ns for each successful increment, so it first looked to
me if there was a lock I overlooked; so this couldn't be true with
a sizeof of this atomic of 24 bytes. And when I saw at the Processs
Viewer I saw that all 64 cores were nearly at 100% _user_ CPU time
all the time - so there couldn't be any kernel-interventions.
So is there anyone here smarter than me and can identify what this
DCAS-substitute does from the assembly-dump ?
 
Here's my test-code:
 
#include <iostream>
#include <atomic>
#include <utility>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
 
using namespace std;
using namespace chrono;
 
using uintptr_pair = pair<uintptr_t, uintptr_t>;
using atomic_pair = atomic<uintptr_pair>;
 
int main()
{
cout << "atomic<pair<uintptr_t, uintptr_t>>: " << sizeof(atomic_pair)
<< endl;
atomic_pair ap( uintptr_pair( 0, 0 ) );
mutex mtx;
unsigned nThreads = thread::hardware_concurrency();
unsigned ready = nThreads;
condition_variable cvReady;
bool run = false;
condition_variable cvRun;
atomic_int64_t sumDur = 0;
auto theThread = [&]( size_t n )
{
unique_lock<mutex> lock( mtx );
if( !--ready )
cvReady.notify_one();
cvRun.wait( lock, [&]() -> bool { return run; } );
lock.unlock();
auto start = high_resolution_clock::now();
uintptr_pair cmp = ap.load( memory_order_relaxed );
for( ; n--; )
while( !ap.compare_exchange_weak( cmp, uintptr_pair( cmp.first + 1,
cmp.second + 1 ), memory_order_relaxed, memory_order_relaxed ) );
sumDur.fetch_add( duration_cast<nanoseconds>(
high_resolution_clock::now() - start ).count(), memory_order_relaxed );
lock.lock();
};
vector<jthread> threads;
threads.reserve( nThreads );
static size_t const ROUNDS = 100'000;
for( unsigned t = nThreads; t--; )
threads.emplace_back( theThread, ROUNDS );
unique_lock<mutex> lock( mtx );
cvReady.wait( lock, [&]() -> bool { return !ready; } );
run = true;
cvRun.notify_all();
lock.unlock();
for( jthread &thr : threads )
thr.join();
cout << (double)sumDur / ((double)nThreads * ROUNDS) << endl;
uintptr_pair p = ap.load( memory_order_relaxed );
cout << "synch: " << (p.first == p.second ? "yes" : "no") << endl;
}
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Sep 19 03:29PM -0700

On 9/19/2021 9:13 AM, Bonita Montero wrote:
>     atomic<pair<uintptr_t, uintptr_t>
> ... and uses DCASes on it or if it supplements the pair with a usual
> lock.
 
[...]
 
Can you get this to compile on your MSVC?
_________________________
#include <iostream>
#include <atomic>
#include <xmmintrin.h>
 
int main()
{
std::atomic<__int64> foo_64;
std::atomic<__m128> foo_128;
 
std::cout << sizeof(__int64) << "\n";
std::cout << sizeof(__m128) << "\n";
 
std::cout << foo_64.is_lock_free() << "\n";
std::cout << foo_128.is_lock_free() << "\n";
 
return 0;
}
_________________________
 
 
In 64-bit mode I am getting:
 
8
16
1
0
 
This means that this compiler is not supporting lock-free operations on
128-bit numbers in 64-bit mode, humm. What do you get? Also, you might
try to see if your system supports the CMPXCHG16B instruction. If so,
code DWCAS manually in assembly language. Now, in 32 bit mode, I also get:
 
8
16
1
0
 
This means that DWCAS _is_ supported in a lock-free manner in 32 bit
mode through CMPXCHG8B. So, try to use CMPXCHG16B directly if you want
DWCAS in 64-bit mode.
 
I have not tried this experiment in GCC. It might work with __int128,
and end up using:
 
https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html
 
__atomic_compare_exchange_16, you might have to use -mcx16.
 
Hope that helps a bit!
Bart <bc@freeuk.com>: Sep 19 12:46AM +0100

On 18/09/2021 23:48, Scott Lurndal wrote:
 
> A trivially useless format string. Try adding field widths and
> re-ordering the arguments within the format string for i18n/l10n
> purposes.
 
You're just picking holes, aren't you?
 
I don't see the problem with field widths. While internationalisation is
a separate aspect that is not a problem I've ever had in 99.999% of my
uses of printf.
 
But grappling with the correct format codes has ALWAYS been a problem,
and needs a solution, not nit-picking ideas because you're trying to put
someone down.
 
(I used a different approach to locale-specific printing decades ago, so
that I would write:
 
println /"Serial number:", sn
 
in my code, but at the customer site, it might output:
 
Serie nummer: 1234
 
if they spoke Dutch. "/" is a translation operator.)
Bart <bc@freeuk.com>: Sep 19 01:09AM +0100

On 18/09/2021 22:29, Christian Gollwitzer wrote:
> % puts "a=$a b=$b c=$c"
> a=3 b=4 c=Hallo
> %
 
Simple Print is one of the most diverse features in program languages
(just take a look through Rosetta Code).
 
Scripting languages tend to do better, although most seem to want to do
it via functions in libraries, sometimes requiring special features of
the language (in C, it's variadic functions and parameters).
 
One of the best IMV was BASIC:
 
PRINT A, B, C ' 1960s and 70s
 
and I can do the same:
 
print A, B, C
 
Although the rules for spacing and newlines still differ widely. My
example will add spacing between the elements. In C++, you need to write:
 
std::cout << A << " " << B << " " << C;
 
where you gradually lose the will to live. I assume there is a formatted
Print feature other than C's printf.
 
(The "=" feature of mine adds a label; invaluable for debugging code,
but elsewhere you usually have to either repeat the expression as a
string, or knock up some C macro to avoid the duplication.)
 
 
> >>> f'input_{a:03}.png'
> 'input_003.png'
 
> ...and, surprise:
 
If you have string processing anyway, then there are many more
possibilities to getting formatted results. But sticking with formatted
Print, this becomes in my languages:
 
fprint "input_#.png", a:"z3" # statement-style
 
s := sfprint("input_#.png", a:"z3") # expression-style
 
I like the format string to be clean, and free of clutter, so that I can
more easily see what it's supposed to look like!
Christian Gollwitzer <auriocus@gmx.de>: Sep 19 08:06AM +0200

Am 19.09.21 um 01:46 schrieb Bart:
 
> in my code, but at the customer site, it might output:
 
>     Serie nummer: 1234
 
> if they spoke Dutch. "/" is a translation operator.)
 
You didn't get the problem Scott was talking about. If you have more
than 1 variable in a sentence, in the translation the word order might
be changed. E.g.
 
 
"The $animal bites $name"
 
could be tranlsated as

"$name gets bitten by the $animal"
 
in some language.
 
Therefore, if you do
 
printf("The %s bites %s", "dog", "Harry")
 
and the translator does
 
"%s gets bitten by the %s"
 
you will end up with
 
"dog gets bitten by the Harry"
 
If, however, there is proper string interpolation like in Python format
strings, then the translator would translatate the string as above, changing
 
"The {animal} bites {name}"
into
"{name} gets bitten by the {animal}"
 
and it would come out correctly. Obviously, there are still problems
with inflections; in most languages the dog, Harry etc. are adapted
depending on the function in the sentence (grammatical cases).
 
Christian
red floyd <no.spam.here@its.invalid>: Sep 18 11:45PM -0700

On 9/18/2021 5:09 PM, Bart wrote:
 
 
>   std::cout << A << " " << B << " " << C;
 
> where you gradually lose the will to live. I assume there is a formatted
> Print feature other than C's printf.
 
boost::format? Or C++20 std::format?
Bart <bc@freeuk.com>: Sep 19 10:26AM +0100

On 19/09/2021 07:06, Christian Gollwitzer wrote:
 
> and it would come out correctly. Obviously, there are still problems
> with inflections; in most languages the dog, Harry etc. are adapted
> depending on the function in the sentence (grammatical cases).
 
But this is not what printf does? That has n$ positional codes, which is
not affected by my suggestion to use, for example, "?" instead of "d",
"f", "s" etc to denote the type of the result.
 
(And if a format string ends in a list outside the program, it's better
that "?" was in there, than "d" "lld" etc, which now become extra
program code to maintain.)
 
My point, again, was that this stuff is not the problem with Print in C
and C++ that I was addressing.
 
(Does C++ have keyword arguments yet? A vastly more useful feature in
everyday coding. If not, then implement those and then we'll talk about
positional print items, which can trivially be dealt with in user-code.)
 
 
> "The {animal} bites {name}"
> into
> "{name} gets bitten by the {animal}"
 
That seems a reasonable way of doing that, except that the second line
will be translated into the target language; you need to ensure that
'name' and 'animal', identifiers in the source code, are not translated too!
 
(I would still prefer that those names, which are really expressions,
were outside the string, with a positional scheme like printf's applied
if necessary.
 
Being expressions, they can presumably include embedded format strings too?)
 
Anyway, in many applications I've seen, people don't really bother
getting things right even in English: in Windows I often see "1 Files"
being shown (now improved to "1 File(s)"!), when the program /knows/ the
quantity and can easy display either "1 File" or "5 Files".
 
This is an example of my code from last century (here using string
processing not formatted print):
 
smcmd((nfiles=0|/"No Files"|(nfiles=1|"1 " + /"File"|str(nfiles) +
/"Files")))
 
It shows 'No files' or '1 File' or 'N Files'. In Dutch, it would show
'Geen bestanden' or '1 Bestand' or 'N Bestanden'. (I supported German
and French too.)
 
I can't remember the details, but if there were differences in word
order, then the whole phrase was translated when there were no variable
parts.
Juha Nieminen <nospam@thanks.invalid>: Sep 19 10:12AM

> C++ application; primarily by getting rid of all outputstringstream
> crap (replacing with snprintf) and eliminating most trivial
> run-time (vs. startup time) uses of std::string.
 
While not super rare, it's nevertheless relatively uncommon to need maximum
I/O throughput in most applications that, for example, use this kind of
class as discussed in this thread. In the vast majority of situations,
even in very CPU-intensive number-crunching applications, the I/O speed
is largely irrelevant (because the amount of data to be printed isn't
that great, nor requires maximum speed).
 
Even in the cases where one *does* need maximum efficiency in I/O
(this would often be some kind of program that handles enormous
amounts of input and/or output data, eg. in some kind of server
or other similar application, requiring the program to read and/or
write gigabytes and gigabytes of data as fast as possible), any
experienced C++ programmer will know that std::ostream and
dynamically allocated strings will not be appropriate for this,
and will use something else. However, even in this case it's not
like having the operator<<() there is going to hurt something.
If it's not suitable for the task at hand, simply don't use it.
 
There are some situations where having a suitable operator<<()
overload for a particular type is very practical and handy.
Such as, for example, when using Google Test (which has extensive
support for adding additional information to an error message
via operator<< overloads.)
Juha Nieminen <nospam@thanks.invalid>: Sep 19 10:17AM

> One quite often runs across C++ purists (or new grads) who falsly eschew
> C constructs as "not C++".
 
My response to them is that "if it's in the C++ standard, then it's C++,
through and through. Use the tools that are best for the task at hand."
 
Just because something is "inherited" from C (so to speak) doesn't mean
it's not suitable and perfectly valid to use in C++. After all, keywords
like 'for' and 'if' are inherited from C. Does that mean they shouldn't
be used in C++? Why is using those ok, but eg. using std::printf() is not?
What's the difference?
 
> Unfortunately, we need to support GCC4 through GCC11 efficiently.
 
I find it fascinating how common gcc 4 is still out there in the wild,
even to this day.
 
It kind of has taken the mantle of gcc 2, which likewise was in very
wide use years and years after it had become completely obsolete and
antiquated (as, IIRC, it didn't even support 100% of C++98.)
The difference is that gcc 4 has persisted for a *lot* longer than
gcc 2 did.
 
I blame certain Linux distros for this.
Juha Nieminen <nospam@thanks.invalid>: Sep 19 10:23AM


> C: printf("A=%d B=%f C=%s\n", a, b, c);
 
> Compared with the equivalent in any of my languages:
 
> M: println =a, =b, =c
 
Uh... println in your languages will automatically print "name=" before
printing the value of a variable? What if you want to use spaces around
the '='? What if you want to use another character instead, like ':',
and have a space only after that character but not before it?
 
Anyway, it's relatively easy in C++ to implement a function that behaves
like std::ostream, but uses a function call syntax instead, like:
 
myprint("a=", a, ", b=", b, ", c=", c, "\n");
 
> The C++ just looks dreadful (and I keep forgetting the << or writing
> commas instead).
 
It's C++'s fault that you keep forgetting the <<?
Bart <bc@freeuk.com>: Sep 19 12:04PM +0100

On 19/09/2021 11:23, Juha Nieminen wrote:
> printing the value of a variable? What if you want to use spaces around
> the '='? What if you want to use another character instead, like ':',
> and have a space only after that character but not before it?
 
It prints the whole expression not just the name, and is primarily for
debugging prints where large numbers of such temporary statements will
be added and removed. With C, it would make my RSI worse.
 
C allows you to define a macro to reduce that duplication, example:
 
#define EQ(x) #x "=",x
 
where the expression is an exact copy of what's in the source (although
I prefer them in upper case for emphasis). But if I plug that into my C
example:
 
printf("%s%d %s%f %s%s\n", EQ(a), EQ(b), EQ(c));
 
you find you're doing even more typing!
 
Of course for permanent print statements and more precise control, you
add those annotations more conventionally.
 
 
>> The C++ just looks dreadful (and I keep forgetting the << or writing
>> commas instead).
 
> It's C++'s fault that you keep forgetting the <<?
 
Yes, because it is so peculiar. I wouldn't know how to create such a
function, but it would have been a better way of presenting a print
feature, and more conventional.
scott@slp53.sl.home (Scott Lurndal): Sep 19 02:07PM


>> Unfortunately, we need to support GCC4 through GCC11 efficiently.
 
>I find it fascinating how common gcc 4 is still out there in the wild,
>even to this day.
 
It is the default compiler for Redhat 6 and Redhat 7 (and thus
the deriviations such as CentOS) which are widely used.
 
 
>I blame certain Linux distros for this.
 
I wouldn't use the verb "blame" here. Most programmers don't
particularly care about the version of the compiler, so long as
it works.
Juha Nieminen <nospam@thanks.invalid>: Sep 19 03:58PM


> I wouldn't use the verb "blame" here. Most programmers don't
> particularly care about the version of the compiler, so long as
> it works.
 
Yeah, the problem with gcc 4 is that it doesn't support fully C++11
(if I remember correctly), much less newer versions.
Juha Nieminen <nospam@thanks.invalid>: Sep 19 04:01PM

> Yes, because it is so peculiar. I wouldn't know how to create such a
> function, but it would have been a better way of presenting a print
> feature, and more conventional.
 
The basic idea with overloading a binary operator, rather than using a
function call syntax, is that the output can be expanded with your own
custom types (which is not really possible if it used a function call
syntax).
 
In other words, you can achieve this:
 
MyClass obj;
std::cout << "Value = " << obj << "\n";
 
I suppose there could be a contrived way of achieving the same thing
with a function call syntax, ie. that you could write
 
std::cout("Value = ", obj, "\n");
 
but I'm not sure how simple that could be made to be. Especially in C++98.
Bart <bc@freeuk.com>: Sep 19 05:39PM +0100

On 19/09/2021 17:01, Juha Nieminen wrote:
> with a function call syntax, ie. that you could write
 
> std::cout("Value = ", obj, "\n");
 
> but I'm not sure how simple that could be made to be. Especially in C++98.
 
That doesn't make sense to me, or maybe there are some limitations in
C++ so that it can only work that way.
 
I don't do overloads in my languages except for the 'tostr' operator
(normally unary, but see below) in my dynamic language.
 
'tostr' is applied automatically to each item in a statement like this:
 
println a, b, c
 
And it turns whatever a, b, c are into strings.
 
There is a default handler for the types known to the language, but a
user defined handler can be applied to a user type.
 
Example (the overloading syntax is crude, but it works):
 
record date=(var day,month,year)
 
function tostr_date(a,fmt)=
return sfprint("#/#/# CE", a.day, a.month, a.year)
end
 
d:=date(19,9,2021)
 
println d # default tostr shows '(19,9,2021)'
 
$setoverload(($tostr),date,tostr_date)
 
println d # custom tostr shows '19/9/2021 CE'
 
 
No binary overloads of some mysterious "<<" operator needed, although
'tostr' is really a binary operator; the second operand provides
optional format info, ignored in my example. If I write:
 
println d:"..."
 
then that "..." string appears as the fmt parameter, and it can be used
in any manner.
scott@slp53.sl.home (Scott Lurndal): Sep 19 09:53PM

>> it works.
 
>Yeah, the problem with gcc 4 is that it doesn't support fully C++11
>(if I remember correctly), much less newer versions.
 
That is correct. As the worldwide data centers migrate to newer RHEL
releases, we're planning on GCC 7.3 as the baseline, which should
open up _some_ limited C++11 feature use (e.g. static_assert would be
useful to elimate some unnecessary runtime assertion checks).
scott@slp53.sl.home (Scott Lurndal): Sep 19 09:54PM


>In other words, you can achieve this:
 
> MyClass obj;
> std::cout << "Value = " << obj << "\n";
 
fprintf(stdout, "Value = %s\n" obj.to_string());
Bonita Montero <Bonita.Montero@gmail.com>: Sep 19 01:33PM +0200

Look at this code:
 
#include <concepts>
 
using namespace std;
 
struct A
{
void op();
};
 
struct B
{
void op( bool );
};
 
template<typename T>
concept concept_C = is_same<T, A>::value || is_same<T, B>::value;
 
template<typename T>
requires concept_C<T>
struct C
{
void opA()
requires is_same<T, A>::value;
void opB( bool f )
requires is_same<T, B>::value;
T *pt;
};
 
template<typename T>
requires concept_C<T>
void C<T>::opA()
requires is_same<T, A>::value
{
pt->op();
}
 
template<typename T>
requires concept_C<T>
void C<T>::opB( bool f )
requires is_same<T, B>::value
{
pt->op( f );
}
 
template
struct C<A>;
 
template
struct C<B>;
 
MSVC will instantiate opA for A as well as B and opB
also for A as well as B and gives the following errors:
 
(42,10): error : too many arguments to function call, expected 0, have 1
(46,8): message : in instantiation of member function 'C<A>::opB'
requested here
(7,7): message : 'op' declared here
(34,9): error : too few arguments to function call, expected 1, have 0
(49,8): message : in instantiation of member function 'C<B>::opA'
requested here
(12,7): message : 'op' declared here
 
clang gives similar errors, but g++12 is smarter and skips inappropriate
compilation, i.e. filters the functions through their concepts, i.e. it
compiles opA only for A and opB only for B - so which compiler is right
here ? And is there a workaround ?
Bonita Montero <Bonita.Montero@gmail.com>: Sep 19 06:03PM +0200

I wrote the same thing on Stack Overflow. C<A>::opB( bool )
and C<B>::opA() shouldn't be instantiated according to the
standard. So this is a bug of clang. MSVC has fixed the bug
in the latest release.
Bonita Montero <Bonita.Montero@gmail.com>: Sep 19 11:23AM +0200

#pragma once
#if defined(__cpp_concepts)
#include <concepts>
 
template<typename Container>
concept concept_reverser = requires( Container cont )
{
{ cont.rbegin() } -> std::convertible_to<typename
Container::reverse_iterator>;
{ cont.rend() } -> std::convertible_to<typename
Container::reverse_iterator>;
};

No comments: