avva: (moose)
[personal profile] avva
Some dark corners of C

Набор забавных малоизвестных особенностей C. Самый интересный пример там, на мой взгляд - следующие две функции:
void foo1(int *x, int *y, int *z) {
  *x += *z;
  *y += *z;
}

void foo2(int *x, int *y, int *z) {
  int w = *z;
  *x += w;
  *y += w;
}


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

Правильный ответ: в первом случае компилятор не может исключить возможность того, что x==z.

Я также не знал, что в C99 есть ключевое слово restrict, которое решает эту проблему.

Date: 2013-03-09 12:53 pm (UTC)
From: [identity profile] pphantom.livejournal.com
Я бы не назвал ее малоизвестной. Программисты, которым нужно выжимать максимальную производительность из кода, обычно об этом знают. А приведенный пример (и исправление ситуации с помощью restrict) - чуть ли не главная причина меньшей производительности Си по сравнению с Фортраном в вычислительных задачах.

Date: 2013-03-09 12:57 pm (UTC)
From: [identity profile] avva.livejournal.com
Таких программистов относительно мало.

Ну и вообще - анти-интуитивные вещи забываются или не приходят на ум. Программист знает, что конечно же x!=z, и для того, чтобы помнить, что компилятор об этом не знает, нужна ментальная дисциплина. Скажу про себя: я хорошо знаю об этой проблеме теоретически, даже сталкивался с ней раз или два практически, оптимизируя какой-то код (очень давно), и все равно во время чтения этой презентации заново удивился.

Date: 2013-03-09 09:20 pm (UTC)
From: [identity profile] vodianoj.livejournal.com
Программист, разработчик функции, как мне кажется, так же как и компилятор не знает, что x!=z.
Если программист, хотел сделать то, что делает фу2 и при этом он написал фу1, то это просто баг потому, что в случаях, когда x==y эти функции ведут себя по разному. Меньшая эффективность тут как раз наименьшая из возможных проблем.

Date: 2013-03-10 08:18 am (UTC)
From: [identity profile] huzhepidarasa.livejournal.com
Это функция слабенькая, только для примера. Настоящие функции — это перемножение матриц, например, 100500 х 100500. А теперь внимание, вопрос: как такую функцию написать? Как foo1 или как foo2?

Date: 2013-03-11 06:15 am (UTC)
From: [identity profile] vodianoj.livejournal.com
В таком случае я бы не выделял это в отдельную функцию - лишняя строчка кода, но не будет потенциального бага.

Date: 2013-03-09 01:15 pm (UTC)
From: [identity profile] igorlord.livejournal.com
The possibility of aliasing seems like a rather obvious everyday concern.

In any case, both examples show a somewhat defective C code that I would not pass on a code review. When *z is not intended to be modified, I would insist that it is marked as const. I do wonder if that const would cause the compiler to assume no aliasing. Something I may want to try...

Date: 2013-03-09 02:31 pm (UTC)
From: [identity profile] salas.livejournal.com
Checked on gcc 4.7.2 (Ubuntu amd64), doesn't look like that. If *b after foo1(a, b, a) depended on that const, this corner would be even darker.

Date: 2013-03-09 01:22 pm (UTC)
From: [identity profile] deadkittten.livejournal.com
Добавлю, что gcc с опциями -O3 или -O4 начинает "самовольно" догадываться, верно ли, что x==z . Иногда это приводит к малопонятным ошибкам.

Date: 2013-03-09 01:25 pm (UTC)
From: [identity profile] panikowsky.livejournal.com
Если заголовок Вашего поста - это перевод названия презентации, то он не совсем точен: мне кажется, "dark corners of C" лучше перевести как "темные закоулки языка C".

Date: 2013-03-09 01:31 pm (UTC)
From: [identity profile] avva.livejournal.com
да, вы правы, спасибо. Мне не пришел в голову хороший аналог dark corners. Сейчас изменю на ваш вариант.

Date: 2013-03-09 01:32 pm (UTC)
From: [identity profile] hryu.livejournal.com
Да, как-то не сообразил :)

Date: 2013-03-09 01:48 pm (UTC)
From: [identity profile] http://users.livejournal.com/_winnie/
> компилятор генерирует более эффективный код для второй из них, foo2
Нужна оговорка, "обычно генерирует" :) У меня тут вчера было волшебство - "менее эффективный" код с assert-тами работает быстрее чем оптимизированная версия без них. http://users.livejournal.com/_winnie/379152.html

Date: 2013-03-09 02:06 pm (UTC)
From: [identity profile] kot-begemot.livejournal.com
Потому что кладёт w в регистр и лазает в память за значением *z один раз, а не два.

Date: 2013-03-09 02:20 pm (UTC)
From: [identity profile] penguinny.livejournal.com
Опыт работы в ассемблере даёт это знание самым непосредственным образом. Кстати, тесно связанный трюк в ассемблере Z80 служил как один из самых быстрых (и точно самый компактный) способ очистки экрана:

ld hl,screen_addr
ld de,screen_addr+1
ld bc,6143
ld (hl),0
ldir

Команда ldir копирует bc байт информации, начинающейся с адреса hl, в блок памяти, начинающийся с адреса de. С учётом выбранных адресов, 0 вручную пишется в первый байт, а затем копируется из первого во второй, из второго в третий, и т.д. Экран очищается по индукции.

Про ещё более быстрый способ очистки экрана я лучше рассказывать не буду.

Date: 2013-03-09 04:13 pm (UTC)
From: [identity profile] kray-zemli.livejournal.com
говорят, как-то через стек можно ещё было. вы его имели в виду?

Date: 2013-03-09 04:31 pm (UTC)
From: [identity profile] penguinny.livejournal.com
Да, именно так. Регистр стека устанавливался в конце экрана, один из 16-битных регистров очищался и затем в частично развёрнутом цикле многократно сохранялся в стек. 16-битные операции на 8-битном процессоре! Шок.

Date: 2013-03-09 02:24 pm (UTC)
From: [identity profile] alex-vinokur.livejournal.com
http://demin.ws/blog/russian/2010/08/01/volatile-is-very-strong-modifier/

Date: 2013-03-09 02:40 pm (UTC)
From: [identity profile] avva.livejournal.com
Я этого не видел раньше, кажется. Красиво!

Date: 2013-03-09 05:32 pm (UTC)
From: [identity profile] huzhepidarasa.livejournal.com
А это видели?
http://channel9.msdn.com/posts/C-and-Beyond-2012-Herb-Sutter-You-dont-know-blank-and-blank

Date: 2013-03-10 09:26 am (UTC)
From: [identity profile] gdy.livejournal.com
А почему очень сильный-то?
Не сильнее const - если бы у потока не было оператора для const char*, а только для char*, та же ситуация бы была, нет?

Date: 2013-03-10 10:05 am (UTC)
From: [identity profile] alex-vinokur.livejournal.com
Да, скорее всего, так.

Date: 2013-03-10 09:44 pm (UTC)
From: [identity profile] huzhepidarasa.livejournal.com
Сильный потому, что оператор для const char* есть ;)

Date: 2013-03-09 04:40 pm (UTC)
From: [identity profile] qehgt.livejournal.com
Ещё можно написать "const int* z" в параметрах, это позволит компилятору считать, что "*z" не будет меняться при работе с "*x" и "*y"

Date: 2013-03-09 05:30 pm (UTC)
From: [identity profile] huzhepidarasa.livejournal.com
Не позволит. "const int* z" означает только, что *z не может меняться через z.

Date: 2013-03-09 05:48 pm (UTC)
From: [identity profile] qehgt.livejournal.com
Позволит. Могу место в Стандарте показать.

Date: 2013-03-09 05:58 pm (UTC)
From: [identity profile] huzhepidarasa.livejournal.com
Покажите.


void foo(int *x, int *y, const int *z) {
*x += *z;
*y += *z;
}

int xx = 3, yy = 4;

void bar()
{
foo(&xx, &yy, &xx);
}


Что говорит Стандарт по этому поводу?
Edited Date: 2013-03-09 06:03 pm (UTC)

Date: 2013-03-09 06:12 pm (UTC)
From: [identity profile] qehgt.livejournal.com
Пожалуй, был не прав.

Date: 2013-03-09 05:07 pm (UTC)
From: [identity profile] yms.livejournal.com
надо же, не знал, сколько чудес кроется за C99...
причем не все из них вошли в C++, даже 11.
Edited Date: 2013-03-09 05:09 pm (UTC)

Date: 2013-03-09 05:29 pm (UTC)
From: [identity profile] pphantom.livejournal.com
В С99 действительно много весьма полезных добавлений, причем они удачно дополняют язык, не меняя его идеологии. C++ дополнялся намного хуже, превратившись в громадную солянку из всего подряд.

Date: 2013-03-09 09:44 pm (UTC)
From: [identity profile] asox.livejournal.com
С99 шёл своим путём, отдельным от С++

Date: 2013-03-10 02:01 am (UTC)
From: [identity profile] yms.livejournal.com
Да теперь уж понятно... Я чистым си перестал интересоваться году в 1992 :)

Date: 2013-03-10 09:31 am (UTC)
From: [identity profile] asox.livejournal.com
Ну я тоже, собснна. Так только слышал краем уха.
С99 вообще странно существует - почти как f77, f90 и т.д.

Гавно

Date: 2013-03-09 07:18 pm (UTC)
From: [identity profile] Андрей Иванов (from livejournal.com)
Из вашего поста следует, что за время от создания Си до сего дня ничего в программировании не сделано, даже Фортран не обогнали, тоесть идёт деградация.

Спасибо за откровенность.
Я с этим согласен. Например, найдите картинку в Гугле "Одноногий мужик в шляпе" и посмотрите сто первых результатов. Сразу видно достижения функционального программирования и прочего компьютерсаенс: полное гавно.

Date: 2013-03-09 08:58 pm (UTC)
From: [identity profile] helvegr.livejournal.com
> Я также не знал, что в C99 есть ключевое слово restrict

А Вы знаете про strict aliasing?
Благодаря нему поведение вот такого вот кода (вполне невинно выглядящего на первый взгляд) неопределено (http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html):

uint32_t 
swap_words( uint32_t arg )
{
  uint16_t* const sp = (uint16_t*)&arg;
  uint16_t        hi = sp[0];
  uint16_t        lo = sp[1];
  
  sp[1] = hi;
  sp[0] = lo;

  return (arg);
}

Date: 2013-03-09 09:15 pm (UTC)
From: [identity profile] migmit.livejournal.com
Не знаю, сообразил бы я про x==z (я читаю через RSS, и ответ успел увидеть до того, как задумался над вопросом), но точно знаю, что на практике я бы написал второй вариант, а не первый. Просто потому, что дважды разыменовывать один указатель — некрасиво. К тому же, компиляторы C и C++ я привык a priori считать экстремально тупыми, и даже не задумываюсь над такими оптимизациями.

Date: 2013-03-09 09:47 pm (UTC)
From: [identity profile] asox.livejournal.com
О!
+100500
А в общем случае - фиг знает что и как соптимизирует компилятор, и подгонять код под оптимизацию конкретной версии конкретного компилятора - извращение.

Date: 2013-03-10 08:14 am (UTC)
From: [identity profile] huzhepidarasa.livejournal.com
От этой привычки надо бы потихоньку отказываться. Компиляторы нонче оптимизируют лучше среднего программиста.

Date: 2013-03-10 09:27 am (UTC)
From: [identity profile] migmit.livejournal.com
Как показывает исходный пост — не надо.

Другой вопрос, что к оптимизации надо подходить грамотно, сначала делая профайлинг.

Date: 2013-03-10 04:36 pm (UTC)
From: [identity profile] huzhepidarasa.livejournal.com
Нет, не показывает. Он показывает, если он что-то показывает, что программист, не задумываясь, вносит «оптимизации», меняющие смысл программы. А компилятор нет.

Date: 2013-03-10 04:54 pm (UTC)
From: [identity profile] migmit.livejournal.com
Ну как — не показывает. Если от привычки не отказываться, то в подобную ловушку не попадаешь.

Другой вопрос, что, опять-таки, для меня основной критерий — это не тупость компилятора, а "некрасиво".

Date: 2013-03-10 08:44 am (UTC)
From: [identity profile] mopexod.livejournal.com
Вот же ж, пардон, блядь какая, это компилятор! Да нахер он об этом думает? Это же замена однгого side-effect-а на другой. Причем, если первый еще можно отладить, но на втором будешь тупить два часа, не веря в такое вероломство.

Date: 2013-03-10 09:48 am (UTC)
From: [identity profile] mopexod.livejournal.com
Посмотрел внимательно и понял: херню написал.

Date: 2013-03-10 03:19 pm (UTC)
From: [identity profile] alex p (from livejournal.com)
А если после int w = *z; написать x = &w; компилятор догадается что w изменится в первом суммировании и его во втором брать из регистра нельзя а только из памяти?

Date: 2013-03-10 03:20 pm (UTC)
From: [identity profile] avva.livejournal.com
Да, конечно.

December 2025

S M T W T F S
  123 4 56
78 9 10 11 1213
1415 1617181920
21 22 23 24 2526 27
28293031   

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Dec. 29th, 2025 07:24 am
Powered by Dreamwidth Studios