Assembly Convolution с ручным управлением памятью

У меня есть процессор riscv и процессор расширения, который можно программировать. Другими словами, расширение имеет свою уникальную ISA.

Я вставлю в это расширение инструкцию для выполнения свертки программой, работающей на процессоре riscv. Другими словами, если я запускаю следующие коды, процессор riscv вставляет инструкцию lw в это расширение на каждой итерации. k соответствует номеру регистра, а kernel_pointer — адресу SRAM.

for(int k = 0; k < input_channel_size; k++){lw_encode(k, kernel_pointer);kernel_pointer++;}

Это расширение имеет собственную DRAM и SRAM, которыми нужно управлять вручную. У меня проблемы с управлением памятью. В моей ситуации емкость DRAM бесконечна, а емкость SRAM составляет 1024 слова. Я могу получить доступ к DRAM только с 32-байтовым выровненным доступом, и его доступ должен передавать несколько 32 байтов.

Учитывая, что каждый пиксель имеет длину 32 бита, как вы знаете, поскольку в почти ситуации полное изображение и ядро ​​​​не могут быть загружены в SRAM, поэтому мне приходится извлекать изображения только в небольших количествах и вычислять их с помощью итерации. Но поскольку DRAM разрешает доступ только к адресу с выравниванием по 32 байтам, а не по адресу с выравниванием по 32 битам, и поскольку она должна извлекать 32 байта, а не 32 бита, во многих случаях мне приходится извлекать ненужные или следующие пиксели операнда.

Для адреса SRAM я назначу адрес от 0 до 255 для входного изображения, от 256 до 511 для фильтра и от 512 до 755 для адреса назначения, и я хочу зарезервировать 756-1023 для дальнейшего расширения. Конечно, эта адресация является произвольной и может быть предварительно изменена.

В этой ситуации, например, пусть изображение[32][32][6] и фильтр[3][3][6] и если я рассматриваю умножение и сложение с точки зрения фильтра[0][0][0:6 ], мне нужно получить изображение[0:30][0:30][0:6]. А так как наименьший размер пикселя извлеченного изображения равен 8, то изображение [0][0] невозможно, так как оно имеет только 6 пикселей, что означает, что 2 пикселя не нужны. Я пытаюсь решить эту проблему, введя концепцию циклической очереди, но не знаю, как справиться с этой ситуацией. А что, если будут извлечены пиксели вне области видимости? Например, изображение[31][31][0:6] может быть получено из-за свойства доступа к DRAM.

Вот фрагмент кода для простоты понимания моей ситуации. Я думаю, что этот код не будет работать, так как он пытается получить доступ к адресу DRAM, который не выровнен по 32 байтам.

        if(input_channel_size < 16){ // weight_stationary_case
        dram_read_wrapping(kernel_pointer, dram_kernel, 9 * input_channel_size, k_corner, false);
        for(int m = 0; m < 9; m++){
            for(int k = 0; k < input_channel_size; k++){
                lw_encode(k, kernel_pointer);
                kernel_pointer++;
            }
            for(int i = 0; i < size_of_output * size_of_output;){
                f_num = dram_read_wrapping(feature_pointer, dram_feature, input_channel_size, f_corner, true);
                for(int j = 0; j < f_num; j++){
                    if(((j + 1) % size_of_output) == 0){
                        feature_pointer += 2 * input_channel_size;
                        feature_pointer = feature_pointer % 0x100;
                        destination_end += 2;
                        if(destination_end >= 0x300) dram_write_wrapping(destination_start, dram_result, destination_end, d_corner);
                        j++;
                        continue;
                    }
                    for(int k = 0; k < input_channel_size; k++){
                        lw_encode(k, feature_pointer);
                        feature_pointer++;
                        feature_pointer = feature_pointer % 0x100 ;
                    }
                    // mac_reserve();
                    for(int k = 0; k < 3; k ++){
                        mac_encode(31, 1 + k, 4 + k);
                    }
                    // mac_reserve();
                    sw_encode(31, destination_end++);
                    if(destination_address == 0x300) dram_write_wrapping(destination_start, dram_result, destination_end, d_corner);
                }
                i += f_num;
            }
            if(m == 2 || m == 5) dram_feature += 2 * input_channel_size;
            else dram_feature++;
        }
    }

Есть ли какая-нибудь идея, которая поможет в этой ситуации? Спасибо. Кроме того, если есть неясный контекст, сообщите мне, чтобы я обновил описание.


person laurent01    schedule 18.07.2020    source источник
comment
Можете быть более конкретными? Поддерживаете ли вы стек вызовов? Какой алгоритм вы пишете на ассемблере? Какова ваша цель? Что вы имеете в виду под углом?   -  person Erik Eidt    schedule 18.07.2020
comment
Я обновляю описание, чтобы оно было более конкретным. Не могли бы вы прочитать его еще раз? Спасибо.   -  person laurent01    schedule 19.07.2020
comment
Я бы поэкспериментировал с вашими шаблонами доступа со следующими целями: (1) определить, что следует кэшировать в SRAM, а что нет, (2) как можно изменить алгоритм, чтобы он мог работать с тайлами, поддерживающими кэширование в SRAM. а не как сейчас. Что касается (1), вы должны иметь возможность взять алгоритм, который у вас есть сейчас, и наложить стратегии кэширования, чтобы вы могли измерить различия в них, и, что касается (2), попытаться определить изменения в порядке обработки алгоритма. так что он больше работает с вещами, уже доступными в SRAM.   -  person Erik Eidt    schedule 19.07.2020
comment
Вы можете найти блокировку кеша Google, также известную как мозаика цикла, чтобы узнать больше о таких методах.   -  person Peter Cordes    schedule 19.07.2020
comment
@PeterCordes software.intel.com/ content/www/us/en/develop/articles/ эта статья, которая поставляется с блокировкой кеша Google, очень полезна для меня. обожаю такие комментарии   -  person laurent01    schedule 20.07.2020


Ответы (1)


В основном я справляюсь с этой проблемой описанным выше методом.

Для случая чтения DRAM: если есть невыровненный доступ, я читаю все через границу, что означает, что я могу получить до трех блоков в SRAM. И я использую это с обрезанным указателем.

В случае записи в DRAM, если есть невыровненный доступ, я читаю соответствующий блок в SRAM и непрерывно записываю то, что хочу, в SRAM и записываю в DRAM.

Этот метод решает проблему, а «Блокировка кеша» и «Разбиение на циклы» ускоряют работу программы, о чем упоминает @PeterCordes.

person laurent01    schedule 21.07.2020