Не знаю как сейчас, но раньше свой QSort работал быстрее qsort'а из stdlib.h. Возможно это из-за дополнительных проверок, или в stdlib.h устойчивая сортировка реализована и т.п.
Насчет x86 / amd64 инструкций слегка ошибся, там какие-то хитры SSE инструкции используются. Но в любом случае, в вашем варианте гораздо больше операций выполняется.
int greater(const void* p, const void* q) // трёхстороннее сравнение
{
double x = *(double*)p; // получить значение double с адреса p
double y = *(double*)q;
if (x>y) return 1;
if (x<y) return -1;
return 0;
}
Короче, меньше и быстрее (одна инструкция sub и ret, заместо cmp, mov и ret в x86 / amd64).
Так все функции сравнения в C работают (поэтому в описаниях всяких там memcmp() в возвращаемом значении и указано, что либо 0, либо >0, либо <0).
Мне меньше всего нравится qsort() из стандартной ISO библиотеки C:
А вот с этим не согласен. Если не брать в расчет варианты std::sort() где мы работает с объектами (std::pair или какие-нибудь массивы данных разного размера), то в C++ она таже самая. Т.е. мы указываем начало, конец и функцию сравнения (просто в C заместо конца мы указываем длину и размер элемента).
Помнится в свое время, когда в задаче требовалось использовать std::sort(), для меня было очень странным, что в аргументах передается указатель начала массива и указатель его конца.
А можно узнать чем можно заменить адресную арифметику? Только без костылей, чтоб было так-же удобно.
100 лет назад, когда писал на фортране, связка ассемблер+фортран+линкер вполне работала.
А каким образом вы линкером вставите ассемблерный код посреди кода на Fortran'е? Что-то вроде этого:
#include <stdio.h>
int main(void) {
//Меняем переменные местами с помощью инструкции x86 процессора
int a = 0xAABBCCDD, b = 0xFFFFFFFF;
__asm__
(
"xchgl %[a], %[b]\n\t"
: [a]"+r"(a), [b]"+r"(b)
);
printf("%x\n%x\n", a, b);
//Меняем байты местами с помощью инструкции циклического сдвига x86 процессора
short int c = 0xAABB;
__asm__
(
"rorw $8, %[c]\n\t"
: [c]"+r"(c)
);
printf("%hx", c);
}
Примеры, конечно, уровня Hello World, но в вашем случае это можно сделать только путем вызова функции, а это лишние действия по её вызову. Да и когда требуется вызвать одну инструкцию, городить целую функцию это слишком.
Речь же не о написании драйверов.
Речь шла о работе с устройствами, что из той-же оперы.
А что насчет адресной арифметики, ассемблерных вставок, типов (ну в C это модификатор) наподобие volatile?
Да и в C гораздо удобнее работать с бинарными данными, включая IO-операции, ежели в Fortran. Я лично в этом убедился на том маленьком коде в статье.
Если целесообразность использования Fortran в мат. задачах еще можно оправдать, то превосходство в низкоуровневом программировании C сомневаться сложно. Может в Fortran'е и подобные задачи можно решать, но во некоторых моментах придется много «костылей» и «велосипедов» городить.
Ну так Fortran используют из-за того, что за полвека создано очень много хороших библиотек, ну а переписывать на С это нужно потратить много времени, да и в целом не очень нужно (зачем, коли на Fortran хорошо работает).
Для x87 FPU может быть, ибо у FSQRT latency в среднем 27, но x87 сам по себе тормознутый, т.к. там происходит куча накладных расходов, при работе с его «стековыми регистрами».
С SSE все проще, у sqrtsd latency 7-32, а так-же очень легко можно перегонять значения между XMM, памятью и регистрами процессора.
Я специально замерял, разницы в скорости не было. Время выполнение каждого варианта колебалось от 10 до 12 секунд, при 2048 итерациях и разрешении 8192. Если там и был какой-то прирост, то он съедается планировщиком процессов, IO планировщиком и скоростью записи на HDD.
Проще говоря, в данной ситуации я предпочту использовать CDABS(), т.к. в коде это выглядит элегантнее, а выигрыша никакого нет.
Оу, про iter, z и iter2 я как-то и не подумал. Просто в варианте на C они у меня локальные переменные, следовательно для каждого потока свои. На днях поищу информацию о локальных переменных в Fortran, исправлю.
Кстати говоря, в do цикле x и y автоматически private.
Разница получится в 1 μops (в моем случае sqrtsd из SSE), на скорость это практически не влияет. По крайней мере на форе IO операций записи на диск это не заметно.
А что до того зачем писать — just for fun. Лично мне нравится ковырять различные языки программирования.
Изначально и было приписано PRIVATE(x, y), только зачем? Я же их только читаю, поэтому можно и не указывать. Ну можно приличия ради, пусть по регистрам процессора раскидает.
А что до верификации… смысл? Тут задача не провести мат. расчеты, а сгенерировать «прикольную» картинку, да и текст учебный, не стоит лишний раз его усложнять.
Ну в конкретно этом случае, при генерации Множества Жюлиа с 2048 итерациями, изображение 4096x4096 с OMP генерировалось 3.175 секунд, а без OMP 5.759 секунд на процессоре Core i3 330M с 2 ядрами и HyperThreading.
А вообще, когда я ковырял OMP и OpenCL на C, то все было как-то так:
639x349
В принципе тут разницы большой между Fortran'ом и C не будет, т.к. код тривиальный, без использования каких-то хитрых возможностей.
Но там я генерировал Мандельброта и использовал float, т.к. у меня небыло видеокарты, которая поддерживает числа двойной точности.
Так помнится есть микроконтроллеры, которые можно стирать ИК излучением.
Поправьте меня, если я где-то ошибся, микроконтроллерами не занимаюсь, поэтому мог наврать.
Зачем так усложнять?
Короче, меньше и быстрее (одна инструкция sub и ret, заместо cmp, mov и ret в x86 / amd64).
Так все функции сравнения в C работают (поэтому в описаниях всяких там memcmp() в возвращаемом значении и указано, что либо 0, либо >0, либо <0).
А вот с этим не согласен. Если не брать в расчет варианты std::sort() где мы работает с объектами (std::pair или какие-нибудь массивы данных разного размера), то в C++ она таже самая. Т.е. мы указываем начало, конец и функцию сравнения (просто в C заместо конца мы указываем длину и размер элемента).
Помнится в свое время, когда в задаче требовалось использовать std::sort(), для меня было очень странным, что в аргументах передается указатель начала массива и указатель его конца.
О том и речь. Эмулировать или извращаться можно на любом языке, только это плохо, поэтому в расчет не берется.
А можно узнать чем можно заменить адресную арифметику? Только без костылей, чтоб было так-же удобно.
А каким образом вы линкером вставите ассемблерный код посреди кода на Fortran'е? Что-то вроде этого:
Примеры, конечно, уровня Hello World, но в вашем случае это можно сделать только путем вызова функции, а это лишние действия по её вызову. Да и когда требуется вызвать одну инструкцию, городить целую функцию это слишком.
Речь шла о работе с устройствами, что из той-же оперы.
Да и в C гораздо удобнее работать с бинарными данными, включая IO-операции, ежели в Fortran. Я лично в этом убедился на том маленьком коде в статье.
Если целесообразность использования Fortran в мат. задачах еще можно оправдать, то превосходство в низкоуровневом программировании C сомневаться сложно. Может в Fortran'е и подобные задачи можно решать, но во некоторых моментах придется много «костылей» и «велосипедов» городить.
Так так и делают. В том-же Fortran 2003 на уровне спецификации появились способы взаимодействия с C.
Если не ошибаюсь, то у них там было куча legacy кода и проще было начать с нуля, ежели исправлять старое.
С SSE все проще, у sqrtsd latency 7-32, а так-же очень легко можно перегонять значения между XMM, памятью и регистрами процессора.
Я специально замерял, разницы в скорости не было. Время выполнение каждого варианта колебалось от 10 до 12 секунд, при 2048 итерациях и разрешении 8192. Если там и был какой-то прирост, то он съедается планировщиком процессов, IO планировщиком и скоростью записи на HDD.
Проще говоря, в данной ситуации я предпочту использовать CDABS(), т.к. в коде это выглядит элегантнее, а выигрыша никакого нет.
А счетчики в Fortran по умолчанию PRIVATE.
Кстати говоря, в do цикле x и y автоматически private.
и:
Разница получится в 1 μops (в моем случае sqrtsd из SSE), на скорость это практически не влияет. По крайней мере на форе IO операций записи на диск это не заметно.
А что до того зачем писать — just for fun. Лично мне нравится ковырять различные языки программирования.
А что до верификации… смысл? Тут задача не провести мат. расчеты, а сгенерировать «прикольную» картинку, да и текст учебный, не стоит лишний раз его усложнять.
А вообще, когда я ковырял OMP и OpenCL на C, то все было как-то так:
В принципе тут разницы большой между Fortran'ом и C не будет, т.к. код тривиальный, без использования каких-то хитрых возможностей.
Но там я генерировал Мандельброта и использовал float, т.к. у меня небыло видеокарты, которая поддерживает числа двойной точности.