Skip to content

Filesystem accesses from pthreads are much slower than in the main thread. #3922

@juj

Description

@juj

In the following test case

#include <emscripten.h>
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define FILENAME    "data.bin"
#define FILESIZE    (1024*1024)
#define BLOCKSIZE   (32*1024)

static void* read_whole_file(void* args)
{
    FILE* file = (FILE*)args;
    void* data = malloc(BLOCKSIZE);

    double start = emscripten_get_now(), read_avrg = 0.0;
    for( size_t i = 0; i < FILESIZE / BLOCKSIZE; ++i )
    {
        double read_start = emscripten_get_now();
        size_t read = fread(data, 1, BLOCKSIZE, file);
        double read_end = emscripten_get_now();
        read_avrg += read_end - read_start;
        if( read != BLOCKSIZE )
        {
            fprintf(stderr, "Error while reading file.\n");
            exit(1);
        }
    }
    double end = emscripten_get_now();
    read_avrg /= FILESIZE / BLOCKSIZE;

    free(data);

    printf("Completed in %fms (~%fms per read of %u bytes)\n", end - start, read_avrg, BLOCKSIZE);
    return args;
}

int main(int argc, char* argv[])
{
    // First create random files for testing purposes
    {
        printf("Writing test file (%u bytes)...\n", FILESIZE);

        FILE* file = fopen(FILENAME, "wb");
        if( !file )
        {
            printf("Error while opening file for write.\n");
            return 1;
        }

        const char data[] = {"abcdefghijklmnopqrstuvwxyz0123456789"};
        for( size_t i = 0, c = FILESIZE / sizeof(data); i < c; ++i )
        {
            if( fwrite(data, 1, sizeof(data), file) != sizeof(data) )
            {
                printf("Error while writing to file.\n");
                return 1;
            }
        }
        size_t padding = FILESIZE % sizeof(data);
        if( padding > 0 )
        {
            if( fwrite(data, 1, padding, file) != padding )
            {
                printf("Error while writing to file.\n");
                return 1;
            }
        }

        fclose(file);
    }

    // Test reading the file from main thread
    {
        printf("Reading file from main thread...\n");

        FILE* file = fopen(FILENAME, "rb");
        if( !file )
        {
            printf("Error while opening file for read.\n");
            return 1;
        }

        read_whole_file(file);
        fclose(file);
    }

    // Test reading the file from another thread
    {
        fprintf(stdout, "Reading file from another thread...\n");

        FILE* file = fopen(FILENAME, "rb");
        if( !file )
        {
            fprintf(stderr, "Error while opening file for read.\n");
            return 1;
        }

        pthread_t thread;
        pthread_create(&thread, NULL, &read_whole_file, file);
        pthread_join(thread, NULL);
        fclose(file);
    }

    printf("Done.\n");
    return 0;
}

Compile with em++ a.cpp -o a.html -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=1. File accesses from a thread are much slower than from the main thread, and the application prints something like

Preallocating 1 workers for a pthread spawn pool.
Writing test file (1048576 bytes)...
Reading file from main thread...
Completed in 11.740000ms (~0.362031ms per read of 32768 bytes)
Reading file from another thread...
Completed in 3232.535000ms (~101.005156ms per read of 32768 bytes)
Done.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions