На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
Страницы: (2) [1] 2  все  ( Перейти к последнему сообщению )  
> выравнивание
    Здравствуйте!


    Я, немного, представляю, что такое "выравнивание". Хотелось бы узнать по больше.

    Как я понимаю, обычно данные не выравниваються?
    Но выравнивание увеличивает быстродействие?
    И как его нужно добиваться?
    Если я правильно понял, то данные в структуре выравниваються?
    В каких случаях нужно выравнивание, а в каких нет?

    Растолкуйте пожалуйста!

    Добавлено
    Сейчас почитал, что мне на сабж ответил яндекс, и запутался ещё больше!
      А здесь пробовал читать? Выравнивание.
        Упс. Спасибо! Этой темы не заметил!
        Сейчас почитаю.

        Добавлено
        Нет. Это не то...

        Я не понимаю, изначально компилятор выравнивает данные или нет?

        И ещё, как в памяти размещать выровненные и невыровненные данные.

        Хотябы просто double. Структуры меня сейчас не интересуют.

        Добавлено
        Вот скажем код:
        ExpandedWrap disabled
          void* value;
          value = malloc( sizeof(double) );
          * (double*) value = 1.23;


        Что даст?

        Или например код:
        ExpandedWrap disabled
          double *value = new double(1.23);



        В чом разница?

        А как на счёт просто
        ExpandedWrap disabled
          double value = 1.23;


        В первом случае, как я понимаю, речи о выравнивании, не может быть?

        Во втором... Тут не понимаю...

        А в третьем, по логике, компилятор должен выравнивать?
          Eric-S, вам нужно изначально понять, что такое выравнивание данные. Это всё объяснено по ссылке, которую дал Lumen.
          Далее, функции, выделяющие память, в чистой теории могут вернуть указатель на невыровненные данные. Однако, на сколько я знаю, стандартные реализации malloc и new всегда возвращают указатель, хоть как-то выровненный. Кратность этого выравнивания - 16, 32 и т.д. - зависит от реализации, и не зависит от того, под какие данные мы эту память выделили.
          Так что для первого и второго приведённых вами случаев: да, указатель будет неким образом выровнен.

          В третьем случае данные располагаются в стеке, а не в куче. Опять же, всё равняется в зависимости от реализации. По-моему, на x86 все компиляторы всегда равняют на 4 байта (8 для x64). Увеличивают ли они выравнивание, если sizeof(/*какой-то там тип*/)>4(8), я не в курсе... Знающие люди сейчас просветят :)

          Другое дело, что есть ещё выравнивание в структуре/классе. Об этом написано по данной ссылке.
            Да, там есть про структуры. Но они-то меня сейчас не очень-то интересуют.
            А вот про то что malloc и new возвращают выровненный указатель - не знал. Спасибо.

            Ещё меня интересует:
            не может ли выделенная память оказаться на границе двух страниц?
            Если может, как это скажеться на производительности и стабильности?
              Eric-S, во-первых, если вас волнует производительность, то выравнивание в структурах не должно вас не волновать :) IMHO, надо понимать как там всё устроено, иначе попытки оптимизации могут иметь плачевный итог...
              Далее, на самом деле x86/x64 для большинства своих команд нет требуют выравнивания операндов в памяти и производительности просто так выравнивание не добавляет.
              Но есть одно "но" - это устройство кэша. Существуют так называемые кэш-строки - блоки данных, из которых кэш и состоит. Так вот, если вдруг int размером в 4 байта окажется "на границе" двух кэш-строк (например, два его байта будут "хвостом" одной кэш-строки, а два других - "головой" другой), то при его загрузке будет пенальти. Т.е. это будет медленнее, чем загрузка int'а, целиком лежащего в одной кэш-строке. Сумбурно объяснил, но из меня фиговый педагог :)
              Для того, чтобы гарантировать, что данные не будут пересекать границы кэш-строк, их выравнивают. Общее правило таково: выравнивание POD-типов должно быть равно их размеру для гарантии не пересечения ими границ кэш-строк. Следование этому правилу сулит повышение производительности (будет ли и на сколько зависит от конкретного случая), а так же, возможно, повышенный расход памяти (это тоже зависит от конкретного случая).

              Во-вторых, а почему, собственно, выравнивание должно сказываться на стабильности?
                кэш строки? В первые о таких слышу... Надо будет погуглить.

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


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

                Во-первых я сейчас полез ковыряться с захватом памяти, и задумался над некоторыми местами в своей программе.
                (Спасибо что ответили и успокоили меня).


                А вот стабильность... У процессоров x86 есть некий флаг, который когда установлен позволяет читать невыровненные данные, а когда снят выбрасывает исключение.
                Процесоры alpha вообще ведут себя не очень понятно.
                И как я понял, невыровненные данные в программе, могут привести к сбоям.
                Или быстродействие падает на 100%.
                Но этот вопрос для меня, слишком тёмен...
                  Да, есть у x86 такой флаг. Но какая операционка его устанавливает? :) И зачем? :)
                  Вообще, почитайте Криса Касперски "Техника оптимизации программ. Эффективное использование памяти". Книга очень хорошая, хотя и несколько "попсовата", что ли... не знаю как лучше сказать... :)
                  Самое главное, что эта книга подтолкнёт вас к чтению техдоков на процы :)

                  Кстати, в попытках выровнять указатель "вручную" логическими и/или арифметическим операция можно получить неприятные баги - разумно и осторожно подходите к проблеме, как и всегда с указателями :)
                  Сообщение отредактировано: MyNameIsIgor -
                    Ну да, вот только что полез читать про кэш память. А документация проца давно уже плачет без меня.
                    Всё собираюсь разобраться с оп-кодами, но плохой английский этому мешает.

                    А с выравниванием... Я короче понял, что туда лезть пока мне не стоит. Оно и так будет всё работать, хотя может малехо тормозить.
                      Вообще, если особо не оговаривать компилятор выравнивает данные так, чтобы они были выровнены как надо. Обычно все так и оставляют. Меняют способ выравнивания, только если надо получить структуру данных с каким-то особым размещением данных. Например для работы с каким-нибудь устройством
                        Обращение по некорректно выровненному адресу приводит к undefined behavior.

                        ExpandedWrap disabled
                          int main()
                          {
                              char buf[sizeof(int)];
                              *(int *)buf = 1; // здесь может быть undefined behavior
                          }

                        ExpandedWrap disabled
                          #include <new>
                           
                          int main()
                          {
                              char buf[sizeof(int)];
                              new(buf) int(1); // здесь может быть undefined behavior
                          }
                          А это только для типов размер которых меньше?
                          Мне как раз нужно зарезервировать память под данные. Причом тип указателя неопределён (к сожалению).

                          Скажем, вот такой код допустим?

                          ExpandedWrap disabled
                            // указатель на произвольные данные
                            void *value;
                             
                             
                            // положить данные
                            template< typename type >
                            void set_value( type val )
                            {
                            value = malloc( sizeof( type ));
                            * (type*) value = val;
                            }
                             
                             
                            // получить данные
                            template< typename type >
                            type get_value( void )
                            {
                            return (* (type*) value);
                            }
                             
                             
                            // главная функция
                            int main( void )
                            {
                             
                            set_value( double(1.23) );
                             
                            double d2 = get_value<double>();
                             
                            return 0;
                            }


                          Или всё же придёться заморачиваться с выравниванием?
                            Цитата Eric-S @
                            А это только для типов размер которых меньше?

                            Вопрос мне не ясен.

                            Цитата Eric-S @
                            Скажем, вот такой код допустим?

                            Про malloc в стандарте C99 сказано следующее:

                            Цитата 7.20.3/1
                            The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object and then used to access such an object or an array of such objects in the space allocated (until the space is explicitly deallocated).

                            Полагаю, в стандарте C90 было примерно то же самое. Стандарт C++ в описании malloc ссылается на C90, однако не уточняется, должны ли требования, касающиеся выравнивания, соблюдаться только в отношении POD-типов или non-POD-типов тоже. Но в твоём случае инстанцирование set_value non-POD-типом по-любому недопустимо. Код можно считать корректным, но вот осмысленным - вряд ли.
                            Сообщение отредактировано: Masterkent -
                              Он конечно же не осмысленен. Я его привёл для примера, как можно больше упростив реальный код.

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

                              ExpandedWrap disabled
                                enum {
                                is_null,
                                is_bool,
                                is_number,
                                is_string
                                };
                                 
                                 
                                // класс обёртка для объекта переменной
                                class var
                                {
                                public:
                                 
                                 
                                // конструктор
                                var()
                                {
                                type = is_null;
                                value = 0;
                                }
                                 
                                // деструктор
                                ~var()
                                {
                                unset();
                                }
                                 
                                 
                                // освобождение
                                void unset()
                                {
                                if( value )
                                {
                                free value;
                                value = 0;
                                type = is_null;
                                }
                                }
                                 
                                 
                                // присвоение числа
                                var operator=( long n )
                                {
                                unset();
                                value = malloc( size(long) );
                                * (long*) value = n;
                                type = is_number;
                                }
                                 
                                 
                                // получить число
                                long to_number()
                                {
                                switch( type )
                                {
                                 
                                case is_number;
                                return (* (long*) value );
                                 
                                }
                                }
                                 
                                 
                                private:
                                int type;
                                void * value;
                                };


                              Ну и так далее, в этом роде.

                              к void* пришол после различных вариантов с шаблонами, дочерними классами и наследованием.
                              Этот вариант, не такой красивый, как некоторые, но у меня зато теперь нет тройной матрёшки из ненужных классов.
                              Фактически же явное приведение типов у меня тоже не так часто встречаеться. Но я пока более красивого, но не менее быстрого варианта, ещё не придумал.
                                Угу. Но delete[] ругаеться на указатель типа void*.

                                Добавлено
                                Точнее предупреждает
                                Цитата

                                warning: deleting `void*' is undefined


                                Добавлено
                                ExpandedWrap disabled
                                  #include <stdio.h>
                                   
                                  int main()
                                  {
                                  void *value = new int(123);
                                   
                                  printf("number = %i\n", *(int*) value );
                                   
                                  delete[] value;
                                  return 0;
                                  }


                                По этому я и заюзал старые malloc и free.
                                А в будущем вообще хочу перейти на HeapAlloc и HeapFree.
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0740 ]   [ 16 queries used ]   [ Generated: 29.03.24, 05:12 GMT ]