Принимая исключение очень медленно ;массив должен быть огромный , чтобы амортизировать дополнительные затраты на выход из цикла из-за сбоя, даже если вы можете сохранить инструкцию внутри цикла.
То, о чем вы, вероятно, читали, так это Проверка границ массива на 64-битном оборудовании с использованием аппаратной защиты памяти : завершение массива в конце виртуальной страницы и оставление следующей страницы без отображения . Виртуальная машина перехватывает SIGSEGV и доставляет исключение для гостевого кода, если когда-либо возникает исключение за пределами допустимого. Это устраняет необходимость проверки границ для случайных обращений, а не в цикле.
Этот метод вызывает только недопустимую ошибку страницы (segfault), когдагостевой код фактически принимает исключение границ массива, не для обычного случая выхода из цикла над массивом.
Давайте посмотрим, как ваша идея будет (не)работа:
Я думаю, что вы говорите о MIPS, где есть инструкция addi
, которая перехватывает переполнение со знаком. (Вместо обычного addiu
, который делает сложение без условной ловушки).
Большинство других ISA не имеют эффективного способа вызвать сбой при подписанном переполнении. x86 имеет into
(прерывание OF установлено), но это отдельная инструкция от add
, которая может устанавливать флаги. Вы могли бы также использовать условную ветвь вместо того, чтобы вызывать исключение и перехватывать его. Или просто используйте dec ecx / jnz
в качестве счетчика цикла или подсчитайте отрицательный индекс до нуля и индекс с конца массива.
Я думаю, что MIPS потребует дополнительных addi
внутри цикл для выделенного счетчика: единственный режим адресации MIPS reg+16_bit_constant
.
Так что, если вы хотите перебрать массив, вам нужно увеличить указатель, иначе вам понадобится дополнительный add tmp, base, index
внутриloop.
Цикл нуждается в инструкции перехода или перехода внизу, и это может быть условная ветвь без каких-либо дополнительных затрат . Таким образом, в MIPS вы вычисляете конец массива вне цикла, а затем пишете обычный цикл, например
addu $t5, $t0, $t4 ; t5 = int *endp = start + byte_length
.loop: ; do{
addiu $t0, $t0, 4 ; p++
lw $t1, ($t0) ; *p
...
bne $t0, $t5, .loop ; }while(p != endp)
Внутри цикла нет потраченных впустую инструкций. Любую проверку границ массива можно выполнить вне цикла, проверив, что endp
не более одного конца конца массива.
(В современных x86, cmp/jne
работает аналогично, декодирование в одном мопе сравнения и ветвления. Многие другие ISA имеют команду декремента и ветвления или аналогичную инструкцию, которая позволяет условиям цикла счетчика иметь только одну инструкцию служебных данных.)
i < array.length
поскольку условие цикла - это не просто проверка границ массива.
Как я уже говорил выше, в простом цикле, который повторяет arr[i]
, вы поднимаете проверку границ из цикла, чтобы поддерживать его таким образом. .