fwiw, i've never used calloc.  if i really need to memset something to 0
then i do it explicitly after the allocation.

why?  the reason has nothing to do with the maintainability arguments
being presented here, and has everything to do with performance.

study the following code and assembly.

hint: sizeof(struct foo) is a *compile-time* constant.  notice how when
the compiler knows it's dealing with a compile-time constant it emits code
that is as efficient (and sometimes more efficient than) setting all the
structure elements explicitly.

notice how large the memset definition is.  i don't know where the
break-even is as far as when it's better to call this big function. it
varies from processor to processor, and the compiler has heuristics to
know when it should out-of-line a memset (with compile-time constant)
versus when it should generate efficient code.

it has no choice but to do a full expensive memset when the size field
isn't known at compile-time.  i.e. when calloc is used.

-dean


% cat calloc.c
#include <stdlib.h>

struct foo {
        struct foo *next;
        char *blah;
        void *whatever;
};

struct foo *new_foo1(void)
{
        struct foo *p;

        p = malloc(sizeof(struct foo));
        memset(p, 0, sizeof(struct foo));
        return p;
}

struct foo *new_foo2(void)
{
        struct foo *p;

        p = malloc(sizeof(struct foo));
        p->next = 0;
        p->blah = 0;
        p->whatever = 0;
        return p;
}

/* this function is here only to pull in the memset definition
 * so that i can disassemble it for you.
 */
void pull_in_memset(void *p, int c, size_t size)
{
        memset(p, c, size);
}

int main(int argc, char **argv)
{
        return 0;
}
% gcc -O calloc.c
% gdb a.out
GNU gdb 19991004
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb) b main
Breakpoint 1 at 0x804844f
(gdb) run
Starting program: /home/dean/oneshot/a.out

Breakpoint 1, 0x804844f in main ()
(gdb) disass new_foo1
Dump of assembler code for function new_foo1:
0x80483f8 <new_foo1>:   push   %ebp
0x80483f9 <new_foo1+1>: mov    %esp,%ebp
0x80483fb <new_foo1+3>: push   $0xc
0x80483fd <new_foo1+5>: call   0x8048310 <malloc>
0x8048402 <new_foo1+10>:        movl   $0x0,(%eax)
0x8048408 <new_foo1+16>:        movl   $0x0,0x4(%eax)
0x804840f <new_foo1+23>:        movl   $0x0,0x8(%eax)
0x8048416 <new_foo1+30>:        leave
0x8048417 <new_foo1+31>:        ret
End of assembler dump.
(gdb) disass new_foo2
Dump of assembler code for function new_foo2:
0x8048418 <new_foo2>:   push   %ebp
0x8048419 <new_foo2+1>: mov    %esp,%ebp
0x804841b <new_foo2+3>: push   $0xc
0x804841d <new_foo2+5>: call   0x8048310 <malloc>
0x8048422 <new_foo2+10>:        movl   $0x0,(%eax)
0x8048428 <new_foo2+16>:        movl   $0x0,0x4(%eax)
0x804842f <new_foo2+23>:        movl   $0x0,0x8(%eax)
0x8048436 <new_foo2+30>:        leave
0x8048437 <new_foo2+31>:        ret
End of assembler dump.
(gdb) disass memset
Dump of assembler code for function memset:
0x189fec <memset>:      push   %ebp
0x189fed <memset+1>:    mov    %esp,%ebp
0x189fef <memset+3>:    push   %edi
0x189ff0 <memset+4>:    push   %esi
0x189ff1 <memset+5>:    mov    0x10(%ebp),%esi
0x189ff4 <memset+8>:    mov    0x8(%ebp),%edi
0x189ff7 <memset+11>:   movzbl 0xc(%ebp),%eax
0x189ffb <memset+15>:   cld
0x189ffc <memset+16>:   cmp    $0xb,%esi
0x189fff <memset+19>:   jbe    0x18a024 <memset+56>
0x18a001 <memset+21>:   mov    %eax,%edx
0x18a003 <memset+23>:   shl    $0x8,%edx
0x18a006 <memset+26>:   or     %edx,%eax
0x18a008 <memset+28>:   mov    %eax,%edx
0x18a00a <memset+30>:   shl    $0x10,%edx
0x18a00d <memset+33>:   or     %edx,%eax
0x18a00f <memset+35>:   mov    %edi,%ecx
0x18a011 <memset+37>:   neg    %ecx
0x18a013 <memset+39>:   and    $0x3,%ecx
0x18a016 <memset+42>:   sub    %ecx,%esi
0x18a018 <memset+44>:   repz stos %al,%es:(%edi)
0x18a01a <memset+46>:   mov    %esi,%ecx
0x18a01c <memset+48>:   shr    $0x2,%ecx
0x18a01f <memset+51>:   repz stos %eax,%es:(%edi)
0x18a021 <memset+53>:   and    $0x3,%esi
0x18a024 <memset+56>:   mov    %esi,%ecx
0x18a026 <memset+58>:   repz stos %al,%es:(%edi)
0x18a028 <memset+60>:   mov    0x8(%ebp),%eax
0x18a02b <memset+63>:   lea    0xfffffff8(%ebp),%esp
0x18a02e <memset+66>:   pop    %esi
0x18a02f <memset+67>:   pop    %edi
0x18a030 <memset+68>:   leave
0x18a031 <memset+69>:   ret
End of assembler dump.
(gdb)

Reply via email to