Обновить
5

Пользователь

Отправить сообщение
мистика! пробовал через тот же сайт, только компилятор стоял gcc11 — код получался неоптимизирвоанным, а сейчас всё в порядке.

всё же некоторые возможности процессора пропадают безвозвратно, что бывает обидно. Например, 64х процессоры при умножении двух чисел возвращают два 64х битных числа, но старшее из них безвозвратно теряется, так как не удаётся построить такую конструкцию, из которой оптимизатор бы понял, что значение нужно сохранить. Как и флаг переноса при сложении. Их же специально придумали для удобства людей, а в высокоуровневых языках убрали — вот и проверяй потом на переполнение. А всякие расширения процессора можно и не вспоминать. Это возможно только в рамках стандартной или не очень библиотеки.
Только им не показывайте, а то ведь всерьёз начнут применять)

На онлайн компиляторе не смог повторить, но локальный gcc однозначно лучше компилирует перестановку через временную переменную (swap4), чем xor варианты:
objdump
0000000000000000 <_Z5swap1PiS_>:
   0:   8b 07                   mov    eax,DWORD PTR [rdi]
   2:   33 06                   xor    eax,DWORD PTR [rsi]
   4:   89 07                   mov    DWORD PTR [rdi],eax
   6:   33 06                   xor    eax,DWORD PTR [rsi]
   8:   89 06                   mov    DWORD PTR [rsi],eax
   a:   31 07                   xor    DWORD PTR [rdi],eax
   c:   c3                      ret

0000000000000010 <_Z5swap2PiS_>:
  10:   8b 07                   mov    eax,DWORD PTR [rdi]
  12:   8b 0e                   mov    ecx,DWORD PTR [rsi]
  14:   48 31 c8                xor    rax,rcx
  17:   48 31 c1                xor    rcx,rax
  1a:   48 31 c8                xor    rax,rcx
  1d:   89 07                   mov    DWORD PTR [rdi],eax
  1f:   89 0e                   mov    DWORD PTR [rsi],ecx
  21:   c3                      ret

0000000000000030 <_Z5swap3PiS_>:
  30:   8b 0f                   mov    ecx,DWORD PTR [rdi]
  32:   8b 06                   mov    eax,DWORD PTR [rsi]
  34:   89 07                   mov    DWORD PTR [rdi],eax
  36:   89 0e                   mov    DWORD PTR [rsi],ecx
  38:   c3                      ret

0000000000000040 <_Z5swap4PiS_>:
  40:   8b 07                   mov    eax,DWORD PTR [rdi]
  42:   8b 16                   mov    edx,DWORD PTR [rsi]
  44:   89 17                   mov    DWORD PTR [rdi],edx
  46:   89 06                   mov    DWORD PTR [rsi],eax
  48:   c3                      ret




Впрочем, если компилировать с
жёстко заданными значениями
int main()
{
    int a=6,b=7,c=8;
    swap(&a,&c);
    return a-c;
}

то результаты получаются следующие:
0000000000000050 <_Z5main1v>:
  50:   b8 02 00 00 00          mov    eax,0x2
  55:   c3                      ret

0000000000000060 <_Z5main2v>:
  60:   b8 06 00 00 00          mov    eax,0x6
  65:   b9 08 00 00 00          mov    ecx,0x8
  6a:   48 31 c8                xor    rax,rcx
  6d:   48 31 c1                xor    rcx,rax
  70:   48 31 c8                xor    rax,rcx
  73:   29 c8                   sub    eax,ecx
  75:   c3                      ret

0000000000000080 <_Z5main3v>:
  80:   b9 06 00 00 00          mov    ecx,0x6
  85:   b8 08 00 00 00          mov    eax,0x8
  8a:   29 c8                   sub    eax,ecx
  8c:   c3                      ret

0000000000000090 <_Z5main4v>:
  90:   b8 02 00 00 00          mov    eax,0x2
  95:   c3                      ret



лучшим окажется тот код, который без вставок!
А всё потому, что ваша версия загружает в память промежуточные результаты.
Если строго следовать написанному мной алгоритму, swap работает, а версия 3 даже без каких либо xor-ов.
ideone.com/NeRdWe
Если говорим о базовой интерпретации, то переменная — это адрес в памяти, которая будет дважды загружена в разные регистры, после чего произойдёт обмен и выгрузка обратно в память. Таким образом swap(a,a) корректно отработает через xor.

В случае со swap оптимизировать не надо в любом случае — это задача компилятора знать особенности всех платформ и скорости выполнения инструкций (и особенности конвейера и кеша они тоже учитывают), а также проследить, что за a и b передаются в функцию, и на основании этого выдать оптимальный код.

Но здесь возникает проблема определённости, так как мы перестаём понимать сложность алгоритма, и однажы оказывается, что размещение в памяти 1 млрд+1 букв занимает на 0.2 сек меньше, чем 1 млрд просто.
Судя по сложившейся системе наименования для SSD «параллелепипед умеренной жёсткости» будет в самый раз)
«автопилоты» — если бы вы знали, как эти автопилоты работают, скорее всего побоялись бы выходить на улицу, зная что по ней поедет такое. но спутники летают… только там технологии и принципы разработки немного кардинально другие.
чтобы ctrl shift вправо, надо обе руки держать на клавиатуре, а если рука занята мышкой, то быстрее выделить. а вот пользоваться компьютером без мыши и без какого-то знания программирования — это вряд ли.
«а наоборот» — думал, напишите, что: а наоборот — подчиняются им и выполняют все их команды.
Где-то читал, что xchg a,b работает дольше, чем xor a,b; xor b, a; xor a,b; но в реальной программе может и такого не произойти. С высокой вероятностью оптимизатор решит не перемещать значения в памяти, а заменить все последующие ссылки a на b и наоборот (если сможет).
К сожалению, я в курсе состояния современной профессиональной разработки.
«терять для этого время» — действительно, просто покажите ваш код, где вы выбрали лучший язык, архитектуру и методологию разработки. А книги Дейкстры у меня итак уже есть.
Если у вас есть конструктивная критика работ Дейкстры, то было бы интересно узнать детали и ваши предложения, как сделать лучше. Понятие «современному накопленному опыту» — слишком широкое и включает в себя не менее широкое «накопленного современного говнокода». Но подозреваю, что постоянные взломы и утечки происходят не из-за кода, написанного Дейкстрой или под его руковоством.
Нам в школе рассказали о лагранжевой механике. Мельком на факультативном занятии, больше к теме не возвращались. Сейчас очень жалею об этом, поскольку в учебниках объясняют даже меньше, чем в нашей школе, а вопросы больше задавать некому. И ведь предложи нам порешать задачки — через пол года или даже быстрее скорее всего я бы задал те же вопросы.

Поэтому совсем не очевидно. Вместо ньютоновской — не стоит, но после — очень спорный вопрос?!
Паскаль до сих пор живёт в среде делфи, на которой и по сей день пишут миниатюрные графические приложения.
По п.1) Недавно проводил исследование необычной формулы, описывающей изгиб металлических платин, и тоже зачем-то сначала рисовал графики. Они оказались совершенно бесполезны и не информативны. После этого переделал программу, чтобы она умела возвращать только одно-единственное числовое значение, и дописал вторую программу, в которой изложил проверяемую гипотезу. Проверив пять гипотез посредством 10 млн запусков первой программы она заключила, что формула фейковая и коэффициенты в ней подгоняли, что ошибку легче всего искать на 30% длины стержня с очень маленькой толщиной, а графики же, поскольку я не знал, что точно ищу, то все оказались мейнстримными частными случаями. Графики тоже нужны — но в конце, когда расчёты уже проведены, и вы точно знаете, что на вашем графике есть то, что вы искали.

Я думаю, есть множество других задач, где визуализация намного более важна, чем в расчёте физических процессов. Как вы пришли к вашим выводам?
Если крипта станет хоть немного популярной, и на неё начнут покупать продукты и бытовые товары, это будут миллионы-миллиарды транзакций в сутки (до 100 тыс. в секунду по-хорошему). С какой скоростью будет разрастаться цепочка, какая будет нагрузка на сети, чтобы её синхронизировать, какие мощности потребуются, чтобы вычислять хеши? Сейчас криптовалюты хорошо себ чувствуют только потому, что количество транзакций в них смехотворно мало по сравнению со всемй экономикой.
Касательно оптимизации. Поигрался с кодом:
prime :: (Integral a) => a -> Bool
prime 1 = True
prime x = and [ x `mod` y /= 0 | y <- [2..(x-1)] ]

Выглядит красиво, не поспорить,
а теперь запускаем:
> prime(1999993)
True
it :: Prelude.Bool
(0.85 secs, 816,057,504 bytes)

> prime(3997859)
True
it :: Prelude.Bool
(1.64 secs, 1,631,185,144 bytes)

> prime x = and [ x `mod` y /= 0 | y < — [2..(div (x-1) 2)] ]
prime :: Integral a => a -> Prelude.Bool
(0.01 secs, 0 bytes)

> prime(1999993)
True
it :: Prelude.Bool
(0.41 secs, 408,056,872 bytes)

> prime(3997859)
True
it :: Prelude.Bool
(0.81 secs, 815,620,928 bytes)

Я, конечно, не ждал чуда с y^2, но на /2 от платформы, занимающей 800МБ на диске расчитывал. Но ладно.
Код на си:
int try_div(int a,int b)
{
	return a%b;
}

int test_prime(int arg)
{
	if(arg<2) return -1;
	if(2==arg) return 0;
	if(!(arg%2)) return 2;
	for(int i=3;i<arg;i+=1)
		if(!(try_div(arg,i)))
			return i;
	return 0;
}

int main(int argc,char**argv)
{
	int j;
	for(int i=3997850;i<3997870;i++){
		j=test_prime(i);
		WriteI(1,i);
		WriteC(1,"\t");
		WriteI(1,j);
		WriteC(1,"\n");}
	return 0;
}


Результат 0.01 сек
3997850 2
3997851 3
3997852 2
3997853 29
3997854 2
3997855 5
3997856 2
3997857 3
3997858 2
3997859 0
3997860 2
3997861 7
3997862 2
3997863 3
3997864 2
3997865 5
3997866 2
3997867 47
3997868 2
3997869 3

Process returned 0 (0x0) execution time: 0.010 s

Алгоритм точно такой же наивный с полным перебором. Но в 160 раз быстрее. Так что оговореннная архитектура наносит какие-то ограничения, но и без неё у хаскел оптимизатор упирается в намного более серьёзные ограничения. Он ещё 1.6 ГБ памяти потратил. Даже 4 млн по 8 байтному bool — всего 32 МБ промежуточных данных. Честно, нет идей, откуда такие большие числа.
Такой пример предлагается в учебнике по хаскел, какой-то смысл для автора значит был.

Нашёл пример интеграции хаскел и си:
github.com/mgrabmueller/rdtsc

А теперь вопрос: можно ли считать, что любая функция в хаскел либо имеет определение на хаскел, либо импортирована подобным образом? Если да, то это в точности то, о чём я писал в предыдущем комментарии, и хотелось бы посмотреть, каким образом из GMP сделали Integer. Если нет, то странно, и непонятно, где проходит граница.

К си претензия непонятна, ибо в нём граница очень чёткая: список синтактический конструкций задан стандартом, и это встроенное, а все функции — это определяемое (в исходном си вроде бы перегрузки операторов нет). Это вопрос не морали или идеологии, а того, как быстро находить исходники каких-то понятий, если они есть, или понять, что их не нет.
Потому что хорошо, когда каждый язык решает свой круг задач и не претендует на универсальность.
Вы можете определять сложение как:
instance Num Nat where
  (+) a Zero = a 
  (+) a (Succ b) = Succ(a+b)

но вряд ли вас устроит такое определения для сложения 734211040 и 610489189. Впрочем, и оно ссылается на сложение, определённое в строенном типе. А его иначе как
(+) a b = ({
  long ADD;
  register long _a asm("rdi") = a
  register long _b asm("rsi") = b
  asm("mov rax,rdi\nadd rax,rsi":"=a" (ADD));
  ADD;})

и не определить.

Где-то внутри компилятора оно так и записано, но можно было вынести это в файл, чтобы любой мог по своим потребностям что-то добавить или изменить, а не проклинать разработчиков и строить костыли. Иначе, подозреваю, не так много лет пройдёт, и хаскелл станет «ещё одним универсальным языком».
Тогда однозначно включаем хаскелл в школьную программу вместо лиспа. Этот язык развивает чувство юмора. Ещё его можно использовать в паре с си: на хаскелле описывать, что нужно вычислить, а на си описывать элементарные алгоритмы, которыми будет оперировать вычислитель хаскела.
Не исключено. В работах Теслы мне были больше интересны его достижения не в области электричества, а в области психологии. И лишь стараниями конспирологов пришлось занялся изучением патентов, чтобы показать, что не только никто не скрывает его открытия, но они даже повсеместно применяются (многие).
Тема интересная, если посоветуете какие-нибудь дополнительные источники, буду признателен.

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность