2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему На страницу Пред.  1 ... 5, 6, 7, 8, 9  След.

Нужна ли такая тема про ассемблер?
Опрос закончился 24.01.2024, 03:22
Да, почитаю. 50%  50%  [ 12 ]
Да, поспрашиваю. 25%  25%  [ 6 ]
Да, поотвечаю. 4%  4%  [ 1 ]
Мне всё равно, но не против, дерзайте. 17%  17%  [ 4 ]
Нет, не интересно, полно готовой литературы. 0%  0%  [ 0 ]
Нет, ничего в этом не понимаю и не собираюсь. 0%  0%  [ 0 ]
Нет, форум не про это, есть другие более подходящие. 4%  4%  [ 1 ]
Другое ... 0%  0%  [ 0 ]
Всего голосов : 24
 
 Re: Первые и последующие шаги в ассемблере x86/x64
Сообщение22.01.2024, 16:19 
Заслуженный участник


20/08/14
11177
Россия, Москва
Yadryara в сообщении #1626805 писал(а):
В отладчике посмотреть?
Нет, в описании команды mul, в каких случаях она выставляет флаг CF (бит переноса, C в регистре Флагов EFLAGS).
Yadryara в сообщении #1626805 писал(а):
Поскольку идёт битва за скорость, 1-й способ, видимо, в топку?
Вовсе нет, он вообще-то самый правильный: разные условия (выход за границы таблицы и превышение проверяемого числа) проверяются разными командами. Всегда сначала стараемся написать правильный код, хотя бы для получения образцового вывода, и лишь потом его ускоряем сравнивая с образцом. А скорости всех трёх способов практически идентичны.
Кроме того, для ускорения мы и вовсе откажемся от этого mul и применим другой способ проверки на превышение тестируемого числа. Будет ли он быстрее так навскиду не скажу, тестировать надо. Но это не значит что придуманное Вами решение плохое! Оно вон какое ускорение даёт.
Yadryara в сообщении #1626805 писал(а):
Как Вам будет угодно.
Не хотелось бы гнать дальше не усвоив работу основных команд (особенно комбинаций сравнений с условными переходами) и вызова процедур (хотя бы на уровне вызова Disp, как её параметры читаются внутри процедуры), иначе такое половинчатое понимание так и будет усугубляться.
Разве что Вы продолжите сами параллельно разбираться (и задавать вопросы если что долго остаётся непонятным), а я потихоньку продолжу только про самые простые (но на мой взгляд интересные) вещи.
Yadryara в сообщении #1626805 писал(а):
В отладчике посмотреть? Я его боюсь :-) Думаю, может мне к более простому коду вернуться, к Фибоначчи и его посмотреть в отладчике.
Для тренировки с отладчиком - да, разумеется, та программа намного проще и понятнее. И до первого invoke (который wsprintf и тоже надо поменять на cinvoke) там никакого лишнего кода FASM не вставляет, прямо что в исходнике, то и в коде. Да и цикл короткий (45 или даже 5 итераций).
А так отладчик лишь кажется сложным, на непонятное просто не обращать внимания, я тоже использую хорошо если 5% его возможностей (а описал выше наверное 1-2%).

(Оффтоп)

У Вас в профиле на OEIS указано что Вы программист (не важно на чём именно), с чего тогда такая боязнь непонятного, Alt-F4 или Ctrl-C в консоли или крест справа вверху спасают почти от всего, :mrgreen: да и основные структуры управления и данных должны знать, и по истории многих вопросов и так в курсе. Видел бы раньше - не стал бы так распинаться, может по другому что-то объяснил бы.

 Профиль  
                  
 
 Re: Первые и последующие шаги в ассемблере x86/x64
Сообщение22.01.2024, 18:26 
Аватара пользователя


29/04/13
7227
Богородский
Dmitriy40, сейчас что мне нужно делать? Самому написать хоть какой-то вариант исправления и отладить?

(Профиль)

Dmitriy40 в сообщении #1626808 писал(а):
У Вас в профиле на OEIS указано что Вы программист

А что мне там надо писать, что я обыгрыватель рулеток? Это конечно намного круче, но... :-)


Что же касается опаски слишком сильно разжевать материал, то я совершенно не против. Всё равно, как видим, сложностей полно.

vpb сказал
vpb в сообщении #1626240 писал(а):
для полных дятлов

Значит для полных дятлов :-)

 Профиль  
                  
 
 Re: Первые и последующие шаги в ассемблере x86/x64
Сообщение22.01.2024, 18:52 
Заслуженный участник


20/08/14
11177
Россия, Москва
Yadryara в сообщении #1626817 писал(а):
сейчас что мне нужно делать? Самому написать хоть какой-то вариант исправления и отладить?
Ну да, хотелось бы. Чтобы получить опыт, личный, не с моих слов. И неважно какой именно вариант, какой понравится или покажется проще.
Но это не экзамен, если никак не получается - спрашивайте.

Отладчик для этого удобен (когда хоть немного приноровился им пользоваться, как я), но не обязателен, все варианты достаточно просты чтобы в голове прокрутить как и когда они будут заканчивать вычисления. И проверить тестами (понаставив туда вызовов Disp например, от ESI и ещё каких-нибудь).
Вот для тренировки именно навыков отладки и понимания реакции процессора (в регистрах) на отдельные команды - да, лучше взять прогу попроще, например первую Фибоначчи. Но нужно ли это Вам именно сейчас - Вам решать. Я отладчик запускаю когда в упор не вижу глюка в 5-10 командах подряд, вот вроде уже 100500 раз проверил в уме и всё должно работать, ан нет. Прохожу их по шагам, внимательно изучая что как меняется в регистрах, и раза с 2-3 замечаю чёрти что в регистрах и вдруг осеняет какой я идиот. Часто это быстрее чем изучать вывод от многих Disp из разных мест. А иногда наоборот, лог намного понятнее. Так что умение пользоваться отладчиком - не первостепенное и даже не обязательное, и тема не про него, а про асм. А вот понимания работы команд хотелось бы. Просто в отладчике это проще увидеть прямо глазками, было в регистрах то-то, F8 (выполнить одну команду или всю функцию) и стало то-то.

Кстати, я вовсе не случайно в Disp поставил hErr вместо hOut - это позволяет при запуске направить весь вывод от Disp в другое место, например в файл, добавив к строке запуска через пробел 2>prog3.log. И потом спокойно с ним разбираться, хоть там миллион строк будет.

 Профиль  
                  
 
 Re: Первые и последующие шаги в ассемблере x86/x64
Сообщение22.01.2024, 19:06 
Аватара пользователя


29/04/13
7227
Богородский
Dmitriy40 в сообщении #1626818 писал(а):
Чтобы получить опыт, личный, не с моих слов. И неважно какой именно вариант, какой понравится или покажется проще.

Выбрал вроде самый простой: заменил cmp EAX, ECX на cmp EDX,1

Ваша проверка сработала!

 Профиль  
                  
 
 Re: Первые и последующие шаги в ассемблере x86/x64
Сообщение22.01.2024, 19:10 
Заслуженный участник


20/08/14
11177
Россия, Москва
Yadryara
А покажите кусочек кода с метки .next до .prime, куда и добавили код? Не вполне уверен что правильно вас понял.

 Профиль  
                  
 
 Re: Первые и последующие шаги в ассемблере x86/x64
Сообщение22.01.2024, 19:16 
Аватара пользователя


29/04/13
7227
Богородский
Используется синтаксис ASM
.next:          inc     ESI
                lea     EAX,[ESI*2]          
                mul     EAX
                cmp     EDX,1    
                jnc     .yes                    ;Ни на что не разделилось, значит простое
.prime:         bt      [primes],ESI            ;Проверим простое ли число в ESI
 

 Профиль  
                  
 
 Re: Первые и последующие шаги в ассемблере x86/x64
Сообщение22.01.2024, 19:22 
Заслуженный участник


20/08/14
11177
Россия, Москва
Yadryara
Да, как и подумал.
Код правильный.
Только он перестал давать ускорение в 17 раз - проверьте! Хотя бы для 1-2-3 миллионов первым аргументом чтобы не ждать долго.
Ведь выход произойдёт строго при ESI=0x8000 (достижении конца таблицы) и не раньше. А это делалось и проще - убранной командой cmp.
Т.е. надо всё же ДВА условия выхода, и по EAX (досрочно) и либо по EDX (или биту C, CF после mul) либо вернуть по ESI - эти оба по концу таблицы. Причём в правильном порядке.

-- 22.01.2024, 19:27 --

Стирать/удалять строки (как тут с cmp) не слишком удобно, лучше их комментировать символом ";" слева - после него FASM пропускает всё до конца строки. Заработает как хотелось - можно будет и удалить всё лишнее.

 Профиль  
                  
 
 Re: Первые и последующие шаги в ассемблере x86/x64
Сообщение22.01.2024, 19:42 
Аватара пользователя


29/04/13
7227
Богородский
Ещё вариант

Используется синтаксис ASM
.next:          inc     ESI
                lea     EAX,[ESI*2]          
                mul     EAX
                cmp     EAX,ECX
                jnc     .yes                    ;Ни на что не разделилось, значит простое
                cmp     EDX,1    
                jnc     .yes                    ;Ни на что не разделилось, значит простое
.prime:         bt      [primes],ESI            ;Проверим простое ли число в ESI
 


Ускорение в те же самые 17 раз.

 Профиль  
                  
 
 Re: Первые и последующие шаги в ассемблере x86/x64
Сообщение22.01.2024, 19:49 
Заслуженный участник


20/08/14
11177
Россия, Москва
Yadryara
ОК, теперь и ускорение есть, и тесты проходит.

-- 22.01.2024, 20:26 --

Тут есть тонкость: вообще говоря по идее проверять надо сначала EDX (или CF), а уж потом EAX. Ведь фактически это сравнение 64-битного числа в EDX:EAX с другим числом (0:ECX), а оное выполняется со старших битов (мы же знаем что например 91>87, хотя по младшим цифрам должно быть наоборот).
Но при размере таблицы до 65536 (как сейчас) и проверке только 32-разрядных чисел (которые все меньше квадрата размера таблицы) нет контрпримера чтобы показать когда это важно. И потому пока работает и так тоже. Где-то же в будущем можно попасть на глюки.

И для справки, проверить EDX на 0 можно разными способами кроме показанного:
код: [ скачать ] [ спрятать ]
Используется синтаксис ASM
        cmp     EDX,0   ;Изменит и перенос CF=0
        je      .yes
;Или
        test    EDX,EDX
        jz      .yes
;Или
        or      EDX,EDX ;Или and
        jz      .yes
;Или
        add     EDX,0   ;Или sub
        jz      .yes
;Или
        sub     EDX,1   ;Если был 0, то возникнет заём=перенос CF=1, а EDX нам больше не нужен
        jc      .yes
;Или
        add     EDX,0xFFFFFFFF  ;Это -1, можно было и её написать, перенос НЕ возникнет только если был 0
        jnc     .yes
;Наверное есть и более вычурные способы
Специально пользуюсь и je и jz, хоть для процессора они и в точности одинаковы, но программист может намекнуть себе любимому проверяет он на ноль (jz) или равенство (je).
Все эти способы должны быть уже понятны, надеюсь.
Обычно применяются первые два.

 Профиль  
                  
 
 Re: Первые и последующие шаги в ассемблере x86/x64
Сообщение22.01.2024, 21:39 
Заслуженный участник


20/08/14
11177
Россия, Москва
Давайте покажу все три варианта проверки, о которых упомянул.
Исходный кусок:
Используется синтаксис ASM
.next:          inc     ESI
                cmp     ESI,max_prime/2
                jnc     .yes                    ;Ни на что не разделилось, значит простое
