Tuesday, April 20, 2021

Digest for comp.lang.c++@googlegroups.com - 25 updates in 2 topics

"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Apr 19 05:16PM -0700

On 4/19/2021 10:05 AM, jacobnavia wrote:
>     if (result) {
>         fread(result,1,size,f);
>         result[size]=0;
 
Why are you zero terminating this? Is the files contents meant to be
used as a C string?
 
 
>     }
>     return result;
> }
[...]
Juha Nieminen <nospam@thanks.invalid>: Apr 20 04:02AM

> Once upon a time, C++ claimed being a superior system programming language.
> Luckily, Linus Torvalds was not fooled as most C++ beginners were.
 
Almost nothing of what Linux wrote in that idiotic rant was true even back
when it was written, much less now.
 
Perhaps the most idiotic idea in his rant is the idea that C somehow
magically and automatically leads people to write good and efficient
code. No need to even explain how that's even possible.
Juha Nieminen <nospam@thanks.invalid>: Apr 20 04:05AM

> stuff works here, even if it all happens automatically.
> I'd say that the fact that it happens behind the scenes might even make
> it harder for a beginner.
 
Might make *what* harder for a beginner, exactly?
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Apr 19 09:42PM -0700

On 4/17/2021 10:12 AM, Manfred wrote:
 
> As second, I agree with Jacob that a text editor is a simple tool, but
> it is not easy to make expecially for a beginner.
 
> [snip]
 
I would not call C easy. Instead, I would refer to it as being more to
"the point", perhaps? Things have to be "explicit", it can be more
verbose. I say that because C does not have RAII. Also, C is really nice
to create plugins wrt creating portable bindings to other languages.
Usually in C, things tend go like the following when coding up a new
"object", so to speak:
 
Typing in the newsreader, sorry for typos.
________________________________
 
#include <stdio.h>
#include <stdlib.h>
 
 
struct foo
{
int a;
void* mem;
};
 
int foo_create(struct foo* const self)
{
if ((self->mem = malloc(sizeof(42))))
{
self->a = 84;
printf("foo_create(%p)\n", (void*)self);
return 1;
}
 
return 0;
}
 
void foo_destroy(struct foo const* const self)
{
printf("foo_destroy(%p)\n", (void*)self);
free(self->mem);
}
 
void foo_output(struct foo const* self, FILE* output)
{
printf("foo_output(%p)\n", (void*)self);
fprintf(output, "struct foo(%p)->a = %d\n", (void*)self, self->a);
fprintf(output, "struct foo(%p)->mem = %p\n", (void*)self, self->mem);
}
 
 
int main()
{
struct foo foo;
 
if (foo_create(&foo))
{
foo_output(&foo, stdout);
 
foo_destroy(&foo);
}
 
return 0;
}
________________________________
 
 
See how I have to make an explicit call to foo_destroy? This can be
handled in C++ in a "cleaner/safer" manner. Again, typing in the
newsreader sorry for typos. This would be a simple C++ wrapper to the C
API above:
________________________________
 
struct foo_wrapper
{
struct foo m_foo;
 
foo_wrapper() { if (! foo_create(&m_foo)) throw; }
~foo_wrapper() { foo_destroy(&m_foo); }
 
void output(FILE* output_) const
{
foo_output(&m_foo, output_);
}
};
 
________________________________
 
Then we can go:
 
{
foo_wrapper foo;
foo.output(stdout);
}
 
There. If it fails to create a foo in foo_create, it throws. Otherwise,
foo_destroy is guaranteed to be called when foo goes out of scope. So,
C++ can be more convenient, and safer. No need to use all of C++, but it
does have its moments.
Juha Nieminen <nospam@thanks.invalid>: Apr 20 04:56AM

> Such kind of topic (like, C/C++ which one is better) had been heavily discussed
> several times before, in ways like debating which religion is better.
> Just do not be overly zealous.
 
The thing that prompted me to vent my frustrations in this thread was, as
I mentioned in my original post, a recent attempt at helping a beginner
who wanted to learn C.
 
It's hard to convey the difficulty and frustration trying to do this,
during the several days of trying to explain things and teach a good
proper approach at this type of C development, especially given the
kind of project that this person was trying to create (ie. a simple
text editor that loads a text file and allows the user to make
edits to it).
 
During the ordeal I just couldn't help but to think, and to comment
several times, how utterly and radically easier the very things he
was trying to do would be in C++, using std::string and std::vector.
(But he wanted to learn C specifically, I'm not sure exactly why.)
It was not only difficult to try to explain how dynamic memory
allocation works in C, and how such string manipulation should be
approached, but also it was difficult to explain *why* it should
be approached like that (ie. the idea of creating a more abstract
reusable "module" for handling dynamically allocated strings).
 
I would have been able to write such a program, but it was very difficult
to teach a complete beginner to do so (other than just giving all the
code readymade, which kind of defeats the purpose).
 
This whole thing just reminded me of *why* I dislike C so much. Sometimes,
even oftentimes, doing things that are extraordinarily simple in other
languages (such as C++) are really laborious and error-prone in C,
and require a level of knowledge, understanding and care that in
general you don't need in those other languages.
 
This is not the first time that pretty much this exact thing has happened
to me. It was merely the latest reminder and refresher.
jacobnavia <jacob@jacob.remcomp.fr>: Apr 20 07:32AM +0200

Le 20/04/2021 à 02:16, Chris M. Thomasson a écrit :
>>          result[size]=0;
 
> Why are you zero terminating this? Is the files contents meant to be
> used as a C string?
 
Maybe. This utility doesn't NEED to know the usage, and zero terminating
it allows to use it as a string if the client code needs to. The cost is
completely negligible (writing zero to a byte).
 
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Apr 19 11:47PM -0700

On 4/19/2021 10:32 PM, jacobnavia wrote:
>> used as a C string?
 
> Maybe. This utility doesn't NEED to know the usage, and zero terminating
> it allows to use it as a string if the client code needs to.
 
Okay. I see.
 
 
> The cost is
> completely negligible (writing zero to a byte).
 
I can think of a pretty hardcore nitpick. Think of a space constrained
client system that has very strict structured memory usage. Say it
forces your FileToRam function to use its own special malloc/free where
everything is aligned and padded to cache lines. So, that 1 byte of over
allocation can make malloc allocate a whole extra cache line! ;^)
 
The user wants to load a file that is 32 cache lines in size. Therefore,
it wants to allocate exactly 32 cache lines of memory, no more, no less.
However, you add that extra byte and force malloc to actually allocate
33 cache lines instead.
 
Sorry for the ultra hardcore nitpick. ;^o
 
 
Juha Nieminen <nospam@thanks.invalid>: Apr 20 10:29AM

> char *FileToRam(FILE *input)
 
By the way, it's always a good idea to use clear names that convey what
the function is doing, especially when we are talking about a C function
that does something like this. Optimally code would be self-documenting.
 
I think a declaration like this better conveys what the function does,
and that the calling code is responsible for freeing the return value:
 
char* allocByteArrayWithContentsOfFile(FILE* input);
 
Or, if you prefer:
 
char* alloc_byte_array_with_contents_of_file(FILE* input);
 
The "alloc" at the beginning better conveys that this is, indeed, an
allocation function and that the returned pointer points to something that
needs to be freed. (In C++ one usually doesn't need to convey this because,
usually, you would use as return value an object that itself manages the
allocated memory.)
 
It's also quite customary in C for such functions to return a success/error
code, instead of the allocated array. While not universal, you see this
quite a lot in C. So, perhaps, it could be like:
 
int alloc_byte_array_with_contents_of_file(FILE* input, char** dest);
 
Different return values can then be used to convey different types of error
(such as an error reading or seeking the file, or allocating the array).
jacobnavia <jacob@jacob.remcomp.fr>: Apr 20 02:14PM +0200

Le 20/04/2021 à 12:29, Juha Nieminen a écrit :
 
> char* allocByteArrayWithContentsOfFile(FILE* input);
 
> Or, if you prefer:
 
> char* alloc_byte_array_with_contents_of_file(FILE* input);
 
I agree that code should be self documenting BUT... the risk of
extremely long names as you propose is that you make a typo somewhere or
that users fed up with typing again and again such long names go to
 
#define readit(a) alloc_byte_array_with_contents_of_file(a)
 
 
> needs to be freed. (In C++ one usually doesn't need to convey this because,
> usually, you would use as return value an object that itself manages the
> allocated memory.)
 
Since the result is a char array with the contents of the file, and the
function taks only 1 input, it is obvious that the copntents are
allocated...
 
> code, instead of the allocated array. While not universal, you see this
> quite a lot in C. So, perhaps, it could be like:
 
> int alloc_byte_array_with_contents_of_file(FILE* input, char** dest);
 
Note that the function returns NULL in case of failure. This is very
common in C.
 
> Different return values can then be used to convey different types of error
> (such as an error reading or seeking the file, or allocating the array).
 
Yeah, but this is more concise, and supposes that you do not care about
what really happened, just that it didn't work.
wij <wyniijj@gmail.com>: Apr 20 05:22AM -0700

On Tuesday, 20 April 2021 at 12:56:34 UTC+8, Juha Nieminen wrote:
> kind of project that this person was trying to create (ie. a simple
> text editor that loads a text file and allows the user to make
> edits to it).
 
Those are common problems to teach that kind level of C beginners.
Nothing to surprise or be frustrated.
 
A minimal functional text editor (like nano) is not simple from ground up.
I do not think using 'pure' C++ (Smart Macro Library or STL) can do the
job 'elegantly' and 'efficiently', as well as numerous system commands,
with respect to using C.
 
> general you don't need in those other languages.
 
> This is not the first time that pretty much this exact thing has happened
> to me. It was merely the latest reminder and refresher.
 
If we were talking some example programs from some C++ book, I have no comment.
Manfred <noname@add.invalid>: Apr 20 06:27PM +0200

On 4/20/2021 6:05 AM, Juha Nieminen wrote:
>> I'd say that the fact that it happens behind the scenes might even make
>> it harder for a beginner.
 
> Might make *what* harder for a beginner, exactly?
 
Anything that happens behind the scenes and the compiler "does it for
you" does require knowledge by the programmer about something they don't
see and yet they must be aware of and know how it works.
And yes, they are intended to (and actually do) make life easier for the
programmer /after/ they master their mechanics, but a beginner's mistake
related with such hidden features is likely to lead to somewhat
shady/obscure bugs - i.e. hard for said beginner.
 
On a different perspective, consider e.g. rvalue refs and "universal"
refs (in Meyers' terms) - they make a useful feature, but good luck
explaining it to said beginner.
Juha Nieminen <nospam@thanks.invalid>: Apr 20 04:41PM

> programmer /after/ they master their mechanics, but a beginner's mistake
> related with such hidden features is likely to lead to somewhat
> shady/obscure bugs - i.e. hard for said beginner.
 
The problem with C is that anything that requires manual construction and
destruction will "contaminate" (can't think of a better word) anything that
uses that first thing, with the same requirements. All the way down.
 
If you have this kind of "string" object that requires manual construction
and destruction, and you put it as a member of a struct, suddenly that
struct "inherits" the same requirement. Use that struct in yet another
struct and that, too, will "inherit" the same requirement.
 
Moreover, suppose you have a complex hierarchy of structs as members of
other structs, no dynamic memory allocation in sight so far. Then you need
to add dynamic memory allocation to one of the structs (that's being used
as a member of other structs etc) and suddenly all that code breaks. If
there are millions of lines of code using any of those structs that have
now "inherited" the need for manual construction and destruction (even though
they were never designed for that initially), you'll have to go and modify
those millions of lines to follow suit.
 
The complexity can quickly grow exponentially.
 
Not so in C++, if you design your structs/classes correctly. Even if some
struct/class very deep in the dependency tree suddenly starts allocating
memory dynamically, that doesn't matter. None of the other structs/classes
that may be using it as their members need to be touched in any way. The
struct/class automatically takes care of its own memory management. You
don't need to go through the millions of lines of code to add support.
 
> On a different perspective, consider e.g. rvalue refs and "universal"
> refs (in Meyers' terms) - they make a useful feature, but good luck
> explaining it to said beginner.
 
I wouldn't teach function and array pointers as a first thing to a beginner.
Likewise I wouldn't teach rvalue references as a first thing.
Juha Nieminen <nospam@thanks.invalid>: Apr 20 04:44PM

>> char* alloc_byte_array_with_contents_of_file(FILE* input);
 
> I agree that code should be self documenting BUT... the risk of
> extremely long names as you propose is that you make a typo somewhere
 
Yeah. It's not like the compiler will give you an error message or anything.
 
> or
> that users fed up with typing again and again such long names go to
 
> #define readit(a) alloc_byte_array_with_contents_of_file(a)
 
That's their shame, not yours.
 
 
> Since the result is a char array with the contents of the file, and the
> function taks only 1 input, it is obvious that the copntents are
> allocated...
 
Not necessarily.
 
Does, for example, fopen() dynamically allocate the FILE object?
I have no idea. It's not at all obvious.
 
 
>> int alloc_byte_array_with_contents_of_file(FILE* input, char** dest);
 
> Note that the function returns NULL in case of failure. This is very
> common in C.
 
The problem is that with one single error value there's no way of knowing
*why* the function failed. That doesn't make it a very good function.
jacobnavia <jacob@jacob.remcomp.fr>: Apr 20 07:44PM +0200

Le 20/04/2021 à 18:41, Juha Nieminen a écrit :
> and destruction, and you put it as a member of a struct, suddenly that
> struct "inherits" the same requirement. Use that struct in yet another
> struct and that, too, will "inherit" the same requirement.
 
No.
 
Just have a constructotr and never free anything. Then link with Boehm's
GARBAGE COLLECTOR and be done with it!
 
I have already said this in this thread but people will go on arguing
about how difficult is to keep the accounting of memory pieces right.
 
And they are right but refuse to see the right tool for theirpurpose:
just use a garbage collector and BE DONE WITH IT.
 
I use an automatic car, no passing of gears. Recently I spoke with a
taxi driver... "Don't you get bored passing gears all day?"
 
And he acknowledged: yes, My arm hurts at the end of the day. Maybe you
are right, should get an automatic...
 
Use an automatic garbage collector and BE DONE WITH IT. And please, do
not start with performance problems, in most string applications the gc
is completely transparent.
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Apr 20 07:53PM +0200

On 20.04.2021 18:41, Juha Nieminen wrote:
> now "inherited" the need for manual construction and destruction (even though
> they were never designed for that initially), you'll have to go and modify
> those millions of lines to follow suit.
 
Very good points.
 
In addition, what C++ provides as built-in language features, has to be
supplied as 3d party library or DIY functionality in C.
 
That means that in C
 
* one can't leverage a single knowledge base, but must learn each
support library that's needed for those C++ features, and
* the compiler can't be fine tuned to a particular implementation.
 
 
 
> The complexity can quickly grow exponentially.
 
Not sure about that though, but that it grows, yes.
 
 
>> explaining it to said beginner.
 
> I wouldn't teach function and array pointers as a first thing to a beginner.
> Likewise I wouldn't teach rvalue references as a first thing.
 
The "Accelerated C++" book by Andrew Koenig and Barbara Mooooooooo
showed that the high level approach can really work for C++.
 
But I think that for these fundamental aspects of programming, if one
desires to teach at a high level of abstraction then some other language
will be better suited. I'm thinking C# (which I known from the "classic"
C# days) or, say, Rust (which I don't know beyond "Hello, world!").
 
A nice feature of C++ is that it's /possible/ to teach programming in a
mixed way, introducing the low level stuff but using containers, strings
etc. It's sad that C++ still lacks standard 128-bit integers and ditto
currency type; still lacks datetime type; still lacks a practically
usable for beginners version of `std::ranges::views::iota` (I prefer to
define things so one can write e.g. `for( const int i: zero_to( n ) )`;
not to mention that the common /C++ implementations/ for Windows still
lack support for UTF-8, both in console and GUI programs, so that
learners can't even write a program that displays their non-English name
in Windows without using 3rd party libraries or the Windows API.
 
That's just basic functionality.
 
Try motivating a leaner by coding up the (recursive) C curve in standard
C++ only. Oh, no graphics. Oh well.
 
So, as I see it standard C++ in itself is not good for learners, but it
can be good with the right 3rd party libraries. I guess you all know
that I've been hobby-working on libraries to fill the mentioned data
type and console i/o voids, and that I've found that it decidedly is a
non-trivial design task (which may be part of the reason why such
libraries don't already exists). And perhaps the good Mr. Fibble's
Neo-something could help with the graphics and sound aspects.
 
- Alf
Kaz Kylheku <563-365-8930@kylheku.com>: Apr 20 06:30PM


> No.
 
> Just have a constructotr and never free anything. Then link with Boehm's
> GARBAGE COLLECTOR and be done with it!
 
You are talking about a different nuance for construction.
 
In high level, dynamic languages, constructing an object refers to
aggregation.
 
The ancient function CONS in Lisp may hgave been the first one to use
the construction terminology. If we make a CONS which contains two
strings, there is not construction of those strings going on; they are
already constructed:
 
(cons "abc" "def")
 
Juha is probably talking about C++ style construction where if we have
struct foo { string s; foo(); }, the foo::foo constructor has to
call the string::string constructor.
 
Juha's remarks are true when objects are put together using composition
rather than aggregation.
 
Because the object B is embedded in another A, and must be initialized,
then the type-B-specific initialization logic for that embedded object
has to be invoked to initialize that B-typed portion of A.
 
From an abstraction point of view, it's really a shit design. But the
motivation is solid: there are certain efficiencies you can gain when
you compose rather than aggregate objects. You save space by not
allocating separate objects, reducing allocator overhead, and by not
allocating a pointer to navigate from one to the other. By not chasing a
pointer, you reduce pipeline stalls due to dependent loads. Low-level,
systems programming languages benefit from being able to compose
objects.
 
It's not worth trying to shoehorn that paradigm into a high level
language though. Composition of objects works for simple, low-level code
that has to run fast, and requires only a simple language.
 
If your idea for building a web browser or word processor includes
anything like this:
 
class Document {
std::string documentName;
 
//
}
 
due to the immense benefit of not having to chase an extra pointer
or allocate an extra object, you must be a supreme moron, and you will
have your lunch eaten by someone working in Javascript or Python.
 
> about how difficult is to keep the accounting of memory pieces right.
 
> And they are right but refuse to see the right tool for theirpurpose:
> just use a garbage collector and BE DONE WITH IT.
 
That should be: use a language with a garbage collector and be done
with it.
 
Statistically/probabilistically speaking, given that you have a genuine
reason to be using C, then conditional probability is high that garbage
collection may not be a good fit for the problem.
 
 
> Use an automatic garbage collector and BE DONE WITH IT. And please, do
> not start with performance problems, in most string applications the gc
> is completely transparent.
 
GC has good performance. But not in a small space. GC needs more memory
than manual freeing in order to perform well.
 
Most of the speed (in terms of raw throughput) improvements in GC
revolve around the theme of allowing garbage to linger around longer, so
that garbage cycles are either less frequent (e.g. tuning a larger
heap), or smaller in scope (generational GC, visiting just a young
subset of the object graph in most GC passes).
 
Allowing garbage to stick around longer translates to one thing: larger
memory footprints.
 
Adding GC to a simple, embedded application in which manual memory
management can be done correctly with relative ease will just bloat
its footprint. If the footprint is kept very tight, GC will run often
which could degrade the performance.
 
In a single-process system with physical memory, you can just
do collection whenever memory is nearly exausted, which is optimal.
The memory is needed for nothing else.
 
In a VM system that is using all of its free memory for a buffer/file
cache, any extra slack space for GC encroaches on that cache, even if no
other applications are running. Which they usually are. VM footprints
matter.
 
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Richard Damon <Richard@Damon-Family.org>: Apr 20 02:46PM -0400

On 4/20/21 1:44 PM, jacobnavia wrote:
 
> Use an automatic garbage collector and BE DONE WITH IT. And please, do
> not start with performance problems, in most string applications the gc
> is completely transparent.
 
But that only works for a fungible resource like memory. If you really
need to release the resource right away (like a lock on something) you
really want RAII.
jacobnavia <jacob@jacob.remcomp.fr>: Apr 20 08:47PM +0200

Le 20/04/2021 à 20:30, Kaz Kylheku a écrit :
> GC has good performance. But not in a small space. GC needs more memory
> than manual freeing in order to perform well.
 
Note: malloc/free do garbage collection by consolidating free memory
blocks into larger ones. This essential FACT is being overlooked here.
 
But granted, in small embedded applications with less CPU power and less
RAM GC is not the ideal solution and you do the accounting by hand. But
in those environments, C++ is anyway out of the question!
 
> that garbage cycles are either less frequent (e.g. tuning a larger
> heap), or smaller in scope (generational GC, visiting just a young
> subset of the object graph in most GC passes).
 
See above. In small spaces GC is not that good. But we were talking
about a TEXT EDITOR for learning C, so that is probably a PC environment
where GC SHINES!
 
> Allowing garbage to stick around longer translates to one thing: larger
> memory footprints.
 
Yes. But you can obviate that by calling the GC explicitely from time to
time.
 
> management can be done correctly with relative ease will just bloat
> its footprint. If the footprint is kept very tight, GC will run often
> which could degrade the performance.
 
Agreed. As I said above, for simple, embedded applications the GC is not
the best solution.
 
> cache, any extra slack space for GC encroaches on that cache, even if no
> other applications are running. Which they usually are. VM footprints
> matter.
 
There are MANY other solutions in C. You can do pool allocating, for
instance, allocating all the related strings from a pool that is freed
in a single instruction. ETC.
Kaz Kylheku <563-365-8930@kylheku.com>: Apr 20 08:18PM

["Followup-To:" header set to comp.lang.c.]
 
> But that only works for a fungible resource like memory. If you really
> need to release the resource right away (like a lock on something) you
> really want RAII.
 
RAII can be entirely separated from memory allocation. RAII can unlock
mutexes and close handles without invalidating memory.
 
I implemented a form of RAII in TXR Lisp.
 
This is the TXR Lisp interactive listener of TXR 256.
Quit with :quit or Ctrl-D on an empty line. Ctrl-X ? for cheatsheet.
If unwanted side effects persist, discontinue imperative programming.
1> (defstruct obj ()
(:init (me) (put-line `@me constructed`))
(:fini (me) (put-line `@me finalized`)))
#<struct-type obj>
2> (with-objects ((x (new obj)))
(put-line "inside with-objects"))
#S(obj) constructed
inside with-objects
#S(obj) finalized
t
 
The object has not gone away; just its finalizer was prematurely
invoked. When the object becomes garbage, that finalizer will not be
called any more; it is no longer registered for finalization.
 
If an exception goes off while an object is being initialized,
a similar thing happens:
 
TXR's no-spray organic production means every bug is carefully removed by hand.
1> (defstruct obj ()
(:init (me)
(put-line `@me constructed ... almost`)
(throw 'error))
(:fini (me) (put-line `@me finalized`)))
#<struct-type obj>
2> (new obj)
#S(obj) constructed ... almost
#S(obj) finalized
** exception args: nil
** during evaluation at expr-1:4 of form (throw 'error)
** run with --backtrace to enable backtraces
 
Struct objects have multiple finalizers at different levels of the
inheritance hierarchy; these get called in well-defined, documented
orders.
 
You can invoke an object's finalizers directly at any time with
(call-finalizers obj). Using that, and unwind-protect, you can
build your own scoping macros.
 
For something like acquiring and releasing a lock, we wouldn't
be constructing an object.
 
This is the problem in C++: RAII is strictly based on constructing and
destroying something, and always requires an object, and that must be a
class.
 
TXR Lisp offers a standard macro (based on unwind-protect under the hood)
for managing resources that can be allocated with expressions that
produce a value, and then disposed of by passing that value to another
expression. The value can be anything:
 
Use TXR only as directed. Unless you are intelligent, curious and creative.
1> (defun make-widget (arg)
(put-line `make-widget: @arg`)
arg)
make-widget
2> (defun break-widget (arg)
(put-line `break-widget: @arg`))
break-widget
3> (with-resources ((x (make-widget 42) (break-widget x))
(y (make-widget 'foo) (break-widget y)))
(put-line "inside with-resources"))
make-widget: 42
make-widget: foo
inside with-resources
break-widget: foo
break-widget: 42
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Apr 20 01:19PM -0700

On 4/20/2021 10:44 AM, jacobnavia wrote:
 
> No.
 
> Just have a constructotr and never free anything. Then link with Boehm's
> GARBAGE COLLECTOR and be done with it!
 
Wow! Really? Did you know that manual memory management can actually
help a GC'ed environment run more efficiently? I have had to deal with
questions about performance issues wrt using a garbage collector to act
as a memory reclamation scheme for lock-free algorithms for almost two
decades. GC can become a rather nasty, unpredictable, bottleneck.
 
 
Kaz Kylheku <563-365-8930@kylheku.com>: Apr 20 08:29PM

> questions about performance issues wrt using a garbage collector to act
> as a memory reclamation scheme for lock-free algorithms for almost two
> decades. GC can become a rather nasty, unpredictable, bottleneck.
 
Snip of a recent commit of mine:
 
commit 174182a29c785493ed41231caff36f35c56819cf
Author: Kaz Kylheku <kaz@kylheku.com>
Date: Tue Apr 6 20:06:18 2021 -0700
 
gc: fix astonishing bug in weak hash processing.
 
This is a flaw that has been in the code since the initial
implementation in 2009.
 
:)
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Apr 20 01:29PM -0700

On 4/20/2021 11:47 AM, jacobnavia wrote:
>> than manual freeing in order to perform well.
 
> Note: malloc/free do garbage collection by consolidating free memory
> blocks into larger ones. This essential FACT is being overlooked here.
 
consolidating smaller blocks into larger ones has nothing to do with the
conventional notion of a garbage collector. So, you saying that
malloc/free do GC is very odd to me, indeed.
 
 
 
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Apr 20 01:39PM -0700

On 4/20/2021 1:29 PM, Kaz Kylheku wrote:
 
> This is a flaw that has been in the code since the initial
> implementation in 2009.
 
> :)
 
Wow! I remember somebody asking me why the GC is under so much pressure.
They showed me their code where most of the allocations were coming
from. Of course they were allocating a new node for every push into a
lock-free stack! I said the simplest thing I could think of, just to see
if it made a difference... It was: Create a LIFO node cache, and create
a layered approach for allocating nodes. Try the cache first, then
resort to allocating a new node for now. The response was well, then we
have to use manual memory management for the node cache. I said, indeed
you do! So, they did it. Guess what? They were able to increase
performance by orders of magnitude! The GC had a lot less pressure on
it. Iirc, this was in Java. All due to good ol' manual memory management
even in a GC system.
Ian Collins <ian-news@hotmail.com>: Apr 21 08:56AM +1200

On 21/04/2021 05:44, jacobnavia wrote:
 
> No.
 
> Just have a constructotr and never free anything. Then link with Boehm's
> GARBAGE COLLECTOR and be done with it!
 
Let's not forget that memory isn't the only resource that a program has
to manage.
 
--
Ian.
olcott <NoOne@NoWhere.com>: Apr 19 11:20PM -0500

The set of Turing machine's that would not halt on their input is
identical to the set of Turing machines that must have their simulation
aborted to prevent their otherwise infinite execution because:
 
(a) Every input having infinite execution would have to have its
simulation aborted to prevent its otherwise infinite execution.
 
(b) Every input not having infinite execution not need to have its
simulation aborted to prevent infinite execution.
 
Thus the set of inputs that must be aborted to prevent their infinite
execution is the exact same set of inputs that have infinite execution
and their complementary set is the set of halting computations.
 
--
Copyright 2021 Pete Olcott
 
"Great spirits have always encountered violent opposition from mediocre
minds." Einstein
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: