ZinGer_KyoN
ZinGer_KyoN

Reputation: 89

In C, Is there a way to load array into memory when need?

Newbie for c, so maybe this question is naive:
Is there a way to load array into memory when need?

I'm working on some embedded chip and there are some lookup tables under different configurations,
after configure the system will run and using only one of many lookup tables, and if initialize system again and configure differently it will use another one.

I'm currently defined each table as global varbiable and use pointer points to different table under different configuration, but I guess this means all tables is loaded to memory right?

int big_tableA[BIG_N] = {...};
int big_tableB[BIG_M] = {...};

int some_global_config = 0;

void foo(){
    int* table;
    table = (some_global_config==0) ? big_tableA : big_tableB
    while 1 {
        // do something about table
    };    
};

Since memory is valuable resource in chip so I wonder is there a way to only load one table that we needed into memory?

Upvotes: 0

Views: 718

Answers (2)

Clifford
Clifford

Reputation: 93556

Of course you can load data into memory from some other source at runtime, but you have to have a data source - the data cannot come from nowhere.

The data might be stored in some external device such as a serial EEPROM or Flash or a mass storage device such as an SD card (implying that you have a file system). Or you might load it from a host device via a communication link or network. Of course a file system or network stack itself takes up space, so the tables would have to be particularly large to justify that.

If all you have is on-chip memory, then where are you going to load this data from? It has to be in memory in any event. However in you example the data is RAM resident and SRAM density is relatively low; most MCUs have larger Flash ROM. If the data in constant, then you are wasting RAM space in this instant, and the initialiser data must also exist in ROM in any case.

So it is likely that you can store these arrays in ROM simply by declaring then const (depends on your toolchain and MCU how a const is actually located - you may require additional compiler directives to enforce that, check the documentation or the link map) :

const int big_tableA[BIG_N] = {...};
const int big_tableB[BIG_M] = {...};

or better:

const int big_tableA[] = {...};
const int big_tableB[] = {...};

const size_t BIG_N = (sizeof(tableA)/sizeof(*tableA) ;
const size_t BIG_M = (sizeof(tableB)/sizeof(*tableB) ;

So that each array is only as large as number of initialisers provided.

If the data is required to be variable, then the table initialisers can be in ROM while the active table is copied to RAM at runtime. You need enough ram for the largest table, so you might have:

int* getActiveTable( int table_index, size_t* length )
{
    static const int big_tableA[] = {...};      // In ROM
    static const int big_tableB[] = {...};      // In ROM
    // add other tables here

    static int active_table[MAX_TABLE_LEN] = {0} ;   // In RAM

    static const struct
    {
        void* data ;
        size_t length ;
    } table_select_lookup[] = { 
                                {big_tableA, sizeof(big_tableA)},
                                {big_tableB, sizeof(big_tableB)} 
                                // add other tables here
                              } ;

    *length = table_select_lookup[table_index].length ;
    memcpy( active_table, 
            table_select_lookup[table_index].table, 
            *length ) ;

    return active_table ;
}

Then to use (in the context of your frankly ill-advised some_global_config ):

void foo()
{
    size_t length = 0 ;
    int* table = getActiveTable( some_global_config, &length ) ;

    // do something with the table
} 

If the tables are constant and do not need to be RAM resident you can use the same interface but simplify the implementation:

const int* getActiveTable( int table_index, size_t* length )
{
    static const int big_tableA[] = {...};      // In ROM
    static const int big_tableB[] = {...};      // In ROM
    // add other tables here

    static const struct
    {
        void* data ;
        size_t length ;
    } table_select_lookup[] = { 
                                {big_tableA, sizeof(big_tableA)},
                                {big_tableB, sizeof(big_tableB)} 
                                // add other tables here
                              } ;

    return table_select_lookup[table_index].table ;
}

That allows the higher level code to be identical regardless of the nature of the tables, and avoids exposing the tables globally. Code can only get the table reference through this function so using the wrong table, to different tables in different parts of the code is less likely.

Upvotes: 0

4386427
4386427

Reputation: 44339

I guess this means all tables is loaded to memory right

Yes, that's correct.

In your code the tables are global variables. In C that means "static storage duration" which means that they exists during the whole program execution. In other words - they require memory from start to end.

To reduce memory consumption, you need to load/unload the tables at runtime. This requires that you have some non-volatile storage (e.g. a file system on a disk, a flash, a rom or similar) that they can be loaded from when you need them.

Another trick you can consider is to compress the tables (i.e. like zip) so that all the tables are in memory in compressed form but only the table needed is in memory in decompressed form. With a "high" number of tables and a "good" compression ratio this can save a lot of memory. Some compress/decompress algorithms are developed to have fast and simple decompression which makes them suitable for such use cases.

Upvotes: 1

Related Questions