21

I'm currently writing a callback function in C:

static size_t writedata(void *ptr, size_t size, size_t nmemb, void *stream){

        size_t written = fwrite(ptr, size, nmemb, (FILE)*stream);
        return written;
}

This function is going to be used in another function, which does a HTTP request, retrieves the request, and writes it to the local machine. That writedata function will be used for the later part. The whole operation has to be multithreaded, so I was in doubt between write and fwrite. Could someone help me outlining the differences between write() and fwrite() in C, so I could choose which one best fits into my problem?

Despertar
  • 21,627
  • 11
  • 81
  • 79
cybertextron
  • 10,547
  • 28
  • 104
  • 208

3 Answers3

28

fwrite writes to a FILE*, i.e. a (potentially) buffered stdio stream. It's specified by the ISO C standard. Additionally, on POSIX systems, fwrite is thread-safe to a certain degree.

write is a lower-level API based on file descriptors, described in the POSIX standard. It doesn't know about buffering. If you want to use it on a FILE*, then fetch its file descriptor with fileno, but be sure to manually lock and flush the stream before attempting a write.

Use fwrite unless you know what you're doing.

Community
  • 1
  • 1
Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • 3
    One thing to note, though, is that you *probably* shouldn't access a FILE* structure (which includes, but is not limited to, calling fread/fwrite) from multiple threads, whereas it's generally safer to do so with low-level file descriptors... but still not a good idea without some synchronization :) – snemarch Jul 10 '12 at 13:32
  • @fred-foo I am curious what is going to happen if I call write() multiple times with 1 thread without locking nor flushing. Given fwrite() is thread safe while write() not, I would imagine that write() will be faster than fwrite() when writing to a ramdrive. Tho, my test indicates otherwise -- about 20x slower. Any idea? – Hei Mar 13 '18 at 13:25
  • `write` actually is thread safe. The difference in performance is probably from the buffering: `fwrite` will make fewer calls to `write` and thus reduce system call overhead. Note that many implementations will optimize out the locking of `fwrite` in a program that only has one thread. – Nate Eldredge Oct 16 '21 at 18:35
6

One very noticeable difference is write is atomic and fwrite isn't.

https://yarchive.net/comp/linux/wakekill.html

Using:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main() {
  if (fork() == 0) {
    FILE* h = fopen("file.txt", "a");
    char* line =
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n";
    for (int i = 0; i < 10000; i++) {
      if (write(fileno(h), line, strlen(line)) != strlen(line)) {
        perror("Could not append line to file");
        exit(1);
      }
    }
    if (fclose(h) != 0) {
      perror("Could not close file");
      exit(1);
    }
  } else {
    FILE* h = fopen("file.txt", "a");
    char* line =
        "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n";
    for (int i = 0; i < 10000; i++) {
      if (write(fileno(h), line, strlen(line)) != strlen(line)) {
        perror("Could not append line to file");
        exit(1);
      }
    }
    if (fclose(h) != 0) {
      perror("Could not close file");
      exit(1);
    }
  }
  return 0;
}

You will get output like this, no interleaving lines:

bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb

Using:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main() {
  if (fork() == 0) {
    FILE* h = fopen("file.txt", "a");
    char* line =
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n";
    for (int i = 0; i < 10000; i++) {
      if (fwrite(line, 1, strlen(line), h) != strlen(line)) {
        perror("Could not append line to file");
        exit(1);
      }
    }
    if (fclose(h) != 0) {
      perror("Could not close file");
      exit(1);
    }
  } else {
    FILE* h = fopen("file.txt", "a");
    char* line =
        "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n";
    for (int i = 0; i < 10000; i++) {
      if (fwrite(line, 1, strlen(line), h) != strlen(line)) {
        perror("Could not append line to file");
        exit(1);
      }
    }
    if (fclose(h) != 0) {
      perror("Could not close file");
      exit(1);
    }
  }
  return 0;
}

You will get output like this, interleaving lines:

bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
ericcurtin
  • 1,499
  • 17
  • 20
0

The write function is a call that your program makes to the operating system and therefore it is slower than fwrite. It also lacks of buffering which makes it even slower because as the philosophy of buffering suggests: "It is faster to handle many portions of small files rather than a big one." It is also important that write is not part of the c standard, so you probably won't find it non-POSIX systems and (rarely) the apropriate use will differ. You should also known that fwrite and fread are some times implemented using write and read (a simple implementation can be found in the chapter about Unix of K&R).

Another noteable thing is that read and write use file descriptors, but fread and fwrite use the FILE pointers which are actually pointers which contain file descriptors and other info about the file opened.

  • 7
    Lacking buffering doesn't make it slower exactly - if you're writing fixed size memory chunks (or managing your own buffering already) for eg, buffering may give unnecessary overhead. It's better to say, using `fwrite` is preferable if you would benefit from buffered writing. – ideasman42 Apr 14 '18 at 11:49