具有手动内存管理的装配卷积
Assembly Convolution with manual memory management
我有 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 字。我可以访问只有 32 字节对齐访问的 DRAM,并且它的访问必须传输 32 字节的倍数。
鉴于每个像素是 32 位,如您所知,由于在几乎所有情况下,整个图像和内核都无法提取到 SRAM 中,因此我只能提取少量图像并进行迭代计算。但是由于 DRAM 只允许访问 32 字节对齐的地址,而不是 32 位对齐的地址,并且由于它必须获取 32 字节而不是 32 位,所以在很多情况下我必须获取不必要的或下一个操作数像素。
对于SRAM的地址,我将0~255地址分配给输入图像,256~511分配给过滤器,512~755分配给目标地址,我想保留756~1023以供进一步扩展。当然这个寻址是任意的,可以临时更改。
在这种情况下,例如让image[32][32][6]和filter[3][3][6]
如果我根据 filter[0][0][0:6] 考虑乘法和加法,我必须获取图像 [0:30][0:30][0:6]。并且由于获取的图像像素的最小尺寸为 8,因此不可能成像 [0][0] 因为它只有 6 个像素,这意味着 2 个像素是不必要的。我试图通过引入循环队列概念来解决这个问题,但不确定如何处理这种情况。如果获取了超出范围的像素怎么办?例如,image[31][31][0:6] 可能由于 属性 的 DRAM 访问而被提取。
为了便于理解我的情况,这里是代码片段。我认为这段代码不起作用,因为它试图访问不是 32 字节对齐的 DRAM 地址。
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++;
}
}
有什么想法对这种情况有帮助吗?谢谢。
另外,如果上下文不明确,请告诉我,以便我更新描述。
基本上我都是用上面介绍的方法来处理这个问题的
对于 DRAM 读取情况,如果存在未对齐访问,我会跨边界读取所有内容,这意味着我最多可以将三个块提取到 SRAM。我将其与切断指针一起使用。
对于DRAM写入情况,如果存在未对齐访问,我将相应的块读取到SRAM,然后连续写入我想要的东西到SRAM并写入DRAM。
这项技术解决了问题,'Cache Blocking' 和 'Loop Tiling' 使程序更快,@PeterCordes 提到了这一点。
我有 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 字。我可以访问只有 32 字节对齐访问的 DRAM,并且它的访问必须传输 32 字节的倍数。
鉴于每个像素是 32 位,如您所知,由于在几乎所有情况下,整个图像和内核都无法提取到 SRAM 中,因此我只能提取少量图像并进行迭代计算。但是由于 DRAM 只允许访问 32 字节对齐的地址,而不是 32 位对齐的地址,并且由于它必须获取 32 字节而不是 32 位,所以在很多情况下我必须获取不必要的或下一个操作数像素。
对于SRAM的地址,我将0~255地址分配给输入图像,256~511分配给过滤器,512~755分配给目标地址,我想保留756~1023以供进一步扩展。当然这个寻址是任意的,可以临时更改。
在这种情况下,例如让image[32][32][6]和filter[3][3][6] 如果我根据 filter[0][0][0:6] 考虑乘法和加法,我必须获取图像 [0:30][0:30][0:6]。并且由于获取的图像像素的最小尺寸为 8,因此不可能成像 [0][0] 因为它只有 6 个像素,这意味着 2 个像素是不必要的。我试图通过引入循环队列概念来解决这个问题,但不确定如何处理这种情况。如果获取了超出范围的像素怎么办?例如,image[31][31][0:6] 可能由于 属性 的 DRAM 访问而被提取。
为了便于理解我的情况,这里是代码片段。我认为这段代码不起作用,因为它试图访问不是 32 字节对齐的 DRAM 地址。
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++;
}
}
有什么想法对这种情况有帮助吗?谢谢。 另外,如果上下文不明确,请告诉我,以便我更新描述。
基本上我都是用上面介绍的方法来处理这个问题的
对于 DRAM 读取情况,如果存在未对齐访问,我会跨边界读取所有内容,这意味着我最多可以将三个块提取到 SRAM。我将其与切断指针一起使用。
对于DRAM写入情况,如果存在未对齐访问,我将相应的块读取到SRAM,然后连续写入我想要的东西到SRAM并写入DRAM。
这项技术解决了问题,'Cache Blocking' 和 'Loop Tiling' 使程序更快,@PeterCordes 提到了这一点。