- No, C is not a simple language (PLO) - 24 Updates
- Equivalent halting definitions - 1 Update
| "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:
Post a Comment