Monday, September 12, 2022

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

Bonita Montero <Bonita.Montero@gmail.com>: Sep 12 06:10AM +0200

Am 11.09.2022 um 21:41 schrieb Chris M. Thomasson:
> [...]
 
> Perhaps I am misunderstanding you here, but, there are differences
> behind different STL implementations, no?
 
I think mostly not since the way they're impemented is obvious.
Juha Nieminen <nospam@thanks.invalid>: Sep 12 05:47AM

> gcc, python, make and binutils installed. Which is true in case of myself, but but wrong
> for 99% of Windows users. So, for those 99% there is a need to install another 2 or 5 GB.
 
> I wonder, if RMS is doing it intentionally or out of ignorance.
 
You can read the .texi file directly. It's not like it's some inscrutable highly
obfuscated binary format. It's actually significantly more legible than eg. XML.
Bo Persson <bo@bo-persson.se>: Sep 12 09:18AM +0200

>> handle that, but it's not up to their mindset.
 
> Its nothing to do with their mindset, its to do with knowing EXACTLY what the
> code is going to do and/or where the program counter is going to go.
 
I have never understood how you can know EXACTLY what the code does in
 
int y = f(x);
 
but have no clue about what happens in
 
my_class f(x);
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Sep 12 12:27AM -0700

On 9/11/2022 9:10 PM, Bonita Montero wrote:
 
>> Perhaps I am misunderstanding you here, but, there are differences
>> behind different STL implementations, no?
 
> I think mostly not since the way they're impemented is obvious.
 
Remember way back, iirc, msvc 6.0 used Dinkumware?
 
https://www.dinkumware.com/
 
A twisted nest of code that was! Good luck...
Bonita Montero <Bonita.Montero@gmail.com>: Sep 12 09:42AM +0200

Am 12.09.2022 um 09:18 schrieb Bo Persson:
 
> int y = f(x);
 
> but have no clue about what happens in
 
> my_class f(x);
 
That's a function that returns a my_class object.
This object is mostly moved from inside f( x ).
if f( x ) is inlined this move is optimized away.
Bo Persson <bo@bo-persson.se>: Sep 12 12:05PM +0200

On 2022-09-12 at 09:42, Bonita Montero wrote:
 
>> but have no clue about what happens in
 
>> my_class f(x);
 
> That's a function that returns a my_class object.
 
It's a function only if x is a type. But x is a value, like in the int
case, so it is a constructor call.
 
> This object is mostly moved from inside f( x ).
> if f( x ) is inlined this move is optimized away.
 
And still, the C guys cannot see what happens here. So "improve" this as
 
struct my_class f;
init(&f, x);
 
And now it is suddenly obvious EXACTLY what happens? :-)
Bonita Montero <Bonita.Montero@gmail.com>: Sep 12 12:12PM +0200

Am 12.09.2022 um 12:05 schrieb Bo Persson:
 
>> This object is mostly moved from inside f( x ).
>> if f( x ) is inlined this move is optimized away.
 
> And still, the C guys cannot see what happens here. So "improve" this as
 
The C++-"guys" can !
 
Muttley@dastardlyhq.com: Sep 12 03:58PM

On Mon, 12 Sep 2022 09:18:44 +0200
 
>int y = f(x);
 
>but have no clue about what happens in
 
>my_class f(x);
 
And if that instance has virtual functions where will the VFT go and what
allocates it? Ditto any non primitive types which may cause an implcit cascade
of allocations.
 
People like yourself and Bonita just don't seem to get the fact that there's no
safety net inside ring 0 code. You can't just handwave away stuff that in
application code would be taken care of by the runtime.
Bonita Montero <Bonita.Montero@gmail.com>: Sep 12 06:48PM +0200


> People like yourself and Bonita just don't seem to get the fact that there's no
> safety net inside ring 0 code. You can't just handwave away stuff that in
> application code would be taken care of by the runtime.
 
You're mentally like ring 0 - the whole day.
Opus <ifonly@youknew.org>: Sep 12 07:22PM +0200

Le 11/09/2022 à 11:16, Bonita Montero a écrit :
>> utilities?
 
> That's a matter of the writer's mindset and tradition and
> not of necessities.
 
What's exactly the point of keeping trolling the comp.lang.c group while
carefully cross-posting to the c++ one just in case we didn't get it
(and probably to show your c++ peers that you're a faithful promoter)?
 
What do you get from it and why is it that people keep replying? That's
polluting a good half of the new posts. Good lord.
Bo Persson <bo@bo-persson.se>: Sep 12 08:32PM +0200


> And if that instance has virtual functions where will the VFT go and what
> allocates it? Ditto any non primitive types which may cause an implcit cascade
> of allocations.
 
Just don't see how we know that int y = f(x); doesn't call other
functions that allocate structs on the heap? Structs that contain
function pointers that are then called, cause an implicit cascade
of allocations.
 
 
> People like yourself and Bonita just don't seem to get the fact that there's no
> safety net inside ring 0 code. You can't just handwave away stuff that in
> application code would be taken care of by the runtime. >
 
You can look at the class declaration to see what the constructor does -
and that there are no virtual functions present. Just like you have to
verify what the C function does.
Bonita Montero <Bonita.Montero@gmail.com>: Sep 12 02:01PM +0200

I wondered why a std::function has 64 bytes in size with MSVC.
I compiled the following code and disassembled it.
 
#include <iostream>
#include <functional>
#include <string_view>
 
using namespace std;
 
int main()
{
string_view
sv( "hello" ),
sv2( "wolrld" );
function<void ()> fn( [sv, sv2]() { cout << sv << " " << sv2 << endl; } );
cout << sizeof fn << endl;
fn();
}
 
The function object passed to the fn constructor has two captures,
i.e. it isn't a normal C-callable so that you might expect that
the constructor of the function<>-object allocates external memory
for that.
But because of its size I expected function<> to have sth. like a
small string optimization, thereby including the whole function
object in its own body up to a certain size. And the disassembly
showed that my expectations were right.
With libstdc++ function<>-objects are 32 bytes in size, so you
won't have to expect that there's some optimization like this.
Is there any mailing list of libstdc++ where I can suggest
such an optimization ?
Paavo Helde <eesnimi@osa.pri.ee>: Sep 12 03:29PM +0300

12.09.2022 15:01 Bonita Montero kirjutas:
> small string optimization, thereby including the whole function
> object in its own body up to a certain size. And the disassembly
> showed that my expectations were right.
 
That's strange because the compiler would be buggy if it copied the
string content when only asked to copy a string_view. And indeed, a
little modification to your program proves that the string content is
not contained in the function object, so there is no SSO needed or used:
 
#include <iostream>
#include <functional>
#include <string_view>
 
int main() {
char hello[] = "hello";
char world[] = "world";
std::string_view sv(hello), sv2(world);
std::function<void()> fn([sv, sv2]() { std::cout << sv << " " <<
sv2 << std::endl; });
std::cout << sizeof fn << std::endl;
 
const char* adieu = "adieu";
std::copy(adieu, adieu + sizeof hello, hello);
fn(); // outputs: adieu world
}
Bonita Montero <Bonita.Montero@gmail.com>: Sep 12 03:38PM +0200

Am 12.09.2022 um 14:29 schrieb Paavo Helde:
 
> That's strange because the compiler would be buggy if it copied the
> string content when only asked to copy a string_view. ...
 
That's not what I wanted to say. The string_views are copied into the
lambda object and the lambda object is copied into the function<> object
as wole without any external allocations. So the function<>-object has
some space for small function-objects passed to its constructor.
Paavo Helde <eesnimi@osa.pri.ee>: Sep 12 05:42PM +0300

12.09.2022 16:38 Bonita Montero kirjutas:
> lambda object and the lambda object is copied into the function<> object
> as wole without any external allocations. So the function<>-object has
> some space for small function-objects passed to its constructor.
 
Ah, I see. It seems I misread your original post. Yes it seems the
lambda arguments up to 48 bytes are passed as embedded inside the
std::function object, with MSVC.
Bonita Montero <Bonita.Montero@gmail.com>: Sep 12 10:08AM +0200

I just wanted to benchmark a function<>-object against a
raw C function-pointer. I expected it to be slightly slower.
 
#include <iostream>
#include <functional>
#include <chrono>
 
using namespace std;
using namespace chrono;
 
int main()
{
auto bench = []<typename Fn>( Fn &&fn )
{
auto start = high_resolution_clock::now();
int x = 0;
for( size_t r = 100'000'000; r--; fn( &x ) );
cout << duration_cast<nanoseconds>( high_resolution_clock::now() -
start ).count() / 1.0e8 << endl;
};
auto fn = []( int *pX ) { ++*pX; };
void (*volatile pVFn)( int * ) = fn;
bench( pVFn );
function<void ( int * )>
fFn( fn ),
*volatile pFFn = &fFn;
bench( *pFFn );
}
 
But interestingly the performance is the same
with about 1.85ns per call on my Zen2-computer.
Bonita Montero <Bonita.Montero@gmail.com>: Sep 12 10:38AM +0200

The problem with my before code might be that I pass a normal C function
pointer to a function<>-object. The constructor of that function-object
is constexpr and if you pass simple C function pointers to it it doesn't
allocate external memory for that function object and the function-poin-
ter is just a member of the function object.
With that the compiler could do a shortcut, bypassing the logic of the
function-object and having a usual C function call. I guessed that this
is the reason for the call to have the same performance.
So I wrote a slightly different benchmark:
 
#include <iostream>
#include <functional>
#include <chrono>
#include <atomic>
 
using namespace std;
using namespace chrono;
 
int main()
{
auto bench = []<typename Fn>( Fn &&fn, double rounds )
{
auto start = high_resolution_clock::now();
fn();
cout << duration_cast<nanoseconds>( high_resolution_clock::now() -
start ).count() / rounds << endl;
};
size_t const ROUNDS = 100'000'000;
bench(
[]()
{
int x = 0;
atomic<void (*)(int *)> aCFn( []( int *pX ) { ++ *pX; } );
for( size_t r = ROUNDS; r--; )
aCFn.load( memory_order_relaxed )( &x );
}, ROUNDS );
bench(
[]()
{
int x = 0;
function<void ()> cppFn( [&]() { ++x; } );
atomic<function<void ()> *> aCppFn( &cppFn );
for( size_t r = ROUNDS; r--; )
(*aCppFn.load( memory_order_relaxed ))();
}, ROUNDS );
}
 
With that benchmark the C++-part allocates external storage for the
copyied function-object. To prevent any optimizations I store pointers
to the C-function and the C++ function<>-object as atomics.
I didn't expect any big differences, but surprisingly the performance
is still the same !
C++ is just a so powerful beast, I really like it.
Bonita Montero <Bonita.Montero@gmail.com>: Sep 12 07:02AM +0200

It could be even easier:
For C-APIs that have a callback and a context-pointer you
simply could write a wrapper that is given a function-object.
 
#include <iostream>
#include <vector>
#include <string>
#include <link.h>
#include <climits>
#include <dlfcn.h>
 
using namespace std;
 
template<typename Fn>
requires requires( Fn fn ) { { fn( (dl_phdr_info *)nullptr, (size_t)0 )
} -> same_as<int>; }
int dlIteratePhdr( Fn fn )
{
return dl_iterate_phdr(
[]( dl_phdr_info *image, size_t size, void *pFn ) -> int
{
return (*(Fn *)pFn)( image, size );
}, &fn );
};
 
int main()
{
size_t nImages = 0;
dlIteratePhdr( [&]( dl_phdr_info *, size_t ) -> int { ++nImages; return
0; } );
vector<string> images;
images.reserve( nImages );
if( dlIteratePhdr(
[&]( dl_phdr_info *image, size_t size ) -> int
{
try
{
images.emplace_back( image->dlpi_name );
return 0;
}
catch( bad_alloc const & )
{
return 1;
}
} ) )
return EXIT_FAILURE;
for( string const &image : images )
cout << "\"" << image << "\"" << endl;
}
 
This makes these APIs conveniently usable as if they were
native C++ APIs.
Juha Nieminen <nospam@thanks.invalid>: Sep 12 06:03AM

> return (*(fn_callback_t *)pFn)( image, size );
> }, &fnDlIterate ) )
> return EXIT_FAILURE;
 
I think this demonstrates beautifully why you shouldn't use "using
namespace std;"
 
The code is full of obfuscated names and it's very difficult to see with
a visual scan which names are declared locally, which are from the
standard library, and which are from some third-party library.
 
Also, I thought that lambdas made std::bind() obsolete.
Bonita Montero <Bonita.Montero@gmail.com>: Sep 12 09:16AM +0200

Am 12.09.2022 um 08:03 schrieb Juha Nieminen:
 
> I think this demonstrates beautifully why you shouldn't use "using
> namespace std;"
 
Idiot.
 
> The code is full of obfuscated names and it's very difficult to see with
> a visual scan which names are declared locally, which are from the
> standard library, and which are from some third-party library.
 
There's nothing obfuscated with that, it's just not your taste.
You're a person that constantly feels uncertain and you need
some rituals to feel certain; people that don't adhere to your
rituals make you feel ucertain again.
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: