Experiment: functional concepts in C

  • Thread starter Ertugrul Söylemez
  • Start date
C

Chris M. Thomasson

jacob navia said:
Ertugrul Söylemez a écrit :

Well, OBVIOUSLY in operating system's code it would be a VERY bad idea
to rely on the OS to do the cleanup isn't it?

Please try to see things in context. Nobody is advocating forgetting to
cleanup when cleanup is necessary. We are talking of the useless cleanup
that is done before exiting the program...

What about forgetting to close and/or unlink a named semaphore? I would
argue that some types of resources should be properly cleaned up before
exiting the program.
 
C

Chris M. Thomasson

[...]
On my system it's pretty fast, but I redeclare malloc/free:

int block_pos = 0;
char block[HUGE];

void* malloc(int size)
{
void* p = block + block_pos;
block_pos += size;
return p;
}

void free( void *)
{
}


A very simple region allocator. Unfortunately, you are not aligning things
properly. Perhaps you should do something like:
________________________________________________________
#include <assert.h>
#include <stddef.h>


#define BLOCK_SIZE 8192U
#define DOUBLE_PER_BLOCK (BLOCK_SIZE / sizeof(double))


static double g_buffer[BLOCK_SIZE / sizeof(double)] = { 0 };
static size_t g_head = 0;


static void*
malloc(size_t size)
{
size_t align_size = (size + sizeof(double) - 1) & -sizeof(double);
size_t blocks = align_size / sizeof(double);
size_t prev_head = g_head;
size_t next_head = g_head + blocks;

if (next_head * sizeof(double) > BLOCK_SIZE)
{
return NULL;
}

g_head = next_head;

return g_buffer + prev_head;
}


static void
free(void* p)
{
double* block = p;

assert(block >= g_buffer &&
g_buffer < g_buffer + DOUBLE_PER_BLOCK);
}


static void
free_region(void* p)
{
double* block = p;

assert(block >= g_buffer &&
g_buffer < g_buffer + DOUBLE_PER_BLOCK);

g_head = block - g_buffer;
}


static void
free_all(void)
{
g_head = 0;
}




struct foo
{
short x;
int y;
char z[3];
};


int
main(void)
{
size_t i;

for (i = 0; i < 8192; ++i)
{
struct foo* f = malloc(sizeof(*f));
if (! f) break;
}

return 0;
}
________________________________________________________




This is still not portable because it assumes the alignment for a double is
sufficient for any type.
 
C

Chris M. Thomasson

Branimir Maksimovic said:
Dimiter said:
On my system it's pretty fast, but I redeclare malloc/free:

int block_pos = 0;
char block[HUGE];

void* malloc(int size)
{
void* p = block + block_pos;
block_pos += size;
return p;
}

void free( void *)
{
}
well you have to align...

void* malloc(int size)
{
/* eg size = (size + 0xf) & ~0xf; */
void* p = block + block_pos;
block_pos += size;
return p;
}

AFAICT, that is still not good enough because the head of the `block' array
is aligned for `char'.
 
C

Chris M. Thomasson

[...]
A very simple region allocator. Unfortunately, you are not aligning things
properly. Perhaps you should do something like:
________________________________________________________
#include <assert.h>
#include <stddef.h>


#define BLOCK_SIZE 8192U
#define DOUBLE_PER_BLOCK (BLOCK_SIZE / sizeof(double))

I forgot to mention that `BLOCK_SIZE' should be evenly divisible by
`sizeof(double)'.
 
E

Ertugrul Söylemez

Dimiter \"malkia\" Stanev said:
Are you sure? So none of the garbage-collecting languages are
pragmatic for that matter?

It's the code that needs to be pragmatic with respect to language
semantics. If the language offers a garbage collector, the semantics
change. But C has no GC, so resource management is left to the
programmer entirely.


Greets
Ertugrul
 
E

Ertugrul Söylemez

Incidentally, do those who believe programs should free() all
malloc()ed memory always catch interrupt signals in case the user
kills the program leaving memory allocated? It would be intolerable
if interrupting a program caused a memory leak...

Presumably they also believe that programs should always return to
main() rather than calling exit(), abort(), or assert(), in case the
operating system does not reclaim the stack. (Though they probably
have to do this anyway for malloc()ed memory.)

This has nothing to do with belief, but with code correctness. An
increasing number of programmers agree that following standards is the
right thing to do. I do catch interrupt signals, if I believe that I
should, e.g. when exiting prematurely may cause an inconsistent state of
something nonvolatile.

In C I might call exit() or abort(), but that's because C has no control
constructs (like exceptions) to encode early exiting in a reasonable
manner. IMO it's incorrect, but returning to the top level may make my
code uglier and less maintainble in C. That's a language problem.

Anyway, nobody should attack others for their programming habits. I
think that freeing resources is a good thing and leads to better code.
My benchmark has proven that the performance impact will be neglible for
virtually all applications, even if they allocate millions of buffers.
So if I happen to write C code, I will free my memory, even just before
program exit.

If you don't do that, it's your business.


Greets
Ertugrul
 
E

Ertugrul Söylemez

Branimir Maksimovic said:
Ertugrul said:
Feel free to pay me for giving Haskell lessons.

makeWords' :: [String] -> Ptr CString -> IO ()
makeWords' [] p = do poke p nullPtr
makeWords' (s:strs) p = do poke p $ unsafePerformIO $ newCString s
makeWords' strs (plusPtr p $ sizeOf p)
;))))

Greets

You should read this section:
http://ertes.de/articles/monads.html#section-9

And here is my version of that function:

makeWords :: [String] -> ByteString
makeWords = B.concat . map B.pack

;)


Greets
Ertugrul
 
J

jacob navia

Chris M. Thomasson a écrit :
What about forgetting to close and/or unlink a named semaphore? I would
argue that some types of resources should be properly cleaned up before
exiting the program.

This kind of resource can't be cleaned up by the system in
most cases, so that should be done. I hope that you aren't
read impaired but in the text you posted I said,
(and I repeated AGAIN so that it is crystal clear):

This could include (but is not limited to)
o named semaphores
o named pipes
o Shared memory

ETC!

We are speaking all the time about unneeded CALLS TO MALLOC before the
program exits. I repeated that you shuld use THAT context only.

Why you ignore that?

It is not possible to discuss rationally without pissing contexts?
 
I

Ike Naar

We are speaking all the time about unneeded CALLS TO MALLOC before the
program exits. I repeated that you shuld use THAT context only.

So you use "CALLS TO MALLOC" to clean up memory?
 
E

Ertugrul Söylemez

jacob navia said:
Richard Heathfield a écrit :

Well, I find programs loaded with redundant code much more inelegant.
MY criteria for elegance is small size, speed, doing the maximum with
the minimal set of code that fulfills the task at hand.

I have spent hours DELETING code from lcc-win or the IDE or the
debugger, trying to reduce the size of the program, an activity that
is not done in today's environments. "Cleaning up" means (for me)
reorganizing the code so that has LESS statements, factoring commonly
used pieces of code into routines, eliminating unneeded features, etc.

Keeping redundant code in my program is (for me) a big mistake. The
less code a program has, the easier is it for anybody to understand
it.

I avoid as much as possible to recode routines already done by the OS,
for instance, I use as much as possible Windows routines for directory
management instead of writing "portable" ones. Under Unix, I use what
the OS offers for the same tasks to keep my code SMALL.

I think you're confusing two related concepts here, namely elegance and
conciseness. They are related in that elegant code is usually also
concise, but the contrary isn't true.

The net result is that my programs are incredibly SMALL. For instance,
I have packed a debugger, a project management utility, an editor,
grep, diff, and many other utilities into a 800K program (32 bits). In
many other languages a "Hello world" program is already that big...

The lcc-win compiler is 651 808 bytes long, including an assembler, an
optimizer, and the preprocessor. GNU's "cc1" is 2 623 488 and probably
doesn't even include the assembler.

Obviously we have different meanings for "elegance" here.

Well, you may have overslept the technological advances of the last two
decades, but note that we measure RAM in GiB today. Hard disk space is
measured in TiB for many people already, so your code restructuring
wasn't great or elegant, but rather a waste of time converting correct,
safe code into smaller, incorrect code to do just the same thing.

I'm sure your IDE/compiler suite isn't going to run on small embedded
systems, so you may want to reconsider your priorities. Nobody will
complain about your program, if it has 3 MiB instead of 800 KiB. After
all modern IDEs take hundreds of megabytes of disk space and nobody
complains about them either.


Greets
Ertugrul
 
J

jacob navia

Ike Naar a écrit :
So you use "CALLS TO MALLOC" to clean up memory?


fine

very clever

I thought free and wrote malloc. It is obvious what I wanted to say isn't it?
 
C

Chris M. Thomasson

jacob navia said:
Chris M. Thomasson a écrit :

This kind of resource can't be cleaned up by the system in
most cases, so that should be done. I hope that you aren't
read impaired but in the text you posted I said,
(and I repeated AGAIN so that it is crystal clear):


This could include (but is not limited to)
o named semaphores
o named pipes
o Shared memory

ETC!

We are speaking all the time about unneeded CALLS TO MALLOC before the
program exits. I repeated that you shuld use THAT context only.

Why you ignore that?

YIKES! I ignored it because I guess I totally missed it. I apologize for
that non-sense Jacob!

;^(...
 
J

jacob navia

Ertugrul Söylemez a écrit :
I think you're confusing two related concepts here, namely elegance and
conciseness. They are related in that elegant code is usually also
concise, but the contrary isn't true.



Well, you may have overslept the technological advances of the last two
decades, but note that we measure RAM in GiB today. Hard disk space is
measured in TiB for many people already, so your code restructuring
wasn't great or elegant, but rather a waste of time converting correct,
safe code into smaller, incorrect code to do just the same thing.

You are completely WRONG.

A small program has many advantages:

o It fits more program in the processor caches. As you (may) know
processor caches are much more limited than RAM. A smaller program
will run FASTER than a bloated one because costly RAM access will be
fewer.

o There is nothing that runs FASTER than a DELETED instruction. Yes,
sometimes you need to add code to make it run faster, but in most
cases eliminating unneeded operations accelerates the program.

o Your point of view is the one of most programers today. Do not care
about efficiency. That means that instead of passing the results of
hardware progress to the user and making programs that effectively
run FASTER in today's hardware we KEEP those advances for us programmers
and we run as slow as 20 years ago. Read about the contest between
a MAcintosh 512K and a Vista AMD64, and you will see that in many aspects
that are important to the end user, the Mac 512K run faster than vista.

Why?

Because of your mentality: BLOATED IS BETTER. Just do not use your brain and
program like a pig. Nobody cares.

o I presented an article about code bloat with the JAVA example in this group
several weeks ago. Re-read it. There I show why I like C and think that C has
a bright future.
I'm sure your IDE/compiler suite isn't going to run on small embedded
systems, so you may want to reconsider your priorities. Nobody will
complain about your program, if it has 3 MiB instead of 800 KiB.

Of course not, but they will complain about slow response times, and
long download time!
> After
all modern IDEs take hundreds of megabytes of disk space and nobody
complains about them either.

Yes. Take for instance Eclipse, that took around 5 minutes to start in
an AIX system. Yes, that's right: 5 minutes. Nice isn't it?

And yes, I did not complain. There was no point in complaining.
Java is like that.

Everybody in Java thinks like you:

PROGRAM LIKE A PIG!
Nobody cares.

Then, to read a 4 digit year from character into an integer you use
several temporary objects, by reading the characters into a list
container temp, that calls a general routine to convert a list
container into a boxed multi-precision integer that gives a boxed
integer that is converted into an unboxed one... eventually.


Obviously nobody cares until this is used in an inner loop and then...

Well then, the user waits, or they buy yet another server box.
 
J

jacob navia

Chris M. Thomasson a écrit :
YIKES! I ignored it because I guess I totally missed it. I apologize for
that non-sense Jacob!

;^(...

OK , and thanks for telling us that. It is nice to discuss with people
that tell when they misunderstood something, it makes the discussion
more relaxed, less aggressive.

jacob
 
K

Kaz Kylheku

So you use "CALLS TO MALLOC" to clean up memory?

By malloc, Jacob probably means the allocation facility.

Do you also think that calls to Washigton are to the city itself?
 
E

Ertugrul Söylemez

jacob navia said:
You are completely WRONG.

You could use some improvement in your reasoning. =)

A small program has many advantages:

[a lot of irrelevant crap]

I'm not coding like a pig. Cleaning up properly is not bloat, and
that's all I'm talking about.

However, even if I were talking about bloat, your reasoning is still
nonsense. You're programming in C after all. To get maximum speed, you
need to program in Assembler.

o It fits more program in the processor caches. As you (may) know
processor caches are much more limited than RAM. A smaller program
will run FASTER than a bloated one because costly RAM access will
be fewer.

So? Ah yes, I understand. A nanosecond slower response time, because I
freed memory unnecessarily. Laughable.

o There is nothing that runs FASTER than a DELETED instruction. Yes,
sometimes you need to add code to make it run faster, but in most
cases eliminating unneeded operations accelerates the program.

In most cases, this acceleration is neglible.

o Your point of view is the one of most programers today. Do not care
about efficiency. That means that instead of passing the results
of hardware progress to the user and making programs that
effectively run FASTER in today's hardware we KEEP those advances
for us programmers and we run as slow as 20 years ago. Read about
the contest between a MAcintosh 512K and a Vista AMD64, and you
will see that in many aspects that are important to the end user,
the Mac 512K run faster than vista.

Why?

Because of your mentality: BLOATED IS BETTER. Just do not use your
brain and program like a pig. Nobody cares.

Complete nonsense. You don't know my mentality, obviously. Neither do
you know the mentality of modern software industry. Instead of making
the same program with the same functionality faster, we make a better
program doing much more things in the same time.

I'm not saying that this is a good in any way. I'm saying that this is
how the majority of programmers think.

o I presented an article about code bloat with the JAVA example in
this group several weeks ago. Re-read it. There I show why I like C
and think that C has a bright future.

This is why you like C over Java. I don't like Java either, and I think
that neither of those two languages have a bright future. C is still
widely used, partly because the old school programmers, not willing to
learn/evaluate anything new, are still there and partly because a lot of
software is already written in C, so using C to extend them makes sense.

Of course not, but they will complain about slow response times, and
long download time!

Because you freed resources? ;)

Yes. Take for instance Eclipse, that took around 5 minutes to start in
an AIX system. Yes, that's right: 5 minutes. Nice isn't it?

And yes, I did not complain. There was no point in complaining.
Java is like that.

We are not arguing about 5 minutes startup times, but about 5
nanoseconds response times and 20 ms exit time.

Everybody in Java thinks like you:

PROGRAM LIKE A PIG!
Nobody cares.

I'm not like that.

Then, to read a 4 digit year from character into an integer you use
several temporary objects, by reading the characters into a list
container temp, that calls a general routine to convert a list
container into a boxed multi-precision integer that gives a boxed
integer that is converted into an unboxed one... eventually.

Obviously nobody cares until this is used in an inner loop and then...

Well then, the user waits, or they buy yet another server box.

That's complete nonsense and you know it.


Greets
Ertugrul
 
J

jacob navia

Ertugrul Söylemez a écrit :
That's complete nonsense and you know it.

That is a factual case study (by a researcher from IBM), and I gave
the article reference in that post about code bloat.

[1]
The Diary of a Datum: An Approach to Modeling Runtime Complexity in
Framework-Based Applications
Nick Mitchell IBM TJ Watson Research Center 19 Skyline Drive Hawthorne,
NY USA +1 914-784-7715
(e-mail address removed)
Gary Sevitsky IBM TJ Watson Research Center 19 Skyline Drive Hawthorne,
NY USA +1 914-784-7619
(e-mail address removed)
Harini Srinivasan
IBM Software Group Route 100 Somers, NY USA +1 914-766-1885
(e-mail address removed)

See the example presented by the authors, with the actual path
of the data in pages 2-4. I am not talking "complete nonsense"
here. I am just reading the literature of this field: BLOAT!

Other articles you can read before telling that is all
"nonsense":

[2]
The Causes of Bloat, The Limits of Health
Nick Mitchell Gary Sevitsky
IBM T.J. Watson Research Center 19 Skyline Drive Hawthorne, NY 10532 USA
{nickm,sevitsky}@us.ibm.com
[3]
A Scalable Technique for Characterizing the Usage of Temporaries in
Framework-intensive Java Applications
Bruno Dufour Dept of Computer Science Rutgers University
(e-mail address removed)
Barbara G. Ryder Dept of Computer Science Rutgers University
(e-mail address removed)
Gary Sevitsky IBM T.J. Watson Research Center (e-mail address removed)

The URL for this (and the other articles) is:
http://domino.research.ibm.com/comm/research_people.nsf/pages/nickmitchell.pubs.html
 
E

Ertugrul Söylemez

jacob navia said:
Ertugrul Söylemez a écrit :
That's complete nonsense and you know it.

That is a factual case study (by a researcher from IBM), and I gave
the article reference in that post about code bloat.

[...]

Other articles you can read before telling that is all
"nonsense":

[...]

Because you're talking about boxing and general routines, you're
obviously not talking about C. In other languages the compiler has much
more information available to use for optimization.

Note for example that in Haskell it is common to create an infinite
list, extend it to an infinite list of infinite lists, zip all sublists
with an infinite list of integers, map a function over all elements,
extract a finite portion, split it into sublists, etc. The compiler
translates all these data structures into variables and loops. The
lists go away entirely. The resulting code is often just as fast as the
equivalent (but much longer and much less readable) C code.

Yes, the code will be slower by a small factor, but that's not related
to the boxing and high level data structures, but to the mere fact that
the compiler is not that smart in the low level yet. The high level
optimizations seem more important to most people, because they make it
possible to code in that style saving lots of development time.


Greets
Ertugrul
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top