.prime:         bt      [primes],ESI            ;Проверим простое ли число в ESI
Замер скорости:
Используется синтаксис Text
C:\>prog3.exe 10000000
664579 primes up to 10000000
time: 93.274s

Первый вариант, лишь добавляем досрочный выход:
Используется синтаксис ASM
.next:          inc     ESI
                cmp     ESI,max_prime/2
                jnc     .yes                    ;Ни на что не разделилось, значит простое
        lea     EAX,[ESI*2]
        mul     EAX
        cmp     EAX,ECX
        jnc     .yes
.prime:         bt      [primes],ESI            ;Проверим простое ли число в ESI
Удобно когда две табуляции слева, можно новый (или временный) код писать левее и его сразу видно.
Исходная проверка cmp осталась. Запускаем:
Используется синтаксис Text
C:\>prog3.exe 10000000
664579 primes up to 10000000
time: 3.636s
Количество правильное, ускорение 93.274/3.636=26х присутствует. Как-то много. Перепроверим:
Используется синтаксис Text
C:\>for /l %n in (1,1,10) do @prog3.exe 10000000 |find "time"
time: 3.636s
time: 3.605s
time: 3.620s
time: 3.636s
time: 3.620s
time: 3.651s
time: 3.635s
time: 3.636s
time: 3.730s
time: 3.667s
Разброс 3.730с-3.605с=0.125с или 3.4%, немало, значит всё что меньше 5% - погрешность или флуктуации, запомним. Но ускорение около 25х таки есть.

Второй вариант, без cmp, но с проверкой EDX, почти как Yadryara (все отличия намеренные):
Используется синтаксис ASM
.next:          inc     ESI
        ;       cmp     ESI,max_prime/2
        ;       jnc     .yes                    ;Ни на что не разделилось, значит простое
        lea     EAX,[ESI*2]
        mul     EAX
        test    EDX,EDX
        jnz     .yes
        cmp     EAX,ECX
        jnc     .yes
.prime:         bt      [primes],ESI            ;Проверим простое ли число в ESI
Запуск и скорость:
код: [ скачать ] [ спрятать ]
Используется синтаксис Text
C:\>prog3.exe 10000000
664579 primes up to 10000000
time: 3.027s

C:\>for /l %n in (1,1,10) do @prog3.exe 10000000 |find "time"
time: 3.027s
time: 3.059s
time: 3.012s
time: 3.043s
time: 3.074s
time: 2.996s
time: 3.012s
time: 3.012s
time: 3.058s
time: 3.012s
Количество правильное, скорость достоверно выше, разница 3.2/3.6=90% (помним про погрешность до 5%).

И третий вариант, без cmp и анализа EDX:
Используется синтаксис ASM
.next:          inc     ESI
        ;       cmp     ESI,max_prime/2
        ;       jnc     .yes                    ;Ни на что не разделилось, значит простое
        lea     EAX,[ESI*2]
        mul     EAX
        jc      .yes
        ;test   EDX,EDX
        ;jnz    .yes
        cmp     EAX,ECX
        jnc     .yes
.prime:         bt      [primes],ESI            ;Проверим простое ли число в ESI
Запуск и скорость:
код: [ скачать ] [ спрятать ]
Используется синтаксис Text
C:\>prog3.exe 10000000
664579 primes up to 10000000
time: 3.043s

C:\>for /l %n in (1,1,10) do @prog3.exe 10000000 |find "time"
time: 3.059s
time: 3.059s
time: 3.059s
time: 3.090s
time: 3.059s
time: 3.059s
time: 3.074s
time: 3.059s
time: 3.058s
time: 3.074s
Количество правильное, скорость не отличается от второго варианта, возможно на процент-два медленнее.

Выигрывает вариант Yadryara! Замена cmp на test и перестановка сравнений местами на скорость не влияет.

 Профиль  
                  
 
 Re: Первые и последующие шаги в ассемблере x86/x64
