mirror of
https://github.com/adtools/clib2.git
synced 2025-12-08 14:59:05 +00:00
Optimizations for better write performance.
fwrite() now tries to fill the write buffer as far is possible and will only resort to using the __putc() macros when necessary. This should improve write performance by quite a bit. If the write buffer happens to be empty and the number of bytes to write is at least as large as the write buffer, then fwrite() will directly call write(). This should improve write performance, too. If the file is in unbuffered mode, fwrite() now always calls write(), bypassing the write buffer altogether.
This commit is contained in:
@ -1,10 +1,8 @@
|
||||
/*
|
||||
* $Id: stdio_fwrite.c,v 1.12 2010-10-20 13:12:58 obarthel Exp $
|
||||
*
|
||||
* :ts=4
|
||||
*
|
||||
* Portable ISO 'C' (1994) runtime library for the Amiga computer
|
||||
* Copyright (c) 2002-2015 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* Copyright (c) 2002-2019 by Olaf Barthel <obarthel (at) gmx.net>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -78,7 +76,6 @@ fwrite(const void *ptr,size_t element_size,size_t count,FILE *stream)
|
||||
|
||||
assert( __is_valid_iob(file) );
|
||||
assert( FLAG_IS_SET(file->iob_Flags,IOBF_IN_USE) );
|
||||
assert( file->iob_BufferSize > 0 );
|
||||
|
||||
if(FLAG_IS_CLEAR(file->iob_Flags,IOBF_IN_USE))
|
||||
{
|
||||
@ -102,9 +99,11 @@ fwrite(const void *ptr,size_t element_size,size_t count,FILE *stream)
|
||||
goto out;
|
||||
}
|
||||
|
||||
clearerr((FILE *)file);
|
||||
|
||||
if(element_size > 0 && count > 0)
|
||||
{
|
||||
const unsigned char * data = (unsigned char *)ptr;
|
||||
const unsigned char * s = (unsigned char *)ptr;
|
||||
unsigned char c;
|
||||
int buffer_mode;
|
||||
size_t total_bytes_written = 0;
|
||||
@ -115,6 +114,9 @@ fwrite(const void *ptr,size_t element_size,size_t count,FILE *stream)
|
||||
if(__fputc_check((FILE *)file) < 0)
|
||||
goto out;
|
||||
|
||||
/* If this is an unbuffered interactiv stream, we will switch
|
||||
to line buffered mode in order to improve readability of
|
||||
the output. */
|
||||
buffer_mode = (file->iob_Flags & IOBF_BUFFER_MODE);
|
||||
if(buffer_mode == IOBF_BUFFER_MODE_NONE)
|
||||
{
|
||||
@ -128,43 +130,166 @@ fwrite(const void *ptr,size_t element_size,size_t count,FILE *stream)
|
||||
__fd_unlock(fd);
|
||||
}
|
||||
|
||||
if(buffer_mode == IOBF_BUFFER_MODE_LINE)
|
||||
if (buffer_mode == IOBF_BUFFER_MODE_LINE)
|
||||
{
|
||||
while(total_size-- > 0)
|
||||
assert( file->iob_BufferSize > 0 );
|
||||
|
||||
while(total_size > 0)
|
||||
{
|
||||
c = (*data++);
|
||||
/* Is there still room in the write buffer to store
|
||||
more of the data? */
|
||||
if(file->iob_BufferWriteBytes < file->iob_BufferSize)
|
||||
{
|
||||
unsigned char * buffer = &file->iob_Buffer[file->iob_BufferWriteBytes];
|
||||
size_t num_buffer_bytes;
|
||||
const unsigned char * lf;
|
||||
|
||||
/* Store only as many characters as will fit into the write buffer. */
|
||||
num_buffer_bytes = file->iob_BufferSize - file->iob_BufferWriteBytes;
|
||||
if(total_size < num_buffer_bytes)
|
||||
num_buffer_bytes = total_size;
|
||||
|
||||
/* Try to find a line feed in the string. If there is one,
|
||||
reduce the number of characters to write to the sequence
|
||||
which ends with the line feed character. */
|
||||
lf = (unsigned char *)memchr(s, '\n', num_buffer_bytes);
|
||||
if(lf != NULL)
|
||||
num_buffer_bytes = lf + 1 - s;
|
||||
|
||||
memmove(buffer, s, num_buffer_bytes);
|
||||
s += num_buffer_bytes;
|
||||
|
||||
file->iob_BufferWriteBytes += num_buffer_bytes;
|
||||
|
||||
/* Write the buffer to disk if it's full or contains a line feed. */
|
||||
if((lf != NULL || __iob_write_buffer_is_full(file)) && __flush_iob_write_buffer(file) < 0)
|
||||
{
|
||||
/* Abort with error. */
|
||||
break;
|
||||
}
|
||||
|
||||
total_bytes_written += num_buffer_bytes;
|
||||
|
||||
/* Stop as soon as no further data needs to be written. */
|
||||
total_size -= num_buffer_bytes;
|
||||
if(total_size == 0)
|
||||
break;
|
||||
|
||||
/* If there is again room in the output buffer,
|
||||
repeat this optimization. */
|
||||
if(file->iob_BufferWriteBytes < file->iob_BufferSize)
|
||||
continue;
|
||||
}
|
||||
|
||||
c = (*s++);
|
||||
|
||||
if(__putc_line_buffered(c,(FILE *)file) == EOF)
|
||||
goto out;
|
||||
break;
|
||||
|
||||
total_size--;
|
||||
total_bytes_written++;
|
||||
}
|
||||
}
|
||||
else if (buffer_mode == IOBF_BUFFER_MODE_NONE)
|
||||
{
|
||||
ssize_t num_bytes_written;
|
||||
|
||||
/* We bypass the buffer entirely. */
|
||||
num_bytes_written = write(file->iob_Descriptor, s, total_size);
|
||||
if(num_bytes_written == -1)
|
||||
{
|
||||
SET_FLAG(file->iob_Flags,IOBF_ERROR);
|
||||
goto out;
|
||||
}
|
||||
|
||||
total_bytes_written = (size_t)num_bytes_written;
|
||||
}
|
||||
else
|
||||
{
|
||||
while(total_size-- > 0)
|
||||
assert( file->iob_BufferSize > 0 );
|
||||
|
||||
while(total_size > 0)
|
||||
{
|
||||
c = (*data++);
|
||||
/* If there is more data to be written than the write buffer will hold
|
||||
and the write buffer is empty anyway, then we'll bypass the write
|
||||
buffer entirely. */
|
||||
if(file->iob_BufferWriteBytes == 0 && total_size >= (size_t)file->iob_BufferSize)
|
||||
{
|
||||
ssize_t num_bytes_written;
|
||||
|
||||
/* We bypass the buffer entirely. */
|
||||
num_bytes_written = write(file->iob_Descriptor, s, total_size);
|
||||
if(num_bytes_written == -1)
|
||||
{
|
||||
SET_FLAG(file->iob_Flags,IOBF_ERROR);
|
||||
goto out;
|
||||
}
|
||||
|
||||
total_bytes_written += num_bytes_written;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Is there still room in the write buffer to store
|
||||
more of the data? */
|
||||
if(file->iob_BufferWriteBytes < file->iob_BufferSize)
|
||||
{
|
||||
unsigned char * buffer = &file->iob_Buffer[file->iob_BufferWriteBytes];
|
||||
size_t num_buffer_bytes;
|
||||
|
||||
/* Store only as many bytes as will fit into the write buffer. */
|
||||
num_buffer_bytes = file->iob_BufferSize - file->iob_BufferWriteBytes;
|
||||
if(total_size < num_buffer_bytes)
|
||||
num_buffer_bytes = total_size;
|
||||
|
||||
memmove(buffer, s, num_buffer_bytes);
|
||||
s += num_buffer_bytes;
|
||||
|
||||
file->iob_BufferWriteBytes += num_buffer_bytes;
|
||||
|
||||
/* Write a full buffer to disk. */
|
||||
if(__iob_write_buffer_is_full(file) && __flush_iob_write_buffer(file) < 0)
|
||||
{
|
||||
/* Abort with error. */
|
||||
break;
|
||||
}
|
||||
|
||||
total_bytes_written += num_buffer_bytes;
|
||||
|
||||
/* Stop as soon as no further data needs to be written. */
|
||||
total_size -= num_buffer_bytes;
|
||||
if(total_size == 0)
|
||||
break;
|
||||
|
||||
/* If there is again room in the output buffer,
|
||||
try this optimization again. */
|
||||
if(file->iob_BufferWriteBytes < file->iob_BufferSize)
|
||||
continue;
|
||||
}
|
||||
|
||||
c = (*s++);
|
||||
|
||||
if(__putc_fully_buffered(c,(FILE *)file) == EOF)
|
||||
goto out;
|
||||
break;
|
||||
|
||||
total_size--;
|
||||
total_bytes_written++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if((file->iob_Flags & IOBF_BUFFER_MODE) == IOBF_BUFFER_MODE_NONE)
|
||||
{
|
||||
if(__iob_write_buffer_is_valid(file) && __flush_iob_write_buffer(file) < 0)
|
||||
goto out;
|
||||
if(__iob_write_buffer_is_valid(file))
|
||||
__flush_iob_write_buffer(file);
|
||||
}
|
||||
|
||||
result = total_bytes_written / element_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Don't let this appear like an EOF or error. */
|
||||
clearerr((FILE *)file);
|
||||
SHOWVALUE(element_size);
|
||||
SHOWVALUE(count);
|
||||
|
||||
SHOWMSG("either element size or count is zero");
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
Reference in New Issue
Block a user