LINUX.ORG.RU

segv при элементарном обходе массива

 ,


0

2

Здарова. Ниже - решение, которое вызывает segv по непонятной причине. пробовал заменить loopneq на loop с ручным выходом, но segv никуда не исчез (loopq вместо loopneq и je exit под cmoveq EXIT_SUCCESS). задача - найти val в arr

.equ QLEN, 8 # размер quadword

.equ SYS_EXIT, 60 # номер выхода

.equ EXIT_FAILURE, 0 # код "не найден"
.equ EXIT_SUCCESS, 1 # код "найден"

.section .data
val: .quad 9 # значение, которое ищем в массиве

len: .quad 5 # длина массива
arr: .quad 4, 9, 3, 8, 4 # массив

.section .text
.globl _start

_start:
  movq val, %rax # грузим val, который ищем в массиве
  movq EXIT_FAILURE, %rdi # грузим код "не найден"

  movq len, %rcx # грузим длину массива
  cmpq $0, %rcx # проверяем на наличие элементов
  je exit # завершаем при отсутствии элементов
loop:
  # итерация с последнего элемента по первый
  movq arr-QLEN(,%rcx,QLEN), %rdx # arr - 8 + rcx * 8 (первая итерация - последний элемент)

  cmpq %rdx, %rax # сравниваем загруженный элемент с val
  cmoveq EXIT_SUCCESS, %rdi # если равен, грузим код успеха

  loopneq loop # если не равен, итерируем (декремент rcx, итерация. если rcx == 1, итерация превратит его в 0 и не прыгнет)
exit:
  movq SYS_EXIT, %rax # грузим код выхода
  syscall # выходим

c чем связан segv? спс


вызывает segv по непонятной причине

Абсолютная адресация в 64битном режиме может вычислять некорректный арес, так как компоновщик размещает дату в произвеольном мете витуальной памяти. Что если использовать относительную?

.equ QLEN, 8

.equ SYS_EXIT, 60
.equ EXIT_FAILURE, 0
.equ EXIT_SUCCESS, 1

.section .data
.align 8
val: .quad 9
len: .quad 5
arr: .quad 4, 9, 3, 8, 4

.section .text
.globl _start
.align 16

_start:
  movq val(%rip), %rax
  movq $EXIT_FAILURE, %rdi
  movq len(%rip), %rcx
  cmpq $0, %rcx
  je exit

  leaq arr(%rip), %rsi

loop:
  movq -QLEN(%rsi,%rcx,QLEN), %rdx
  cmpq %rdx, %rax
  movq $EXIT_SUCCESS, %rdx
  cmoveq %rdx, %rdi
  loop loop

exit:
  movq $SYS_EXIT, %rax
  syscall
LightDiver ★★★★★
()
Ответ на: комментарий от P0085

Из забавного, я сделал такой же код на си - он в 0.4 раза быстрее ассемблерного. Хм, может я криво написал, оченьм мало с ним работаю. Примерно такой же кд на луа всего в 2 раза медленнее, что вообще поражает.

А теперь самое интересное: я сделал такой же код на раст и сишный медленнее в полтора раза.

Ладно, надо больше спать. Что я вообще творю.

LightDiver ★★★★★
()
Последнее исправление: LightDiver (всего исправлений: 1)
Ответ на: комментарий от LightDiver

надо смотреть на дисассембл, мож там другие инструкции. если as и gcc имеют разный уровень оптимизации (например, c может заменить весь цикл на exit(1) :)). насчёт rust и lua хз, но подобное возможно и там (или пляски с линкером в рантайм). спать то да. не спать не получится

P0085
() автор топика
Ответ на: комментарий от P0085

Гланый тормоз - loopne. Я сейчас попробовал dec/jnz - в 4.6 раз быстрее. Все еще медленнее раста вдвое. Но я хз как глянуть во что превращает код раст. Там какие то еще оптимизации свои.

LightDiver ★★★★★
()
Ответ на: комментарий от LightDiver

в 4.6 раз быстрее. Все еще медленнее

а теперь разверни цикл простой копипастой интсрукций по количеству итераций и замерь еще раз.

в гцц, например, есть -funroll-loops для этого

aol ★★★★★
()
Ответ на: комментарий от aol

Круто, в полтора раза быстрее раста. Но универсльности ноль, читабельность в жопе, размер кода линейно растет.

LightDiver ★★★★★
()
Ответ на: комментарий от cobold

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

Может немного удивлю, хотя врядли. Попробуй такой «бенчарк».

use std::process::exit;

const ITERS: usize = 1_000_000_000;
const ARR: [i64; 5] = [4, 9, 3, 8, 4];
const VAL: i64 = 9;
const LEN: usize = 5;
const EXIT_FAILURE: i32 = 0;
const EXIT_SUCCESS: i32 = 1;

fn main() {
    let mut found = EXIT_FAILURE;
    
    for _ in 0..ITERS {
        found = EXIT_FAILURE;
        let mut i = LEN;
        while i > 0 {
            i -= 1;
            if ARR[i] == VAL {
                found = EXIT_SUCCESS;
                break;
            }
        }
    }
    
    exit(found);
}
LightDiver ★★★★★
()
Последнее исправление: LightDiver (всего исправлений: 2)
Ответ на: комментарий от cobold

Без -O нет смысла смотреть, а чтобы результат не вычислял во время компиляции добавь std::hint::black_box вокруг доступа к массиву.

anonymous
()
Ответ на: комментарий от cobold

А ты выполни сразу как код и посмотри результат.

Чисто формально код тот же по сути. Ну почти.

LightDiver ★★★★★
()
Последнее исправление: LightDiver (всего исправлений: 1)
Ответ на: комментарий от LightDiver

Он там все равно какую-то дичь делает. Казалось бы, хочешь развернуть цикл, так разверни. К чему он этих sse пересылок навтыкал? Есть подозрение что это баг и он лишнего захватил когда тело цикла разворачивал. К тому же он использует сложную адресацию в cmp, на которую нужен +1 такт

cobold ★★★★★
()
Ответ на: комментарий от LightDiver

Вернёмся к оп посту. Как думаешь автор озадачился вопросом потому что у него не мгновенно ищется элемент в массиве из 5 элементов?

cobold ★★★★★
()
Ответ на: комментарий от cobold

Атор на 64битной системе пытался искать в 32битном смещении. В итоге данные попадали за пределы поиска и вылезала ошибка.

Просто мне интересно стало сравнить скорость между языками и вылезло неожиданное. По крайней мере для меня.

LightDiver ★★★★★
()
Последнее исправление: LightDiver (всего исправлений: 1)
  • Markdown
Пустая строка (два раза Enter) начинает новый абзац. Знак '>' в начале абзаца выделяет абзац курсивом цитирования.
Внимание: прочитайте описание разметки Markdown.
Используйте Ctrl-Enter для размещения комментария