Визуализатор структуры адреса на Си для Linux и Termux: Попасть в «Голову» кеш-линии

от автора

В 47 мои увлечения стали Си, Radare2 , Биты, Логика, Память. Изучая память зацепился за адреса. На адрес 0x7ffe10b284 можно смотреть бесконечно долго. Трудно сказать с ходу насколько удачно ваши данные легли в память. Влезают они в одну кеш-линию или размазаны по двум. Чтоб не заниматься битовой арифметикой в уме, я написал утилиту на Си для Linux и Termux. Она раскладывает младшие 12 бит адреса на 4 строки визуализации. Теперь сразу видно, попали мы в «Голову» или застряли в «Хвосте» кеш-линии.

Утилита максимально легковесная. Вам не нужны сложные дебаггеры, достаточно gcc. Работает, как на десктопном Linux, так и в Termux на Android. Можно проверить выравнивание даже лёжа на диване.

Что такое «Голова» и «Хвост» кеш-линии

В современных процессорах (х86_64, ARM) память нарезана на блоки по 64 байта — это и есть кеш-линия. Процессор не умеет забирать из памяти один байт, он всегда тянет всю линию целиком.

Если ваша структура данных (например 16 интов) начинается в начале этой линии (адрес кратен размеру данных 64), она ложится в ее «Голове». Если же адрес смещен к концу блока (ближе к 63-му биты), данные попадают в хвост.

Почему это критично

Представьте вашу структуру массив на 16 интов (это как раз 64 байта размер кеш-линии). И вы не расположили так, что 8 интов лежат в конце одной кеш-линии в «Хвосте» , в остальные 8 в начале следующей.

  • РЕЗУЛЬТАТ: Процессору придётся выкачивать две линии вместо одной. Поход в память это дорого. Процессору в этом случае приходится ждать заряд, разряд конденсаторов.

  • ИТОГИ: Лишние такты, посадка производительности, не эффективное использование кеш L1.

АККУМУЛЯТОР: Каждая лишняя активная линия это мизерный но реальный ток. В масштабах миллионов операций в секунду это превращается в повышенный расход АКБ. Что особенно критично для мобильных устройств.

Код на Си

Я новичок мой код скорее всего кривой и косой. Я хочу визуализировать адрес памяти.

Логика простая: мы берём адрес переменной, разглаживает на биты и смотрим карту где мы находимся. Смещение страницы, кеш, номер набора и все остальное.

#include <stdio.h>#include <stdint.h>#define RAZR 12           //колличество разрядов выводим на печать#define GEL "\033[1;33m"#define RED "\033[1;31m"#define GRIN "\033[1;32m"#define ROZ "\033[1;35m"#define SIN "\033[1;34m"#define RES "\033[0m"void hex_dec_nom_raz(){        printf("\n\n");        int dec = 1; // строка десятичное представление двоичного числа dec        for (int i = 0; i < RAZR; i ++)        {                printf(GRIN "%8d" RES , dec);                dec = dec << 1;        }        printf("\n\n");        for (int i = 0; i < RAZR; i ++) // строка для вычисления номера индекса номер набора        {                printf(ROZ "%8d" RES ,1 << (i % 6));        }        printf("\n\n");        for (int j = 0; j < RAZR; j ++) // строка для номер разряда        {                printf(SIN  "%8d" RES , j);        }        printf("\n\n");}int bin_addr (uint64_t addr) //функция двоичного вида адреса{        int bin, offset_kech, mask = 63;        for (int i = 0; i < RAZR; i ++)        {                bin = (addr >> i) & 1; // для цветного вывода единиц                if (bin == 1)                {                        printf(RED "%8d" RES, bin);                }                else                {                        printf("%8d", bin);                }        }        offset_kech = addr & mask; // смотрим смещение в КЭШ линии        if ( offset_kech != 0 )        {                printf(GEL "\nРасстояние до головы КЕШ линии > %d < байт \n" RES, offset_kech);        }        else        {                printf(GEL "\nТы попал в голову\n" RES);        }        printf("\n");        return 0;}int main (){        uint64_t addr_mem;        hex_dec_nom_raz ();        while (1)        {                if (scanf("%li", &addr_mem) == 1)                {                        bin_addr (addr_mem);                }                else                {                        break;                }        }        return 0;

Эксперимент

Сравним два адреса. Чтоб окончательно убедиться в пользе визуализатор, я сравниваю два случая.

Пишем две маленькие программы. В каждой программе массив, один случайный, другой выровненный по кеш-линии. И нам нужно вывести адреса нулевых элементов.

Массив со случайным адресом < vim mas_random.c>

Пишем код на Си

Пишем код на Си

Массив выровненный по кеш-линии <vim mas_alig.c>

Пишем код на Си

Пишем код на Си

gcc mas_alig.c -o mas_alig

  • gcc mas_random.c -o mas_random

  • Запуск

    • ./mas_alig

    • ./mas_random

    Выводим адреса массивов

    Выводим адреса массивов

    ./bin_analiz_addt

    • Утилита после запуска

      Утилита после запуска

      Адрес жёлтого цвета это выровненный массив по кеш-линии

    • Адрес красного цвета это адрес случайного массива

    ЗЕЛЁНАЯ ЛИНИЯ: Десятичное представление двоичного числа.

    • РОЗОВАЯ ЛИНИЯ: Номер набора.

    • СИНЯЯ ЛИНИЯ: Номер разряда.

    • БЕЛЫЕ ЛИНИИ: Двоичное представление адреса памяти.

    Первый адрес заканчивается на 0х80 это выровненный массив по кеш-линии. Единица в 7 разряде. Мы положили свои 16 интов в одну кеш-линию. ( Потом мы можем прочитать смещение страницы нам до «Головы» 128 байт. 4096 — 128 полученная цифра расстояние до «Хвоста» (до конца страницы). Номер набора 2 розовая линия с 6 по 11 разряды.

    Второй адрес заканчивается 0х7с это адрес случайного массива. Две кеш-линии не избежны. Мы находимся в средине страницы сложи 1024+512+256+64+32+16+8+4. Номер набора 1 +4+8+16.

    Здесь на самом деле много информации

    Техничка

    • Мы можем управлять разрядностью #define RAZ.

    • Вводить сколько угодно адресов.

    • DEC or HEX

    • Выход из программы любой символ я применяю стандарт » q «.

    • Можно выводить множество сообщений где ты находишься в кеш, на странице, можем расписать тэги.

    Ссылка на GitHub

    https://github.com/Data7Viz/addr_bit

ссылка на оригинал статьи https://habr.com/ru/articles/1026430/