 |
forums.ps2dev.org Homebrew PS2, PSP & PS3 Development Discussions
|
| View previous topic :: View next topic |
| Author |
Message |
Beuc
Joined: 26 Mar 2009 Posts: 33 Location: holland
|
Posted: Tue Jul 07, 2009 12:57 am Post subject: Better I/O performances with a different BUFSIZ |
|
|
Hi,
I saw that the memory stick access is pretty unefficient when using fread(3).
From what I can see this is because BUFSIZ is set to 1024 in the psptoolchain, and it could be improved to up to 8x.
How about using a BUFSIZ value like 65k to (dramatically) improve Memory Stick perfs?
I did the following tests with read(2):
| Code: |
test_bufsiz= 512 - read(2): 31493ms (22048501B, 700.11B/s)
test_bufsiz= 1024 - read(2): 16443ms (22048501B, 1340.91B/s)
test_bufsiz= 2048 - read(2): 8964ms (22048501B, 2459.67B/s)
test_bufsiz= 4096 - read(2): 5457ms (22048501B, 4040.41B/s)
test_bufsiz= 8192 - read(2): 3703ms (22048501B, 5954.23B/s)
test_bufsiz= 16384 - read(2): 2842ms (22048501B, 7758.09B/s)
test_bufsiz= 32768 - read(2): 2391ms (22048501B, 9221.46B/s)
test_bufsiz= 65536 - read(2): 2162ms (22048501B, 10198.20B/s)
test_bufsiz= 131072 - read(2): 2126ms (22048501B, 10370.88B/s)
test_bufsiz= 262144 - read(2): 2113ms (22048501B, 10434.69B/s)
test_bufsiz= 524288 - read(2): 2104ms (22048501B, 10479.33B/s)
test_bufsiz= 1048576 - read(2): 2099ms (22048501B, 10504.29B/s)
test_bufsiz= 2097152 - read(2): 2097ms (22048501B, 10514.31B/s)
test_bufsiz= 4194304 - read(2): 2096ms (22048501B, 10519.32B/s)
test_bufsiz= 8388608 - read(2): 2094ms (22048501B, 10529.37B/s)
test_bufsiz=16777216 - read(2): 2095ms (22048501B, 10524.34B/s)
|
Source code:
| Code: |
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <SDL/SDL.h>
#include <pspmoduleinfo.h>
PSP_HEAP_SIZE_MAX();
int main()
{
SDL_Init(SDL_INIT_EVERYTHING);
Uint32 start;
printf("I/O perf test\n");
int i;
for (i = 9; i <= 24; i++)
{
int test_bufsiz = 1<<i;
char* buf = malloc(test_bufsiz);
if (buf == NULL) printf("out of memory\n"), exit(0);
printf("test_bufsiz=%8d -", test_bufsiz);
start = SDL_GetTicks();
int nb_read = 0;
int total = 0;
int din = open("ms0:/340.PBP", 0);
while ((nb_read = read(din, buf, test_bufsiz)) > 0) total += nb_read;
int t = SDL_GetTicks() - start;
printf(" read(2): %5dms (%8dB, %.2fB/s)", t, total, 1.0*total/t);
close(din);
printf("\n");
free(buf);
}
SDL_Quit();
return 0;
}
|
We can compare with fread, which internally uses 1024:
| Code: |
test_bufsiz= 512 - fread(3): 28748ms (22048256B, 766.95B/s)
test_bufsiz= 1024 - fread(3): 28704ms (22047744B, 768.11B/s)
test_bufsiz= 2048 - fread(3): 28348ms (22046720B, 777.72B/s)
test_bufsiz= 4096 - fread(3): 28681ms (22044672B, 768.62B/s)
test_bufsiz= 8192 - fread(3): 28712ms (22044672B, 767.79B/s)
test_bufsiz= 16384 - fread(3): 28376ms (22036480B, 776.59B/s)
test_bufsiz= 32768 - fread(3): 28742ms (22020096B, 766.13B/s)
test_bufsiz= 65536 - fread(3): 28725ms (22020096B, 766.58B/s)
test_bufsiz= 131072 - fread(3): 28379ms (22020096B, 775.93B/s)
test_bufsiz= 262144 - fread(3): 28741ms (22020096B, 766.16B/s)
test_bufsiz= 524288 - fread(3): 28722ms (22020096B, 766.66B/s)
test_bufsiz= 1048576 - fread(3): 28372ms (22020096B, 776.12B/s)
test_bufsiz= 2097152 - fread(3): 28735ms (20971520B, 729.82B/s)
test_bufsiz= 4194304 - fread(3): 28726ms (20971520B, 730.05B/s)
test_bufsiz= 8388608 - fread(3): 28367ms (16777216B, 591.43B/s)
test_bufsiz=16777216 - fread(3): 28725ms (16777216B, 584.06B/s)
|
(The byte counts is not accurate because fread(3) only returns "I got it all" or "I didn't got it all", without details on how much data was read in a partial read, but the timing data remains right.)
Source code:
| Code: |
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <SDL/SDL.h>
#include <pspmoduleinfo.h>
PSP_HEAP_SIZE_MAX();
int main()
{
SDL_Init(SDL_INIT_EVERYTHING);
Uint32 start;
printf("I/O perf test\n");
int i;
for (i = 9; i <= 24; i++)
{
int test_bufsiz = 1<<i;
char* buf = malloc(test_bufsiz);
if (buf == NULL) printf("out of memory\n"), exit(0);
printf("test_bufsiz=%8d -", test_bufsiz);
start = SDL_GetTicks();
int total2 = 0;
FILE* fin = fopen("ms0:/340.PBP", "rb");
while (fread(buf, test_bufsiz, 1, fin)) total2 += test_bufsiz;
int t = SDL_GetTicks() - start;
printf(" fread(3): %5dms (%8dB, %.2fB/s)", t, total2, 1.0*total2/t);
fclose(fin);
printf("\n");
free(buf);
}
SDL_Quit();
return 0;
}
|
|
|
| Back to top |
|
 |
jimparis
Joined: 10 Jun 2005 Posts: 1179 Location: Boston
|
Posted: Tue Jul 07, 2009 3:03 am Post subject: |
|
|
| sure, if you could write a patch I'll apply it. |
|
| Back to top |
|
 |
TyRaNiD
Joined: 18 Jan 2004 Posts: 918
|
Posted: Tue Jul 07, 2009 3:07 am Post subject: |
|
|
Urm be careful though (I haven't checked where the buffer is defined) but there is always the risk that increasing the size could make existing code fail due to higher memory requirements (well when recompiled against a new libc).
If it is a static buffer (I guess likely) then you could blow away almost an extra meg of ram if someone opened as many MS files as they can. If it is stack based there is the risk of stack overflow which are really bastards to track down on the PSP.
As I have always tried to point out, libc is there more for glue code to make porting easier than something which you should rely on in a memory limited embedded system. |
|
| Back to top |
|
 |
Beuc
Joined: 26 Mar 2009 Posts: 33 Location: holland
|
Posted: Tue Jul 07, 2009 3:59 am Post subject: |
|
|
| jimparis wrote: | | sure, if you could write a patch I'll apply it. |
I'll give this a try!
| TyRaNiD wrote: | Urm be careful though (I haven't checked where the buffer is defined) but there is always the risk that increasing the size could make existing code fail due to higher memory requirements (well when recompiled against a new libc).
If it is a static buffer (I guess likely) then you could blow away almost an extra meg of ram if someone opened as many MS files as they can. If it is stack based there is the risk of stack overflow which are really bastards to track down on the PSP.
As I have always tried to point out, libc is there more for glue code to make porting easier than something which you should rely on in a memory limited embedded system. |
Yes, a too big buffer sound like a bad idea. Plus it's only 8192k in GNU/Linux (though that's an efficient value there) - better remain on the same order of magnitude.
Still, fread(3) is used by lots of games, if only through SDL (and SDL_rwops.c), so it's worth the trouble :) |
|
| Back to top |
|
 |
J.F.
Joined: 22 Feb 2004 Posts: 2906
|
Posted: Tue Jul 07, 2009 4:05 am Post subject: |
|
|
I'd suggest something like the PSP_HEAP_SIZE_XXX defines... make a weak variable called something like "_fread_bufsize" that defaults to 1024 when not defined, but otherwise takes the defined value. That way, someone who's aware of the memory constraint and wants the fastest speed can add "PSP_FREAD_BUFSIZE(65536);" to the application to set the buffer size explicitly.
Look at the sbrk code in newlib to see how the heap size is handled. |
|
| Back to top |
|
 |
coolkehon
Joined: 20 Oct 2008 Posts: 355
|
Posted: Tue Jul 07, 2009 4:30 am Post subject: |
|
|
this sound like a good idea
note: just posting so that i will get notified by email because i dont know how to watch this topic =) |
|
| Back to top |
|
 |
