LINUX.ORG.RU

Деление целых на 2 через битовый сдвиг - архаизм?

 


1

3
#include <iostream>
using namespace std;

int main()
{
	int v = 0;
	for(int i = 0; i < 2000 * 1000 * 1000; ++i) {
		v ^= i >> 1; /* i / 2 */
	}
	cout << v << endl;
	return 0;
}

Если битовый сдвиг заменить обычным делением, то время выполнения не изменится. Обе операции насколько мне известно занимают 1 такт. Запускал на x64.
Существуют ли архитектуры (arm, mips, ...), для которых эти и другие известные трюки - полноправная оптимизация?

UPD: при делении на 3, разница между сдвигом и делением ощутимая. Вопрос: как так, ведь обе инструкции за 1 такт выполняются?



Последнее исправление: nerdogeek (всего исправлений: 2)

Ответ на: комментарий от Absurd

И MCVC и GCC при сдвиге знаковых переменных используют SAR а не SHR

Иии... что?

$ cat a.c
int main()
{
        int a = -3;

        printf("%d %d\n", a >> 1, a / 2);
}
$ gcc a.c
a.c: In function ‘main’:
a.c:6: warning: incompatible implicit declaration of built-in function ‘printf’

$ ./a.out 
-2 -1
tailgunner ★★★★★
()
Ответ на: комментарий от anonymous

В первом абзаце ты правильно пишешь, что задержку померять нельзя.

Да, для одной инструкции нельзя.

Во втором пишешь, что надо мерять задержку. Шизофазия?

Для миллионов уже можно.

И это разные задержки - ты говоришь про задержку от mov r, m до реального исполнения этой инструкции - это мерить бесполезно, и тут ты прав. А вот время от mov r, m; до НАЧАЛА исполнения слудующего mov r, m; мы можем. И именно это мы и мерием.

Нам не интересно когда х86 это реально посчитает - нам интересно время за которое он пройдёт наши 5ккк инструкций.

Кстати, задержки в два такта быть не может, пайплайн длиннее чем эти твои два такта, задержка даже у mov больше.

Да, но мы и не юзаем конструкции короче пайплайна - тут цикл на миллиард операций, который в десятки-сотни тысяч раз длинне пайплайна.

osh5pntp8
()
Ответ на: комментарий от Absurd

Мне интересен тот сдвиг, который перемещает биты. Который берёт первы/последний бит в зависимости от направления и записывает(вернее перемещает) его в бит, который правее/левее его на N битов, где N - это операнд, в которым ты указываешь насколько надо сдвинуть. И именно про него я и говорю.

osh5pntp8
()
Ответ на: комментарий от tailgunner

Хм, я сейчас за MSVC, тут обе величины одинаковые.

Надо было под gcc проверить, поленился.

Базировался на этом:

http://gcc.gnu.org/ml/gcc/2000-04/msg00152.html

PS:

// c5.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"


int _tmain(int argc, _TCHAR* argv[])
{
	int i = _wtoi(argv[1]);
	int v1 = i>>1;
	int v2 = i/2;
	std::cout<<v1<<" "<<v2<<"\n";
	return 0;
}



; Line 10
        mov     eax, DWORD PTR _i$[ebp]
        sar     eax, 1
        mov     DWORD PTR _v1$[ebp], eax
; Line 11
        mov     eax, DWORD PTR _i$[ebp]
        cdq
        sub     eax, edx
        sar     eax, 1
        mov     DWORD PTR _v2$[ebp], eax
Absurd ★★★
()
Последнее исправление: Absurd (всего исправлений: 1)
Ответ на: комментарий от tailgunner

ЕМНИП, в замене деления на сдвиг у чисел со знаком есть какая-то засада.

Возможно. Но вот такой тест не даёт в результате ни div, ни idiv:

$ cat q.c
int a1(int p1) {
   return p1 / 2;
}

unsigned int a2(unsigned int p1) {
   return p1 / 2;
}
$ gcc -c q.c
$ objdump -d q.o

q.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <a1>:
   0:	55                   	push   %rbp
   1:	48 89 e5             	mov    %rsp,%rbp
   4:	89 7d fc             	mov    %edi,-0x4(%rbp)
   7:	8b 45 fc             	mov    -0x4(%rbp),%eax
   a:	89 c2                	mov    %eax,%edx
   c:	c1 ea 1f             	shr    $0x1f,%edx
   f:	01 d0                	add    %edx,%eax
  11:	d1 f8                	sar    %eax
  13:	5d                   	pop    %rbp
  14:	c3                   	retq   

0000000000000015 <a2>:
  15:	55                   	push   %rbp
  16:	48 89 e5             	mov    %rsp,%rbp
  19:	89 7d fc             	mov    %edi,-0x4(%rbp)
  1c:	8b 45 fc             	mov    -0x4(%rbp),%eax
  1f:	d1 e8                	shr    %eax
  21:	5d                   	pop    %rbp
  22:	c3                   	retq   
$ 

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

На первый взгляд странно, но, с другой стороны, в stree.c гораздо больше работы для оптимизатора.

tailgunner ★★★★★
()

я твой ник читаю как «педогрек» О_О

nokachi
()

Сдвиг это естественная операция для регистров. Так что даже в прекрасном будущем квантовых компутеров он всегда будет оптимален.

А вот деление не факт.

ziemin ★★
()
Ответ на: комментарий от ziemin

Но даже если это не так оперативно запилят патч, который все сдвиги будет в деления переделывать

ziemin ★★
()
Ответ на: комментарий от anonymous

Оптимален?!? Ты видел barrel shifter на кристалле как выглядит?

А как инкремент выглядит? А как инверсия? И, ИЛИ? Что теперь истерику устраивать. В любом случае деление сложней.

ziemin ★★
()
Ответ на: комментарий от nerdogeek

Выкинь тогда свой телефон, раз не нужны - в ARM до Cortex-A15 нет деления (XScale не в счёт).

anonymous
()
Ответ на: комментарий от osh5pntp8

А вот и неандертало из криокамеры вылезло.

anonymous
()
Ответ на: комментарий от Absurd

Конечно арифметический сдвиг для знаковых, как же он иначе знак сохранит, дебилод ты тупой? Истину открыл, лол.

anonymous
()
Ответ на: комментарий от ziemin

А как инкремент выглядит? А как инверсия? И, ИЛИ?

Элементарно. И даже деление запросто может занимать меньшую площадь на кристалле, чем сдвиг.

anonymous
()
Ответ на: комментарий от anonymous

И даже деление запросто может занимать меньшую площадь на кристалле, чем сдвиг.

Ощущение недосказанности возникает. Пример будет?

ziemin ★★
()

эмм... а вот интересно а как ты за один такт сможешь сдвигом получить тот же результат что и при делении на 3 ?

mm3 ★★★
()
Ответ на: комментарий от nikitos

Ммм, я вижу расхождение если числа становятся большими.

А вообще, откуда эта формула? Как её получили? Хотя, вроде, догадался, 1366/(2^12) ~= 1/3

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

фи... до третьего знака после запятой всего

mm3 ★★★
()
Ответ на: комментарий от devl547

нет. сейчас важно выдавать продукт в продакшн. будет тормозить - пох, поставят проц помощнее. если будешь заниматься вылизыванием еще не релизнутого продукта, его релизнут конкренты и не получишь вообще ничего, либо настолько увлечешься оптимизацией, что забыв о функциональности, не выпустишь продукт никогда. Так что мое мнение - делай как умеешь, потом оптимизируй. При этом «как умеешь» уже подразумевает оптимизации того уровня, который разработчику известен, типа если ты знаешь, что лист быстрее вектора на вставках в середину, а твое приложение использует вставки в середину - используй лист. Не знаешь - используй что угодно, лишь бы работала нужная функциональность

marvin_yorke ★★★
()

это и есть тот самый студенческий сипипи, где он используется исключительно потому что научили писать cout << ... а без плюсов оно не работает?

mos ★★☆☆☆
()
Ответ на: комментарий от marvin_yorke

Так что мое мнение - делай как умеешь, потом оптимизируй

А конкуренты выпустят такой же продукт, только без тормозов, и перетянут всех к себе.

devl547 ★★★★★
()
Ответ на: комментарий от nikitos

расхождение уже в четвертом знаке

x4DA ★★★★★
()
Ответ на: комментарий от anonymous

А до тебя не доходит, что на современных x86 сдвиг будет вместо деления подставлен уже на уровне декодера инструкций?

Процитируй Software Optimization Guide for AMD Family 15h Processors или Intel® 64 and IA-32 Architectures Optimization Reference Manual на предмет чего-либо подобного, либо ты обычный врунишка.

trycatch ★★★
()
Ответ на: комментарий от anonymous
>деление сдвигом
>2013
>90 сообщений

линукс такой линукс
anonymous
()
Ответ на: комментарий от arturpub

А ну да, щас не модно осиливать aio, и считать во время кешмисов, а так же юзать префетч.

Т.е. по твоему можно на всё забить, ваять говно, оправдывая это долгим ИО и кешмисами? Собирай с -O0 - всё ровно 98% времени проводит в кешшмисах, или зассышь? Зассышь, ибо балабол.

osh5pntp8
()
Ответ на: комментарий от osh5pntp8

Я даже не знаю с какими ключами собирает мой конпелятор, братиш, и даже как он называется мне неинтересно (не то что бы я не знал), меня интересует лишь насколько в целом адекватно выполняется поставленная задача. Задача выполнялась пару раз не очень адекватно, оба раза профайлер помог.

За балабола ответишь?

arturpub ★★
()
Ответ на: комментарий от arturpub

Я даже не знаю с какими ключами собирает мой конпелятор, братиш, и даже как он называется мне неинтересно (не то что бы я не знал)

Знаешь - ты же выбираешь релиз в своей маздайной студии - выбирай другой.

меня интересует лишь насколько в целом адекватно выполняется поставленная задача.

Т.е. ты увиливаешь от ответа. Что значит адекватно?

Задача выполнялась пару раз не очень адекватно, оба раза профайлер помог.

Значит твои задачи простые, и это просто разные миры.

За балабола ответишь?

Что мне отвечать - ты проболаболился. Ты зассышь собрать свою программулину с O0 - это объективный факт. Ты не знаешь, что такое кешмисс, что такое ИО, но ты позволяешь себе дальше нести тотальную ересь.

Нормальный код не спит на IO, а для обхода кешмиссов люди придумали nt и префетч - почитай, соседний тред какраз об этом. Люди пытаются мыслить по новому, чтобы писать не примитивный последовательный недокод, но если тебе это не надо - это не значит, что дрим это не надо.

Проболился ты так же тем, сказав, что оптимизации ненужны, ибо, как я уже говорил - я бы посмотрел на твою попаболь, если бы из конпеляторов выпилили бы половину оптимизаций, а твоя маздайка запускалась 10минут, а плерок выдавал 5фпс, а броузер страничку рисовал 10секунд, а сервер отвечал на 100запросов в секунду.

osh5pntp8
()
Ответ на: комментарий от marvin_yorke

Но если у тебя деление на переменную, во время компиляции компилятор не сможет предсказать ее значение

не факт. Только если переменная внешняя, или зависит от внешней.

drBatty ★★
()
Ответ на: комментарий от annulen

Деление на 2 компилятор соптимизирует, деление на заранее неизвестную степень двойки - нет. Умножение - тоже.

4.2

ещё с 8086 есть команды сдвига на регистр, а не только на константу.

drBatty ★★
()
Ответ на: комментарий от nerdogeek

Не на всех ЦПУ ПЭВМ на базе ЧПУ есть деление вообще.

Не нужны такие в 2013-м году

нужны и есть. Про DSP слышал?

drBatty ★★
()
Ответ на: комментарий от osh5pntp8

Деление на штеуде занимает в овер 20тактов в лучшем случае. 64битное умножение с 64битным результатом 1такт, с 128битным - 2 такта. Сдиг, если ты сдвигаешь регистр - выполняет 0.5такта. Битовые операции 0.33. Вернее это не время выполнения, а время, через которое штеуд начнёт исполнять следующую инструкцию. Обще время выполнение у деления тактов 50, у сдвигов/битовых операций для регистров - 1такт, для операндов в памяти +~2такта.

а ссылочкой поделишься? Или шышками, которые ты куришь.

drBatty ★★
()
Ответ на: комментарий от anonymous

Деление за один такт в общем случае невозможно.

facepalm

drBatty ★★
()
Ответ на: комментарий от drBatty

В каком лесу я их курил, ах да, в лесу, который к штеудам отношения не имеет - всего лишь Intel® 64 and IA-32 Architectures Optimization Reference Manual.

osh5pntp8
()

Может, компилятор заменяет деление сдвигом?

alix ★★★★
()
Ответ на: комментарий от osh5pntp8

У меня не маздайная студия, но ты почти угадал. Адекватно значит есть несколько критериев, например скорость исполнения, занимаемая память, количество ошибок, наличие требуемого функционала, выпуск релиза как реальный факт. Бабаболка ты, так как я не зассу собирать с -O0, и никогда не переключался с Debug на Release — все три утверждения очень даже факт.

Люди не пытаются мыслить по-новому, людям приходится это делать. Если кто-то юзает префетч и высчитывает такты, чтобы перемолоть сложный поток циферок в картинку на экране, он как-нибудь сам сдвинет вправо вместо того, чтобы поделить пополам, ага.

Ну и не забудь, что вся добавленная ценность в так называемой последней миле, а не битое*ских либах, которые и в глаза никто не видел ;)

arturpub ★★
()
Ответ на: комментарий от osh5pntp8

Оу, и расскажи мне, как умный конпелятор, кроме оптимизации деления сдвигом, использует префетчи, паттерны доступа к памяти, отправляет IO в aio, и вообще сам делает тебе везде хорошо? А то тут высокие технологии, а про конпелятор, с которого все началось, ты лихо забыл.

arturpub ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.