Сообщение22.01.2024, 23:09 
Заслуженный участник


20/08/14
11177
Россия, Москва
А теперь подумаем как можно ещё ускорить программу. И посмотрим насколько.

Первая достаточно простая идея: а зачем нам проверять умножением все нечётные числа? Цикл inc, cmp, jnc, bt, jnc по метке .next выполняется достаточно быстро, ну прощёлкает он до следующего простого, вот его и проверим умножением на превышение ECX лишь перед выполнением деления, для чисел около 4млрд простым является примерно каждое 22-е число, т.е. в среднем цикл пропуска составных делителей прощелкает лишь 11 раз, зато для таких чисел мы не будем делать почти 25 тысяч умножений (32768 нечётных в таблице минус 6540 простых среди них). Берём код из первого варианта и переставляем его чуть ниже, перед jmp .check:
Используется синтаксис ASM
.next:          inc     ESI
                cmp     ESI,max_prime/2
                jnc     .yes                    ;Ни на что не разделилось, значит простое
.prime:         bt      [primes],ESI            ;Проверим простое ли число в ESI
                jnc     .next                   ;Если не простое, то пропустим деление
        lea     EAX,[ESI*2]
        mul     EAX
        cmp     EAX,ECX
        jnc     .yes
                jmp     .check                  ;Если простое, то попробуем на него поделить
Запускаем, измеряем:
код: [ скачать ] [ спрятать ]
Используется синтаксис Text
C:\>prog3.exe 10000000
664579 primes up to 10000000
time: 2.809s

C:\>for /l %n in (1,1,10) do @prog3.exe 10000000 |find "time"
time: 2.903s
time: 2.840s
time: 2.825s
time: 2.824s
time: 2.809s
time: 2.856s
time: 2.856s
time: 2.840s
time: 2.840s
time: 2.840s
Получаем достоверно чуть быстрее, от второго варианта (он кстати был не 3.2, а 3.02с, это не 90%, а скорее 83% от первого) 2.85/3.02=95%. Выигрыш 5%. Как-то немного.
От cmp (как во втором и третьем вариантах) тут отказаться нельзя, bt стоит выше и полезет за границу таблицы.

Думаем дальше. Зачем нам что-то возводить в квадрат если мы и так делим наше проверяемое число на простой делитель? Это же фактически обратная операция к квадрату. И если частное стало меньше делителя, значит квадрат делителя превысил делимое. Т.е. достаточно что-то проверить после div. При этом будет одно лишнее деление, но малые простые и так быстро проверяются, а для больших какая разница, 6540 или 6541 деление. Частное у нас в EAX, и делитель в EBX, нас интересует превышение делителя над частным, вычитаем делитель из частного и смотрим на перенос (изменение самого исходного кода, без всяких добавлений):
Используется синтаксис ASM
.check:         mov     EDX,0
                mov     EAX,ECX                 ;Проверяемое число
                lea     EBX,[ESI*2+1]           ;Делитель соответствующий биту ESI в таблице
                div     EBX
        cmp     EAX,EBX
        jc      .yes
                mov     EAX,0
                test    EDX,EDX
                jz      .ret                    ;Разделилось без остатка, значит точно не простое
Запускаем, измеряем:
код: [ скачать ] [ спрятать ]
Используется синтаксис Text
C:\>prog3.exe 10000000
664579 primes up to 10000000
time: 2.793s

C:\>for /l %n in (1,1,10) do @prog3.exe 10000000 |find "time"
time: 2.778s
time: 2.809s
time: 2.794s
time: 2.794s
time: 2.794s
time: 2.825s
time: 2.809s
time: 2.793s
time: 2.794s
time: 2.809s
Количество правильное, а где собственно ускорение от отказа от умножений-то? Подождём подольше:
Используется синтаксис Text
C:\>prog3.exe 100000000
5761455 primes up to 100000000
time: 77.315s
...
C:\>prog3.exe 100000000
5761455 primes up to 100000000
time: 73.852s
Разница 5%, ну хоть как-то заметна.
Выходит что если уж выполняем деление, то одно лишнее умножение роли почти не играет. То ли деление столь медленное, то ли умножение столь быстрое, не понять.

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

 Профиль  
                  
 
 Re: Первые и последующие шаги в ассемблере x86/x64
Сообщение23.01.2024, 00:45 
Заслуженный участник


20/08/14
11177
Россия, Москва
Посмотрим на начало нашей isprime:
Используется синтаксис ASM
                mov     ECX,[num]               ;Проверяемое число
                mov     EAX,0                   ;Флаг что число не простое
                cmp     ECX,2
                jc      .ret                    ;Числа меньше 2 все не простые
                cmp     ECX,4
                jc      .yes                    ;Числа 2 и 3 простые
                test    ECX,1
                jz      .ret                    ;Остальные чётные числа все не простые
                cmp     ECX,max_prime
                jc      .low                    ;Число меньше размера таблицы простых, возьмём бит простоты напрямую
Вот зачем здесь cmp ECX,4? Раз дальше будет проверка на чётность нам надо её обойти для числа 2, но сравнение с двойкой уже есть выше! Да, там стоит jc для 0 и 1, но ведь cmp выдаёт и флаг нуля (ZF), кто нам мешает его проверить и не сравнивать с 4? Никто. А что будет с тройкой? А она как нечётная меньшая размера таблицы возьмётся напрямую из последней. Да и 0 с 1 тоже можно не проверять командой jc .ret, им в таблице всё равно стоит 0 как не простым и можно смело лезть в таблицу. Больше ничего не изменится. Числа 0, 1, 3 будут проверяться на наносекунду (реально нс, не гипербола) дольше - да и пофиг.
Используется синтаксис ASM
                mov     ECX,[num]               ;Проверяемое число
                mov     EAX,0                   ;Флаг что число не простое
                cmp     ECX,2
                je      .yes                    ;Число 2 простое
                test    ECX,1
                jz      .ret                    ;Остальные чётные числа все не простые
                cmp     ECX,max_prime
                jc      .low                    ;Число меньше размера таблицы простых, возьмём бит простоты напрямую
Скорость замерять не буду, очевидно она не изменилась от одной команды.

Это пример что после одного сравнения можно уйти не только по ДА/НЕТ (истина/ложь), но и в большее количество путей. В каком-то языке (и процессоре) даже есть специальный оператор if с тремя альтернативами после сравнения: меньше, равно, больше. Не считая команду ftst в FPU, она всё же не ветвится, лишь флаги выдаёт. А мы выходит легко можем двумя командами сделать тройное ветвление (три альтернативы). А можем и больше ... Если условия придумаем.
UPD. Вот ведь, так заоптимизировался что потерял пример. В этом абзаце речь про пару команд подряд после cmp ECX,2:
Используется синтаксис ASM
                jc .ret
                je .yes
Первую из которых "оптимизировал" удалением.


А теперь вернёмся к главной программе и посмотрим на вот этот цикл подсчёта количества простых в ней:
Используется синтаксис ASM
                mov     EDX,EAX
                mov     ECX,0                   ;Количество простых по указанное число
                mov     EBX,0                   ;Текущее проверяемое число
.count:         stdcall isprime, EBX            ;Проверим на простоту
                add     ECX,EAX
                inc     EBX
                cmp     EBX,EDX
                jbe     .count
Зачем мы перебираем чётные? Мы же знаем что они все составные кроме 2. А ведь на каждое из них тратится время, вызов функции, штук 10 команд в ней, возврат, здесь вон 4 команды. Давайте начнём с числа 3, а двойку посчитаем "в уме", мы же уже отладили isprime, она двойку считает правильно:
Используется синтаксис ASM
                mov     EDX,EAX
                mov     ECX,1                   ;Количество простых по указанное число
                mov     EBX,3                   ;Текущее проверяемое число
.count:         stdcall isprime, EBX            ;Проверим на простоту
                add     ECX,EAX
                add     EBX,2
                cmp     EBX,EDX
                jbe     .count
Запускаем:
Используется синтаксис Text
C:\>prog3.exe 100000000
5761455 primes up to 100000000
time: 75.334s
Количество правильное, но время, как так? Мы же ускоряли программу, куча команд (половина вызовов функции) перестала выполняться - и вдруг медленнее?! Ладно, пусть не вдвое быстрее (половину же чисел не проверяем), понятно что все чётные проверяются почти моментально и на общее время влияют не сильно, но чтобы вообще медленнее ...
А вот и так бывает. Честно скажу, понятия не имею почему медленнее. Обычно такие странности случаются из-за предсказателя переходов в процессоре, когда при изменении программы условные переходы становятся или более случайными, или какой-то переход перестаёт быть намного более вероятным (чаще срабатывает чем нет). Но здесь вроде бы ничего такого нет. А скорость падает. Незначительно, но ожидали то рост. Возможно это всего лишь такие флуктуации, но замерять точнее лень.
Потому так важны реальные тесты, практика нередко не сходится с теорией (видимо из-за недостаточного знания последней).

В последнем варианте одна мелкая засада:
Используется синтаксис Text
C:\>for /l %n in (1,1,10) do @prog3.exe %n |find "up"
2 primes up to 1
2 primes up to 2
2 primes up to 3
2 primes up to 4
3 primes up to 5
3 primes up to 6
4 primes up to 7
4 primes up to 8
4 primes up to 9
4 primes up to 10
Первые две строки неправильные. Т.е. для не проверяемых 1 и 2. Можно "грубой силой" заставить показывать правильно, а можно вернуть проверку двойки (а до неё простых нет) и заодно ещё один финт:
Используется синтаксис ASM
                mov     EDX,EAX
                mov     ECX,0                   ;Количество простых по указанное число
                mov     EBX,2                   ;Текущее проверяемое число
                cmp     EDX,EBX
                jb      .disp1
.count:         stdcall isprime, EBX            ;Проверим на простоту
                add     ECX,EAX
                inc     EBX
                jz      .disp1
                or      EBX,1
                cmp     EBX,EDX
                jbe     .count
.disp1:         cinvoke wsprintf, buf, .fmt3, ECX, EDX
Финт в команде or EBX,1 после увеличения EBX на 1. Как мы помним 0 в младшем бите только у чётных чисел, если после увеличения полчился ноль, т.е. чётное, то or уствновит младший бит в 1 и превратит число в нечётное на 1 больше. А если вдруг после увеличения младший бит уже 1, то 1 or 1 = 1 и ничего неправиьлного or не сделает. Т.е. числа будут перебираться только по нечётным за исключением самого первого, которое двойка.
Время практически то же. Тесты оставляю читателям.

Ещё здесь добавлен переход jz .disp1 - зачем? А вот предположим найдётся маньяк пожелавший узнать сколько таки простых по $2^{32}-1$, вот запустит он программу без этой команды с числом 4294967295 (0xFFFFFFFF), подождёт час, два, три, когда-нибудь inc EBX (с or или без, всё равно) досчитает до этого числа, cmp EBX,EDX выдаст флаг равенства, jbe пойдёт обратно на цикл, это число проверится isprime, снова дойдёт до inc EBX и выдаст ... 0! Ну или 1 с or. Команда cmp невозмутимо снова выдаст C=1, jbe обрадуется и снова на цикл. И снова, и снова ... до посинения. :facepalm: Потому если уж зашли за границу чисел, то командой jz (напомню, inc не выдаёт флаг переноса CF который сюда просится вместе с jc) выйдем из замкнутого круга и покажем что насчитали до границы. Проверить оставляю желающим, мне дважды по несколько часов жаль.


