I have a C project that I'm using with Alchemy. The project has some post-build command-line tests that I'd like to run using the swfbridge.
These tests run, but they're extremely slow. The problem is that they read some moderately large files (~3MB) into memory. Running these same tests with the same files via regular Alchemy (e.g., not using swfbridge but using supplyFile from AS) is very fast.
I think the bottleneck is the swfbridge. More specicially, in the way that swfbridge loads files. It reads them in and transmits them in 1024 byte chunks across the localhost connection to the main alchemy swf. (You can see this happening in the swfbridge.log.)
My question is: is there a way to make swfbridge more efficient? Can I make it use a different chunk size, for example?
Here is an example of the file-reading code. If you give this code a ~3MB file it will run very slowly.
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
size_t filesize(const char filename[]) {
struct stat stbuf;
if (stat(filename, &stbuf) == -1) {
fprintf(stdout, "file_size: can't find %s\n...\n", filename);
return (-1);
}
return stbuf.st_size;
}
int main(int argc, char **argv) {
const char *filename= argv[1];
size_t size= filesize(filename);
printf("allocating %d bytes \n", size); fflush(stdout);
char *data= (char*)malloc(size);
printf("reading %d bytes \n", size); fflush(stdout);
FILE *file= fopen(filename, "r");
fread(data, size, 1, file);
printf("done \n"); fflush(stdout);
free(data);
fclose(file);
return 0;
}
I figured out a workaround to this problem.
When running a swf from the command line, the file i/o is really slow because the swf process is getting the file from the swfbridge process via localhost sockets. Something about this is unoptimized (the Alchemy guys probably didn't expect anyone to use this in a serious way). swfbridge is used so that stdin and stdout will work. But I'm not completely clear on why its used for file i/o-- the swf is running in adl (Air!) after all-- it has file system access.
Anyway, the fact that the swf is running in Air can be used. We can route fread/fwrite through Air methods (rather than through swfbridge) by using the wonderful funopen. Its actually a good bit of code, but here's an idea of it:
FILE* air_fopen(const char filename[], const char mode[]) {
AS3_Val file= AS3_FileFromPath(filename);
AS3_Val FileModeClass= AS3_GetClass("flash.filesystem", "FileMode");
AS3_Val fileMode = AS3_GetS(FileModeClass, fopenModeToAirMode(mode));
AS3_Val fileStream = AS3_NewObject("flash.filesystem", "FileStream");
AS3_CallTS("open", fileStream, "AS3ValType, AS3ValType", file, fileMode);
AS3_Release(FileModeClass);
AS3_Release(fileMode);
AS3_Release(file);
return funopen(fileStream,
(funopen_read_t)air_fread, (funopen_write_t)air_fwrite,
(funopen_seek_t)air_fseek, (funopen_close)air_fclose);
}
where air_read is like this:
int air_fread(AS3_Val fileStream, char *dest, int size) {
int bytesAvailable= AS3_GetIntProperty(fileStream, "bytesAvailable");
if (bytesAvailable <= 0) {
return 0;
} else if (size > bytesAvailable) {
size= bytesAvailable;
}
AS3_CallTS("readBytes", fileStream, "AS3ValType, IntType, IntType", AS3_Ram(), dest, size);
return size;
}
The other air_fwrite/air_fweek/air_fclose are similar. Note that some of these functions (like AS3_FileFromPath, AS3_GetInProperty, AS3_NewObject, etc) are my own simple wrappers around the AS3 api.
This approach removes the swfbridge bottleneck and makes command-line swfs just as fast as normal ones.