Комментариев от @COKPOWEHEU по этой лекции нет, зато есть замечание от Вячеслава Разыкова
Георгий Владимирович, здравствуйте. Пересмотрел прошлогоднюю лекцию по конвейеру в RISC-V. Вы там затронули тему внеочередного выполнения инструкций. Понимаю, тема очень обширная, чтобы её охватить полностью, но предлагаю немного углубиться в этом году.
В частности, рассказать, что эта "магия" удаления "пузырьков" из пайплайна может потребовать внимания со стороны программиста, потому что эта оптимизация создаёт практические проблемы, которые требуют добавления барьеров синхронизации. Упомянуть, что в RISC-V это решается инструкциями fence, fence.i (у Паттерсона ещё sfence.vma упоминается, хотя их много разных).
Также можно продемонстрировать такой хрестоматийный пример, который моделирует ситуацию, когда нужна fence. Здесь программа запускается на двух hart’ах. Один пишет в буфер и выставляет флаг завершения записи. Другой ожидает выставления флага, а потом читает из буфера.
1 .equ BUFFER_SIZE, 32
2
3 .data
4 flag: .byte 0
5 buffer: .zero 32
6
7
8 .section .text._start
9 .global _start
10 _start:
11 csrr t0,mhartid
12
13 beqz t0,hart0_entry
14 bnez t0,hart1_entry
15
16 _main_wait:
17 j _hart0_wait
18
19
20 hart0_entry:
21 # Записываем данные в буфер
22 la t0,buffer
23 li t1,BUFFER_SIZE
24 _buffer_write:
25 sb t1,0(t0)
26 addi t0,t0,1
27 addi t1,t1,-1
28 bnez t1,_buffer_write
29
30 # fence rw,rw
31
32 # Устанавливаем флаг окончания записи в буфер
33 la t2,flag
34 li t3,1
35 sb t3,0(t2)
36
37 _hart0_wait:
38 j _hart0_wait
39
40
41 hart1_entry:
42 # Ждём флага окончания записи
43 la t0,flag
44 _flag_wait:
45 lb t1,0(t0)
46 beqz t1,_flag_wait
47
48 # fence rw,rw
49
50 # Читаем из буфера
51 la t2,buffer
52 li t3,BUFFER_SIZE
53 _buffer_read:
54 lb t4,0(t2)
55 addi t2,t2,1
56 addi t3,t3,-1
57 bnez t3,_buffer_read
58
59 _hart1_wait:
60 j _hart1_wait
В этом коде блоки инструкции на строках 22-28 и строках 33-35 не зависят ни по данным, ни по регистрам, аналогично со строками 43-46 и 51-57, поэтому процессор или подсистема памяти может переупорядочить операции так, что другой hart увидит обновление flag раньше, чем завершится запись в buffer. В итоге программа работает не так, как ожидается. Но если раскомментировать строки с fence rw,rw, то все операции чтения/записи в 22-28 будут выполнены до 33-35, а 43-46 до 51-57 и поведение программы будет соответствовать ожидаемому.
Я его сейчас запустил на spike — проверить алгоритм, однако воспроизвести такой баг даже на реальном железе нелегко, а эмуляторов, способных на такое, вероятно, не существует. Но для общего представления можно разобрать пример теоретически. Вещь-то очень важная в больших чипах на самом деле. В операционках встречается повсеместно. В linux через макрос mb() задействуется. Да даже если с riscv-tests человек начнет работать, сразу с ней столкнется.