На этом собственно про хитрости всё сказал что хотел и вспомнил.
Прежде чем переходить к тесту Ферма жду вопросов и предложений.
Например придумать какую-нибудь промежуточную по сложности программу между Фибоначчи и этим тестом на простоту выше. Для тренировки. Или освоиться с отладчиком. Или про отличия в x64 рассказать (но это удобно после теста Ферма, он хорошо ложится на числа до 2^64). Или рассказать об возможном ускорении написанной isprime заменой части делений на меньшее количество или вообще на умножения (причём это пригодится и в тесте Ферма). Или сунуться краем носа в модулярную арифметику (с расчётом выйти на поиск простых по паттерну). Или ещё что.

 Профиль  
                  
 
 Re: Первые и последующие шаги в ассемблере x86/x64
Сообщение23.01.2024, 08:25 
Аватара пользователя


29/04/13
7227
Богородский
Постепенно буду разбираться с этим огромным морем команд. Может сам попробую написать простенький код арифмоста.

Dmitriy40 в сообщении #1626860 писал(а):
Или сунуться краем носа в модулярную арифметику (с расчётом выйти на поиск простых по паттерну).

Голосую за этот пункт. А спецов по теории чисел на форуме немало. Может и заглянут сюда. Кто разбирается в КТО ?

nnosipov, VAL, scwec, Shadow, Andrey A... Может как раз vpb разбирается.

 Профиль  
                  
 
 Re: Первые и последующие шаги в ассемблере x86/x64
Сообщение23.01.2024, 10:49 
Заслуженный участник


20/08/14
11177
Россия, Москва
Да с КТО всё просто: если есть два массив остатков x[], y[] по двум разным взаимнопростым модулям Mx и My соответственно, то общий массив z[] по модулю Mz=Mx*My, размером равным произведению размеров x[] и y[], вычисляется как z[]=x[]+Mx*(((y[]-x[])*d)%My). Здесь d - обратный элемент к Mx по модулю My, в PARI вычисляется как Mod(1/Mx, My). Операция % это разумеется взятие остатка.
Для двух массивов - вся нужная КТО.
Для трёх массивов не сильно сложнее, но замороченнее, будет третье слагаемое с вычитанием третьего массива уже из z[] (вроде бы, главное не из x[] или y[]). Её можно построить итерационно, заменив в сумме выше один из массивов и модулей на z[] и Mz.
Как видно d зависит только от Mx и My и его достаточно вычислить один раз, а потом просто перебирать y[] и вычислять кусочек z[], потом меняем x[] и снова перебираем y[].
Причём если множитель справа чуть подраскрыть, то там (x[]*d)%My тоже можно вычислить лишь один раз для всего y[] (или вообще хранить). А имея вместо y[] сразу (y[]*d)%My их можно тупо вычитать по модулю, что делается буквально тремя быстрыми командами без умножений и делений (правда пока My влезает в регистр и лучше без знакового(старшего) бита, т.е. в 31/63 бит).
Вот как это вытащить из под Mx не совсем понятно, но умножение это не деление, не так страшно (тоже пока Mx влезает в регистр, иначе неприятно).

У меня проблема с КТО в другом: мне надо очень быстро вычислять не просто вектор z[], а матрицу z[]%p[] для хотя бы пары-тройки десятков простых p[] для каждого элемента z[]. И как вот это делать без делений (а лучше и без умножений) и имея лишь нечто типа матриц x[]%p[] и y[]%p[] - совсем непонятно.

 Профиль  
                  
 
 Re: Первые и последующие шаги в ассемблере x86/x64
Сообщение23.01.2024, 17:03 
Заслуженный участник


18/01/15
3104
Yadryara в сообщении #1626867 писал(а):
Может как раз vpb разбирается.
Э ... скажем так. Коли уж меня позвали, то я на днях как-нибудь сосредоточусь, попробую вникнуть, о чем тут речь идет (в смысле арифметики, а не программирования), а сейчас, прошу извинить, голова опухшая.

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 133 ]  На страницу Пред.  1 ... 5, 6, 7, 8, 9  След.

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group