|
|
| Посл.отвђт | Сообщенiе |
|
|
Дата: Янв 27, 2004 01:09:25 Доброго времени суток! В асме я новичок, так что не судите строго. Собственно, меня интересуют вопросы оптимизации. Вот три куска кода, которые делают одно и тоже: копируют данные из одного участка памяти в другой. Если можно прокомментируйте код(ну типа того, что: "в esi нежелательно загружать адрес или типа того") _asm { pushad xor ecx, ecx ; нужны ли Эти две команды и вообще, нужно ли очищать регистры, xor ebx, ebx ; если в них будет произведена запись? mov edx, intsize ; = 1000000 байт shr edx, 2 mov esi, p_src ; адрес исходных данных mov eax, p_dest ; адрес, куда будем копировать label1: mov ecx, dword ptr[esi+ebx] mov dword ptr[eax+ebx], ecx add ebx, 4 dec edx test edx, edx ; почему это можно убрать? jnz label1 popad } ----------------- _asm { pushad // сколько тактов тратится на pushad и popad? mov ecx, intsize shr ecx, 2 mov esi, p_src mov edi, p_dest repnz movsd // эта команда очень тормозная? popad } ---------------- _asm { pushad mov ecx, intsize shr ecx, 6 mov esi, p_src mov edi, p_dest label: movq mm0, [esi] movq mm1, [esi+8] movq mm2, [esi+16] movq mm3, [esi+24] movq mm4, [esi+32] movq mm5, [esi+40] movq mm6, [esi+48] movq mm7, [esi+56] movq [edi], mm0 movq [edi+8], mm1 movq [edi+16], mm0 movq [edi+24], mm1 movq [edi+32], mm0 movq [edi+40], mm1 movq [edi+48], mm0 movq [edi+56], mm1 add esi, 64 add edi, 64 dec ecx test ecx, ecx // убрать эту команду? jnz label emms popad } Какая из первых двух программ быстрее? Третья программа, на мой взгляд быстрее всех, но вот есть одно НО! Правда ли, что для переключения в режим MMX команд, процу надо ~50 тактов?!? Можно ли оптимизировать эти три программы(по быстродействию ессно) далее? Предвидя ответ "можно" :) спрошу КАК ? Даст ли выравнивание большой прирост в производительности? Какие еще есть способы копирования памяти? Напишите плиз кусок кода, позволяющий хоть примерно отследить время выполнения участка кода. Я пробовал с помощью RDTSC, но разброс более чем на порядок привел меня к мысли, что я чего-то не так делаю :(. Отсылать по этому вопросу в другой топик меня не нужно, я там был. |
|
|
Дата: Янв 27, 2004 02:54:36 ozzman В первом случае ecx обнулять не нужно. test edx, edx ; почему это можно убрать? Предыдущая инструкция dec влияет на флаг Z. сколько тактов тратится на pushad и popad? Много! :-) repnz movsd // эта команда очень тормозная? Сравнительно, но лучше первый вариант. Даст ли выравнивание большой прирост в производительности? Да. На все последующие вопросы один ответ - поиск по форуму! |
|
|
Дата: Янв 27, 2004 08:59:30 Поправьте меня, если я ошибаюсь, но по-моему, первая программа самая оптимальная из всех. Инструкции в цикле первой программы прекрасно спариваются, да и цикл невелик, а значит мало того, что команды будут выполняться параллельно, так еще и маленький цикл при втором и последующих проходах будет целиком браться из кеша. Насчет инструкций ММХ мне, к сожалению, ничего не известно, но что-то мне подсказывает, что они не дадут такой скорости как первая программа. |
|
|
Дата: Янв 27, 2004 12:20:29 На самом деле все зависит от размера копируемой памяти - для совсем маленьких, до размера страницы (4кб) быстрее будет первый вариант, для средних - второй, а для мегабайтных кусков - третий. Рекомендую отдизасмить функцию __intel_fast_memcpy() из интеловского С++ компилера, весит она больше 5кб в скомпиленном виде, и делает кучу проверок на размер/выравнивание, наличие MMX/SSE и пр. и в результате идет по самой быстрой ветке для конкретного случая. |
|
|
Дата: Янв 27, 2004 15:00:52 · Поправил: S_T_A_S_ ozzman Судя по условию задачи (mov edx, intsize ; = 1000000 байт) нужны большие куски.. Посмотрите это dz 3BePIOra Да цикл хороший, еще бы из памяти так же читать/писать как из кеша. :) |
|
|
Дата: Янв 27, 2004 17:02:11 А по самому коду нет нареканий? I'm beginner. |
|
|
Дата: Янв 27, 2004 18:10:37 Можно объединить счетчик цикла и "индекс массива" т.е. использовать один регистр вместо 2-х: mov edx, intsize ;; (должно быть кратно DWORD) mov esi, p_src ; адрес исходных данных mov eax, p_dest ; адрес, куда будем копировать label1: sub edx, 4 mov ecx, dword ptr[esi+edx] mov dword ptr[eax+edx], ecx jnz label1 Возможно, это не совсем удачный пример с точки зрения копирования (задом-наперед), но смысл наверное проще видно. В pdf по ссылке (слово ЭТО ^^ ) есть примеры получше для MMX :) Еще, могут быть проблемы с количеством копируемых байт, если оно не кратно копируемому за один проход цикла. PUSHAD можно избежать, если знать, какие регистры можно безболезненно использовать в inline asm в с. (это не ко мне) |
|
|
Дата: Янв 27, 2004 18:55:49 >В pdf по ссылке (слово ЭТО ^^ ) есть примеры получше для MMX :) Я не настолько beginner, чтобы не найти гиперссылку :) Сейчас сижу, перевожу... Большое спасибо за линк. Кстати, там часто используется ширина полосы частот - bandwidth: xxx MB/sec. А как её измерить? |
|
|
Дата: Янв 27, 2004 20:39:22 чтобы не найти гиперссылк Дык, я из вопроса сделал предположение, что файл не читали.. там как раз ответ bandwidth (пропускная способность): объем данных вы знаете; если измерять время - очень неточно. На форуме есть примеры использования команды rdtsc (и на С) - получаем кол-во тактов, далее зная частоту процессора, получаем время точнее. |
|
|
Дата: Янв 27, 2004 22:21:40 помнится, замерял я скорость копирования и самим быстрым оказался: rep movsd; (не mmx(8byte) или sse(16byte)) да недаром же intel параграф посвящает этому. >сколько тактов тратится на pushad и popad? по 8 тактов. (скоко внутри операции) |
|
|
Дата: Янв 28, 2004 01:25:47 Про rdtsc на форуме нашел только топик IceStudent, из него мало чего почерпнул для себя. Пытался сделать сам: int time _asm { pushad cpuid rdtsc mov time, eax ... // тестируемый код rdtsc sub eax, time mov time ,eax popad } cout << "time = " << time << endl; Так вот time получается в диапазоне от 11492317 до 12272456 при тестировании одного и того же участка кода. Интересно почему? Что я делаю не так? |
|
|
Дата: Янв 28, 2004 08:02:56 je_ >сколько тактов тратится на pushad и popad? - по 8 тактов. (скоко внутри операции) На каком CPU? На athlon - 6 тактов. По PIV в intel'овском мануале ничего нет :( Имхо, все равно быстрее пользовать: mov [esp], reg; sub esp, N самим быстрым оказался rep movsd.. ..да недаром же intel параграф посвящает этому. Где бы мне этот параграф найти? Посмотрите, что Dr.Golova пишет по поводу intel ozzman time получается в диапазоне от 11492317 до 12272456 при тестировании одного и того же участка кода. Интересно почему? Оно так и будет. Виндос задачи переключает, пока вы копируете. Обычно меряют несколько раз и берут среднее значение. Хотя и без этого будет видно, какой способ быстрее. |
|
|
Дата: Янв 28, 2004 11:31:10 S_T_A_S_, >На каком CPU? на моем PIII-600CEL >Имхо, все равно быстрее пользовать: mov[esp],reg; sub esp, зачем имхо? замерь точно. но зачем PASHAD вообще делать? там ведь ESI,EDI хватит. >Где бы мне этот параграф найти? черт, я тоже не нашёл! но помню ведь.. еще постараюсь. |
|
|
Дата: Янв 28, 2004 11:40:58 · Поправил: je_ НАШЁЛ!(но прав ли я?) Intel Architecture Software Developer’s Manual Volume 3: System Programming 7.2.3. Out of Order Stores From String Operations in P6 Family Processors |
|
|
Дата: Янв 28, 2004 14:02:14 je_ зачем имхо? замерь точно. А КАК это ТОЧНО замерить? Ну в теории: MOV [ESP], reg будут спариваться, а потом один SUB на все. Только это для старых CPU неверно. И в данном случае совсем незаметно будет, учитывая основную цель. черт, я тоже не нашёл! но помню ведь.. еще постараюсь. Да зачем его искать? Это наверное в каком-то старом документе написано. Я не знаю детали работы контроллера памяти и т.п. но общие упрощенные принципы я понимаю так: - CPU выставляет адрес чипсету; - идет запрос к памяти на чтение от контроллера памяти чипсета; - память выдает данные чипсету; - чипсет - процессору; Далее - обратный порядок при записи Интересный момент заключается в том, что ваш CPU имеет ядро 600MHz, а шину памяти 66 (!!) MHz. Т.е. память на порядок медленнее данные может выдавать/принимать на шину, чем ядро их может обрабатывать. Теперь проблема: время, которое проходит после того, как выставлен адрес до появления непосредственно самих данных (латентность) тоже примерно на порядок больше, чем скорость шины памяти. Видите сколько времени пройдет, пока мы получим первый байт!! Поэтому так не делают. А делают так: - CPU выставляет СЕРИЮ адресов контроллеру - контроллер открывает НЕСКОЛЬКО разных банков памяти - заполняет несколько строк кеша - CPU имеет сразу "пачку" данных Вот теперь эту "пачку" записываем обратно в память. НО в обход кеша (что бы лишний раз не читать из памяти) Таким образом имеем близкую к теоретической скорость работы с памятью. НО ТОЛЬКО НА БОЛЬШИХ болках. На маленьких - толку не будет, пока не придумаешь хитрый алгоритм, который сильно зависит от данных. Поэтому MOVSD и работает в определенных случаях быстрее. Но только на маленьких объемах ЗЫ Вот A64 - грамотно сделали. Интегрировали контроллер памяти в ядро. Поэтому он и шустрый |
|
Powered by miniBB 1.6 © 2001-2002
Время загрузки страницы (сек.): 0.081 |