Здравствуйте, jazzer, Вы писали:
J>вместо инкремента индекса l++ и обращения по нему data[l] инкрементишь указатели на элементы массива, начиная с data, а лучше — заканчивая, т.е. J>
Здравствуйте, jazzer, Вы писали:
J>вместо инкремента индекса l++ и обращения по нему data[l] инкрементишь указатели на элементы массива, начиная с data, а лучше — заканчивая, т.е. J>
Мысль вслух: лучше не гладить память против шерсти. На некоторых процессорах hardware prefetching работает хуже в обратном направлении. Например, Pentium M может отслеживать 12 потоков доступа к памяти в прямом направлении, и только 4 — в обратном.
The Pentium M processor also provides a hardware prefetcher for data.
It can track 12 separate streams in the forward direction and 4 streams in
the backward direction.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>
It's kind of fun to do the impossible (Walt Disney)
для начала лучше разнести на два цикла, плюс адрессацию с индекса внутри массива заменить на работу с указателями
оптимизация записи в память подробно расписанна в книге Криса Касперски — "Техника оптимизации программ"
Re[2]: Как из этого выжать максимальную скорость?
От:
Аноним
Дата:
17.08.05 09:22
Оценка:
Здравствуйте, jazzer, Вы писали:
J>Здравствуйте, Аноним, Вы писали:
J>скомпилировать с оптимизацией по скорости. J>и умножение size*size вынести за пределы цикла, хотя упомянутая оптимизация сама это вполне сможет сделать.
Все равно проигрывает memcpy в 3 раза. vc7.1 release.
Re[2]: Как из этого выжать максимальную скорость?
От:
Аноним
Дата:
17.08.05 09:27
Оценка:
Здравствуйте, arcman, Вы писали:
A>Здравствуйте, Аноним, Вы писали:
A>для начала лучше разнести на два цикла, плюс адрессацию с индекса внутри массива заменить на работу с указателями A>оптимизация записи в память подробно расписанна в книге Криса Касперски — "Техника оптимизации программ"
A>>для начала лучше разнести на два цикла, плюс адрессацию с индекса внутри массива заменить на работу с указателями A>>оптимизация записи в память подробно расписанна в книге Криса Касперски — "Техника оптимизации программ"
А>Книги нет :( А как с указателями?
вместо инкремента индекса l++ и обращения по нему data[l] инкрементишь указатели на элементы массива, начиная с data, а лучше — заканчивая, т.е.
for (int *p = data+size*size; p!=data; *(--p) = 99999);
A>>>для начала лучше разнести на два цикла, плюс адрессацию с индекса внутри массива заменить на работу с указателями A>>>оптимизация записи в память подробно расписанна в книге Криса Касперски — "Техника оптимизации программ"
А>>Книги нет А как с указателями?
J>вместо инкремента индекса l++ и обращения по нему data[l] инкрементишь указатели на элементы массива, начиная с data, а лучше — заканчивая, т.е. J>
Для чего требуется повышение быстродействия?
На глаз — вынести заполнение массива data из цикла. Попробовать memset, правда, не знаю, будет ли быстрее. Ну а общий подход — смотреть, какие ассемблерные команды генерируются и, если что, переписывать как ассемблерную вставку.
Но зачем, однако?
G> Для чего требуется повышение быстродействия? G> На глаз — вынести заполнение массива data из цикла. Попробовать memset, правда, не знаю, будет ли быстрее. Ну а общий подход — смотреть, какие ассемблерные команды генерируются и, если что, переписывать как ассемблерную вставку. G> Но зачем, однако?
Как memset? memset(data,99999,sizeof(unsigned int)*size*size); не пашет.
Здравствуйте, Аноним, Вы писали:
А>Сделал. memcpy быстрее в 1.5 раза.
Оптимизацию по скорости включил? Компилятор у тебя какой?
Все эти извращения с указателями нормальный компилятор должен разворачивать в sse-шные инструкции для Intel и в аналогичные, если есть, на других. Более того, он должен оптимизировать и обычный цикл с индексами.
Тут недавно Bell ссылку давал.
Прелюбопытнейшая статья, особенно Duff's device мне понравился.
Кстати, обратите внимание, что если вам надо поддерживать несколько компиляторов, то результаты сравнения memcpy и for-цикла могут сильно варьироваться.
GA>Тут недавно Bell ссылку давал. GA>Прелюбопытнейшая статья, особенно Duff's device мне понравился.
GA>Кстати, обратите внимание, что если вам надо поддерживать несколько компиляторов, то результаты сравнения memcpy и for-цикла могут сильно варьироваться.
В статье memcpy всех порулил. Буду его использовать. (Пишу только на vc7.1)
Re[6]: Как из этого выжать максимальную скорость?
От:
Аноним
Дата:
17.08.05 10:31
Оценка:
Здравствуйте, jazzer, Вы писали:
J>Здравствуйте, Аноним, Вы писали:
А>>Сделал. memcpy быстрее в 1.5 раза.
J>Оптимизацию по скорости включил? Компилятор у тебя какой?
J>Все эти извращения с указателями нормальный компилятор должен разворачивать в sse-шные инструкции для Intel и в аналогичные, если есть, на других. Более того, он должен оптимизировать и обычный цикл с индексами.
J>см, например, здесь: J>http://rsdn.ru/Forum/Message.aspx?mid=1329039&only=1
Здравствуйте, Аноним, Вы писали:
А>В статье memcpy всех порулил. Буду его использовать. (Пишу только на vc7.1)
Ну а вот с этим:
data[l] = 99999; <--- ?
можно поэкпериментировать.
И вообще, когда будете работать с non-POD'ами, в статье найдете источник вдохновения.
Для начала как говорили разбить на 2 цикла чтоб не иметь проблем с кэшами
и первый код переносить с помощью memcpy или mmx
unsigned int nLength;
unsigned int *pData = data;
nLength = size*size;
nLast = nLength&3;
memcpy(mark, klas.yel, size*size*(sizeof(type)) //
//данный цикл можно прооптимизить в зависимости от желания юзать всякие расширения асмовские типа ммх(процентов 20 добавит)for (unsigned int l = 0; i < nLength / 4; ++l)//оптимизатор все равно на сдвиг заменит
{
*pData++ = 99999; // поможем оптимизатору явно развернув цикл
*pData++ = 99999;
*pData++ = 99999;
*pData++ = 99999;
}
for(unsigned int l = 0; i < nLast; ++l)
{
*pData++ = 99999;
}
для забивания гвоздя надо выбирать правильный микроскоп.
С>for (unsigned int l = 0; i < nLength / 4; ++l)//оптимизатор все равно на сдвиг заменит С>{ С> *pData++ = 99999; // поможем оптимизатору явно развернув цикл С> *pData++ = 99999; С> *pData++ = 99999; С> *pData++ = 99999; С>}
Если nLength не кратна 4, будут проблемы.
A>>>для начала лучше разнести на два цикла, плюс адрессацию с индекса внутри массива заменить на работу с указателями A>>>оптимизация записи в память подробно расписанна в книге Криса Касперски — "Техника оптимизации программ"
А>>Книги нет А как с указателями?
J>вместо инкремента индекса l++ и обращения по нему data[l] инкрементишь указатели на элементы массива, начиная с data, а лучше — заканчивая, т.е.
Думаю, нет, уверен, что с более-менее нормальным компилятором это даст ровно такой же код, как и приведенный начальный цикл. Все более-менее адекватные компиляторы уже давно индексную и адресную арифметику сводят к одному коду.
Вот что дается VC6:
В общем, компилятор и сам не дурак. Другое дело, что для различных процессоров stosd и movsd не всегда оптимальны — например, для 486 было оптимально делать цикл + мув, на остальных, безусловно, быстрее будет через fpu\sse\mmx, ну тут оптимизатор настроить на соотв. процессор и получим нужные эффект.
Начальный пример автора сложнее — там структура в качестве источника данных используется, поэтому насчет применения там memcpy что то я не уверен. А вот что для структуры:
Комментарии излишни... Пожалуй, что все-таки стоило второй цикл несколько лучше прооптимизировать, например, там можно избавиться от второго счетчика. Но это особых различий в производительности не даст.
Так что максимум, что может сделать автор — посчитать сначала границы цикла, а не делать это в каждой итерации... да и то это соптимизуется компилятором, исключая особые случаи, он посчитает границы цикла изначально.
Здравствуйте, jazzer, Вы писали:
J>вместо инкремента индекса l++ и обращения по нему data[l] инкрементишь указатели на элементы массива, начиная с data, а лучше — заканчивая, т.е. J>
А так точно быстрее? Казалось бы, нормальный компилятор и сам могёт такое навернуть. Случай-то типичный . А вот распутать эти все оптимизации обратно -- не факт.
Вы вот лучше признайтесь, каковы размеры всех этих данных?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, KHeLeKRoN, Вы писали:
KHL>AFAIK если код в цикле влезает в L1-кэш, то это оочень убыстрит всю работу (более чем в два раза). И такое разнесение поможет влезть.
Дополнительно: в коротком цикле больше вероятность уместить все необходимые данные в регистры. Еще у некоторых компиляторов есть прагмы для подсказки компилятору, что области данных гарантированно не пересекаются — тоже может дать серьезный выигрыш.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>
It's kind of fun to do the impossible (Walt Disney)
Здравствуйте, Andrew S, Вы писали:
A>>>>для начала лучше разнести на два цикла, плюс адрессацию с индекса внутри массива заменить на работу с указателями A>>>>оптимизация записи в память подробно расписанна в книге Криса Касперски — "Техника оптимизации программ"
А>>>Книги нет А как с указателями?
J>>вместо инкремента индекса l++ и обращения по нему data[l] инкрементишь указатели на элементы массива, начиная с data, а лучше — заканчивая, т.е.
AS>Думаю, нет, уверен, что с более-менее нормальным компилятором это даст ровно такой же код, как и приведенный начальный цикл. Все более-менее адекватные компиляторы уже давно индексную и адресную арифметику сводят к одному коду. AS>Вот что дается VC6:
AS>
F>быстрее всё-таки так
F> int *p = data1+9; F> do F> { F> *p=9999; F> }while(p-- != data1);
Быстрее так быть не может по нескольким причинам:
1. Результирующий код одинаков, в случае по крайней мере моего компилятора.
2. Если компилятор и сформирует "вывернутый" код, на многих процессорах обратная запись гораздо медленнее прямой.
А вообще, лучше приведите тот код, что у вас получился в результате — тогда и будет о чем говорить. А просто кидаться необоснованными ничем заявлениями все могут...
Здравствуйте, Andrew S, Вы писали:
F>>быстрее всё-таки так
F>> int *p = data1+9; F>> do F>> { F>> *p=9999; F>> }while(p-- != data1);
AS>Быстрее так быть не может по нескольким причинам: AS>1. Результирующий код одинаков, в случае по крайней мере моего компилятора.
критерием для меня был конечно результирующий код
AS>2. Если компилятор и сформирует "вывернутый" код, на многих процессорах обратная запись гораздо медленнее прямой.
это я плохо понимаю о чём( про "вывернутость" ), может с точки зрения человека это и так, но только с непривычки
я как раз смотрел на разных RISC архитектурах
AS>А вообще, лучше приведите тот код, что у вас получился в результате — тогда и будет о чем говорить. А просто кидаться необоснованными ничем заявлениями все могут...
ЗAS>>2. Если компилятор и сформирует "вывернутый" код, на многих процессорах обратная запись гораздо медленнее прямой.
F>это я плохо понимаю о чём( про "вывернутость" ), может с точки зрения человека это и так, но только с непривычки F>я как раз смотрел на разных RISC архитектурах
AS>>А вообще, лучше приведите тот код, что у вас получился в результате — тогда и будет о чем говорить. А просто кидаться необоснованными ничем заявлениями все могут...
При чем здесь arm? В данном топике речь идет о линейке компиляторов VC под ia32, конкретно — vc7.1. Давайте не будем рассматривать _сильно_ другие (какие, кстати?) компиляторы, поскольку это явно уход от темы. Я не уверен, что компилятор, приведенный вам, достаточно удачно оптимизирует.
Точнее уверен, что неудачно, поскольку вот что формирует в приведенном вами коде VC6:
Здравствуйте, Andrew S, Вы писали:
ЗAS>>>2. Если компилятор и сформирует "вывернутый" код, на многих процессорах обратная запись гораздо медленнее прямой.
F>>это я плохо понимаю о чём( про "вывернутость" ), может с точки зрения человека это и так, но только с непривычки F>>я как раз смотрел на разных RISC архитектурах
AS>http://www.rsdn.ru/Forum/Message.aspx?mid=1332782&only=1
весь windows не переделать, а в приложении или даже коде драйвера можно писать правильно, это всё равно не будет даже 1/3
AS>>>А вообще, лучше приведите тот код, что у вас получился в результате — тогда и будет о чем говорить. А просто кидаться необоснованными ничем заявлениями все могут...
AS>При чем здесь arm? В данном топике речь идет о линейке компиляторов VC под ia32, конкретно — vc7.1. Давайте не будем рассматривать _сильно_ другие (какие, кстати?) компиляторы, поскольку это явно уход от темы. Я не уверен, что компилятор, приведенный вам, достаточно удачно оптимизирует. AS>Точнее уверен, что неудачно, поскольку вот что формирует в приведенном вами коде VC6:
AS>
F> int data1[10000];
F> int l = 10000;
F> do
F> {
F> data1[--l]=99999;
F> }while(l);
F>
F>и смотреть без оптимизации бесполезно
А с чего вы решили, что я смотрю код без оптимизации? На мой взгляд, из приведенных кусков следует, что я его беру из промежуточных ассемблерных листингов. Так что тут все нормально.
Вот ваш последний вариант:
; 204 : int data1[10000];
; 205 :
; 206 : int l = 10000;
lea eax, DWORD PTR _data1$98162[esp+80004]
mov ecx, 10000 ; 00002710H
$L98164:
; 207 : do
; 208 : {
; 209 : data1[--l]=99999;
sub eax, 4
; 210 : }while(l);
dec ecx
mov DWORD PTR [eax], 99999 ; 0001869fH
jne SHORT $L98164
Значит, компиляторы не очень, если они такой элементарный прямой цикл делают больше обратного.
F>ну про обратное чтение я уже сказал, что есть ещё код windows
При чем тут это? У вас что, тайм слайс истекает каждую итерация цикла, что код виндовс будет кардинальным образом влиять на быстродействие? К тоже вам уже несколько раз напомнили, что процессоры оптимизированы на прямое чтение\запись памяти, а не обратно. Прямой цикл VC6 явно лучше оптимизирует (и явно оптимизация заточена именно на прямой цикл), прямой цикл работает быстрее на многих архитектурах, ну и наконец — прямой цикл используется чаще всего. В обшем, на мой взгляд, ваше доводы не состоятельны.
можно попробовать заполнить несколько первых значений, а потом размножить с помощью memcpy.
AS>>И каким образом movsd будет быстрее stosd?
A>Я всего-лишь предположил В исходном сообщении не было намеков ни на платформу, ни даже на typeof(data[l]).
F>я наверное чего-то не догоняю, объясните, пожалуйста, если не сложно
F>а как правильный листинг сгенерить с F>rep stosd F>?
Использовать более-менее нормальный компилятор (в мое случае — VC6), оптимизацию на скорость, опция генерить листинг в asm файлы, релиз.
Остальное все по умолчанию (впрочем, оптимизация на скорость в VC6 тоже по умолчанию).
Вроде все
Здравствуйте, Andrew S, Вы писали:
F>>я наверное чего-то не догоняю, объясните, пожалуйста, если не сложно
F>>а как правильный листинг сгенерить с F>>rep stosd F>>?
AS>Использовать более-менее нормальный компилятор (в мое случае — VC6), оптимизацию на скорость, опция генерить листинг в asm файлы, релиз. AS>Остальное все по умолчанию (впрочем, оптимизация на скорость в VC6 тоже по умолчанию). AS>Вроде все
Использую стандартную Visual C++ 6.0
оптимизация на скорость /O2
листинг в asm файлы, релиз.
AS>>Использовать более-менее нормальный компилятор (в мое случае — VC6), оптимизацию на скорость, опция генерить листинг в asm файлы, релиз. AS>>Остальное все по умолчанию (впрочем, оптимизация на скорость в VC6 тоже по умолчанию). AS>>Вроде все
F>Использую стандартную Visual C++ 6.0 F>оптимизация на скорость /O2
Сервис паки ставили? У меня 6-й. До этого еще процессор пак 5-й стоял, но вроде как 6-й сервис пак его убил.
Обманывать мне вас смысла нет — да и смысл, если VC6 генерит именно это, не сам же я такой код нарисовал
AS>В любом случае — перенос почти всегда медленее заливки числом. Если только заранее несколько значений не посчитать в регистры. Но никак не в память.
А кэш?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, Andrew S, Вы писали:
AS>>В любом случае — перенос почти всегда медленее заливки числом. Если только заранее несколько значений не посчитать в регистры. Но никак не в память.
E>А кэш?
А он берёт данные из памяти, которая на порядок (!) медленнее. И на больших объёмах данных, память самое узкое место.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Здравствуйте, gear nuke, Вы писали:
GN>Здравствуйте, Erop, Вы писали:
E>>Здравствуйте, Andrew S, Вы писали:
AS>>>В любом случае — перенос почти всегда медленее заливки числом. Если только заранее несколько значений не посчитать в регистры. Но никак не в память.
E>>А кэш?
GN>А он берёт данные из памяти, которая на порядок (!) медленнее. И на больших объёмах данных, память самое узкое место.
Если читать в последний раз записанный участок памяти, то наверное он будет в кэше. Я попробовал сравнить обычный цикл с таким копированием на i86. Результаты почти не отличаются. Пробовал копировать пачками по 4 и 8 интов. На спарке это метод заметно медленнее.
Re[15]: Как из этого выжать максимальную скорость?
Здравствуйте, Andrew S, Вы писали:
AS>>>Использовать более-менее нормальный компилятор (в мое случае — VC6), оптимизацию на скорость, опция генерить листинг в asm файлы, релиз. AS>>>Остальное все по умолчанию (впрочем, оптимизация на скорость в VC6 тоже по умолчанию). AS>>>Вроде все
F>>Использую стандартную Visual C++ 6.0 F>>оптимизация на скорость /O2
AS>Сервис паки ставили? У меня 6-й. До этого еще процессор пак 5-й стоял, но вроде как 6-й сервис пак его убил. AS>Обманывать мне вас смысла нет — да и смысл, если VC6 генерит именно это, не сам же я такой код нарисовал
у меня стоит sp6
Вы не могли бы код всей функции привести, пожалуйста, ну типа
Здравствуйте, alnsn
AS>>>>В любом случае — перенос почти всегда медленее заливки числом. Если только заранее несколько значений не посчитать в регистры. Но никак не в память.
E>>>А кэш?
GN>>А он берёт данные из памяти, которая на порядок (!) медленнее. И на больших объёмах данных, память самое узкое место.
A>Если читать в последний раз записанный участок памяти, то наверное он будет в кэше.
Верно, хотя зависит от метода записи в память. Для x86 cуществуют команды, пишущие в обход кеша (компиляторы С их обычно не гененрируют).
A>Я попробовал сравнить обычный цикл с таким копированием на i86. Результаты почти не отличаются. Пробовал копировать пачками по 4 и 8 интов.
Очень многое зависит от объёма копируемых данных. На больших объёмах можно получить выигрыш до 3х раз(пример, для P4 это не очень актуально, поскольку там rep movsd ускорена аппаратно).
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth