2
$\begingroup$

I want to make a simple C program in order to measure L1, L2 and L3 latencies of my CPU. I know some info about them:

Caches (sum of all):
  L1d:                   128 KiB (4 instances)
  L1i:                   256 KiB (4 instances)
  L2:                    2 MiB (4 instances)
  L3:                    4 MiB (1 instance)

I tried with the following code, but the output has no sense for me.

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#define L1_BLOCKS 512
#define L2_BLOCKS 8196
#define L3_BLOCKS 65536
#define BLOCK_SIZE 64

/*
  Calculate AVG time to access from arr[from_block]
  to arr[from_block + test_size].
*/
double mem_test(uint64_t *arr, int from_block, int test_size) {
    unsigned char tmp;

    double elapsed = 0;
    clock_t start_time, end_time;

    start_time = clock();

    for (int block_idx = from_block; block_idx < from_block + test_size; block_idx++) {
        tmp += arr[block_idx];
    }

    end_time = clock();
    elapsed = (double) (end_time - start_time) / test_size;

    return elapsed;
}

int main() {
    uint64_t *arr = malloc(BLOCK_SIZE * L3_BLOCKS);
    unsigned char tmp;

    // Test main memory
    // At this moment cache L1, L2, L3 should not contain
    // any of the arr blocks.
    double main_latency = mem_test(arr, 0, L1_BLOCKS);
    printf("MAIN MEMORY: %.20f\n", main_latency);
    
    // Load memory in cache.
    for (int idx = L3_BLOCKS; idx > 0; --idx) {
        arr[idx] = 1;
        tmp = arr[idx];
    }
    // At this moment, L1 should contiain all blocks from
    // arr[0] to arr[L1_BLOCKS], L2 all blocks from arr[0]
    // to arr[L2_BLOCKS], and L3 all blocks from arr[0] to
    // arr[L3_BLOCKS].

    double l1_latency = mem_test(arr, 0, L1_BLOCKS);
    printf("L1: %.20f\n", l1_latency);

    double l2_latency = mem_test(arr, L1_BLOCKS, L1_BLOCKS);
    printf("L2: %.20f\n", l2_latency);

    double l3_latency = mem_test(arr, L2_BLOCKS, L1_BLOCKS);
    printf("L3: %.20f\n", l3_latency);

    free(arr);

    return 0;
}
MAIN MEMORY: 0.00585937500000000000
L1: 0.00585937500000000000
L2: 0.00390625000000000000
L3: 0.00390625000000000000
$\endgroup$
3
  • $\begingroup$ You described a problem, coded a perceived solution and critically read the results - very well. The source code looks intended for compilation to machine code: The first thing to check is the instructions generated; many compiler support this. Does the code generated actually access memory? After all, tmp makes no difference for the value returned, and hence the contents of arr. Next, check the third argument in the calls to mem_test(). $\endgroup$
    – greybeard
    Commented Mar 13 at 19:38
  • $\begingroup$ @greybeard I have the impression that the loop does nothing at all, and CLOCKS_PER_SEC is a rather large constant. $\endgroup$
    – gnasher729
    Commented Mar 17 at 19:58
  • $\begingroup$ (@gnasher729 CLOCKS_PER_SEC apropos of what?) $\endgroup$
    – greybeard
    Commented Mar 17 at 21:18

1 Answer 1

0
$\begingroup$

Since tmp is never used, an optimising compiler may not calculate it at all - you would get a result of 0. That needs fixing. Making tmp static and printing it at the very end is enough.

Clock() returns time not in seconds, but units of CLOCKS_PER_SEC. Divide the elapsed time by CLOCKS_PER_SEC.

When you try to get the main memory latency, you have no idea whether the memory is in some cache or not. That number is meaningless.

To measure it properly: read the data once. Ignore the time. Then read the data repeatedly for one second and use that for your result.

Don’t just check the cache sizes. You want to know the curve when your use approaches or slightly exceeds the cache size. I’d start with 100 bytes then increase by 1% repeatedly up to a very large size. You want to know if your caches have any positive effect for large sizes.

Your test doesn’t test latency, it tests bandwidth. Very different. And you have so many instructions per byte that even with main memory, the instructions may be the limit, not the memory bandwidth.

Read bytes n bytes apart, for n = 1, 2, 4, 8, 16, 32, 64, 128 and 256. Check the results.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.