Beuc
Joined: 26 Mar 2009 Posts: 33 Location: holland
|
Posted: Tue Jul 07, 2009 5:17 am Post subject: |
|
|
| J.F. wrote: | I'd suggest something like the PSP_HEAP_SIZE_XXX defines... make a weak variable called something like "_fread_bufsize" that defaults to 1024 when not defined, but otherwise takes the defined value. That way, someone who's aware of the memory constraint and wants the fastest speed can add "PSP_FREAD_BUFSIZE(65536);" to the application to set the buffer size explicitly.
Look at the sbrk code in newlib to see how the heap size is handled. |
Well, BUFSIZ is used in a lot of statically allocated variables, including outside of newlib, so I think it needs to stay constant.
Maybe we can use this technique to change the FILE* internal buffer size though.
(apparently this happens in newlib/libc/stdio/makebuf.c:109) |
|
| Back to top |
|
 |
Beuc
Joined: 26 Mar 2009 Posts: 33 Location: holland
|
Posted: Tue Jul 07, 2009 8:26 am Post subject: |
|
|
| Beuc wrote: | Maybe we can use this technique to change the FILE* internal buffer size though.
(apparently this happens in newlib/libc/stdio/makebuf.c:109) |
I tried the following patch:
| Code: |
--- newlib-1.16.0.bak/newlib/libc/stdio/makebuf.c 2007-05-02 01:03:36.000000000 +0200
+++ newlib-1.16.0-psp/newlib/libc/stdio/makebuf.c 2009-07-06 23:56:19.000000000 +0200
@@ -74,8 +74,12 @@
#ifdef HAVE_BLKSIZE
size = st.st_blksize <= 0 ? BUFSIZ : st.st_blksize;
#else
+#if defined(__psp__)
+ size = 65536;
+#else
size = BUFSIZ;
#endif
+#endif
/*
* Optimize fseek() only if it is a regular file.
* (The test for __sseek is mainly paranoia.)
|
Here's the results of the fread(3) loop with different values:
| Code: |
newlib size = 1024 - fread(3): 26929ms (current)
newlib size = 4096 - fread(3): 11260ms
newlib size = 32768 - fread(3): 3467ms
newlib size = 65536 - fread(3): 2885ms
newlib size = 131072 - fread(3): 2595ms
newlib size = 1048576 - fread(3): 2367ms
|
It doesn't matter what test_bufsiz you use, the perfs are the same with +/- 50ms.
(btw perfs are slightly better than in my first post because I rebooted from fw 1.50 to 5.00 during the afternoon - 65k perfs for fw 1.50 are around 2925ms).
This looks promising :)
Also:
| TyRaNiD wrote: | | If it is a static buffer (I guess likely) then you could blow away almost an extra meg of ram if someone opened as many MS files as they can. If it is stack based there is the risk of stack overflow which are really bastards to track down on the PSP. |
AFAICS it's a malloc:
| Code: |
if ((p = _malloc_r (ptr, size)) == NULL)
|
I also see that the PSP crash when open more than 10 files at once. So the increased memory usage is at most (65k-1k)*10 = 640k.
We need to perform some _write_ test now.
If write performances are not harmed I think this patch is enough. BUFSIZ is not changed, and still does not represent an efficient buffer size, but there may be less issues with only altering 'size'.
What do you think? |
|
| Back to top |
|
 |
Beuc
Joined: 26 Mar 2009 Posts: 33 Location: holland
|
Posted: Tue Jul 07, 2009 8:48 am Post subject: |
|
|
Hmmm, BAD news.
I tried recompiling the game port I'm working on (GNU FreeDink), and apparently this patch dramatically _harms_ the loading time.
I suppose that when accessing lots of small files or when doing a decent number of fseek()s, reading big chunks (with a 65k buffer) harm performances (like 4x slower) more than when reading small chunks (with a 1k buffer).
Ah well.. |
|
| Back to top |
|
 |
J.F.
Joined: 22 Feb 2004 Posts: 2906
|
Posted: Tue Jul 07, 2009 9:05 am Post subject: |
|
|
| Beuc wrote: | Hmmm, BAD news.
I tried recompiling the game port I'm working on (GNU FreeDink), and apparently this patch dramatically _harms_ the loading time.
I suppose that when accessing lots of small files or when doing a decent number of fseek()s, reading big chunks (with a 65k buffer) harm performances (like 4x slower) more than when reading small chunks (with a 1k buffer).
Ah well.. |
I suppose fread needs to be made smarter - only reading what is asked for on the first read, but reading larger amounts if the next (one or two or more) freads are all sequential. That way sequential reads are sped up, while small random reads aren't slowed by reading large amounts that won't be used. |
|
| Back to top |
|
 |
TyRaNiD
Joined: 18 Jan 2004 Posts: 918
|
Posted: Wed Jul 08, 2009 2:56 am Post subject: |
|
|
| 640K is still quite a sizable increase :) |
|
| Back to top |
|
 |
Beuc
Joined: 26 Mar 2009 Posts: 33 Location: holland
|
Posted: Wed Jul 08, 2009 7:18 am Post subject: |
|
|
AFAICS access time for small files doesn't change, this only impacts smalls reads in larger files:
| Code: |
test_bufsiz= 1024 - read(2): 401ms - lseek(2): 467ms
test_bufsiz= 2048 - read(2): 397ms - lseek(2): 444ms
test_bufsiz= 4096 - read(2): 397ms - lseek(2): 803ms
test_bufsiz= 8192 - read(2): 398ms - lseek(2): 766ms
test_bufsiz= 16384 - read(2): 397ms - lseek(2): 846ms
test_bufsiz= 32768 - read(2): 398ms - lseek(2): 995ms
test_bufsiz= 65536 - read(2): 397ms - lseek(2): 1425ms
test_bufsiz= 131072 - read(2): 399ms - lseek(2): 2069ms
test_bufsiz= 262144 - read(2): 398ms - lseek(2): 3320ms
test_bufsiz= 524288 - read(2): 397ms - lseek(2): 5827ms
test_bufsiz= 1048576 - read(2): 397ms - lseek(2): 10873ms
|
Source code:
| Code: |
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <SDL/SDL.h>
#include <pspmoduleinfo.h>
PSP_HEAP_SIZE_MAX();
int main()
{
SDL_Init(SDL_INIT_EVERYTHING);
Uint32 start;
printf("I/O perf test\n");
int i;
for (i = 10; i <= 20; i++)
{
int n;
int test_bufsiz = 1<<i;
char* buf = malloc(test_bufsiz);
if (buf == NULL) printf("out of memory\n"), exit(0);
printf("test_bufsiz=%8d", test_bufsiz);
start = SDL_GetTicks();
for (n = 0; n < 100; n++)
{
int nb_read = 0;
int total = 0;
int din = open("ms0:/batser.bin", 0);
if (din < 0) perror("open"), exit(0);
while ((nb_read = read(din, buf, test_bufsiz)) > 0) total += nb_read;
close(din);
}
printf(" - read(2): %5dms", SDL_GetTicks() - start);
start = SDL_GetTicks();
int din = open("ms0:/500.PBP", 0);
if (din < 0) perror("open"), exit(0);
for (n = 0; n < 100; n++)
{
int nb_read = 0;
int total = 0;
if (lseek(din, 3500000, SEEK_SET) < 0)
perror("lseek"), exit(0);
do {
nb_read = read(din, buf, test_bufsiz);
total += nb_read;
} while (total < 5000);
}
close(din);
printf(" - lseek(2): %5dms", SDL_GetTicks() - start);
printf("\n");
free(buf);
}
SDL_Quit();
return 0;
}
|
|
|
| Back to top |
|
 |
Torch

Joined: 28 May 2008 Posts: 842
|
Posted: Thu Jul 09, 2009 12:21 am Post subject: |
|
|
| How do the sceIO* functions handle things? Does it have another buffer or does it DMA straight into your pointer? |
|
| Back to top |
|
 |
Beuc
Joined: 26 Mar 2009 Posts: 33 Location: holland
|
Posted: Fri Jul 10, 2009 7:21 am Post subject: |
|
|
Hi!
| Torch wrote: | | How do the sceIO* functions handle things? Does it have another buffer or does it DMA straight into your pointer? |
AFAICS the only software buffer is in fread(3). fread(3) itself calls read(2) which calls sceIoRead, with some reentrant (*_r) steps in-between.
The tests suggests that the PSP kernel doesn't manage cache for MS access. |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|