LINUX.ORG.RU

sizeof выдает неверный размер


0

0

Есть некоторая структура, указателей не содержит. sizeof для этой структуры выдает, ну скажем, 32 байта, хотя реальный размер структуры меньше. Знаю, что в МС вижал Си есть такая штука как выравнивание, и когда этому выравниванию говоришь byte, то sizeof на мою структуру выдает верный размер. Что нужно сказать gcc (какую опцию включить или выключить), чтобы sizeof на структуру выдавал корректный размер? В мане на gcc куча всяких алигнментов, какой нужен в моем случае, я так и не понял. Помогите плиз.

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

> Средства для упаковки/выравнивания есть в любом компиляторе.

А теперь _докажи_ это. Ты видел все компилторы Си для всех платформ ?

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

>А теперь _докажи_ это.

А что тут доказывать? Все я, конечно же, не видел, но упаковка, то есть представление структуры в виде последовательности байт, это то без чего компилятор низкого уровня не может считаться этим самым компилятором низкого уровня, и спорить тут не о чем. GCC, MS VC, Borland, Intel все умеют упаковывать (выравнивать). Sun compiler не видел, но не сомневаюсь, что там тоже есть нужная #pragma.

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

Уверяю тебя - её там нет. И не гоношись, что типа это обязательное требование, ты распрекрасненько можешь делать все те же операции через сдвиги, & и |.

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

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

Об упаковке речь не идет, речь идет о выравнивании по некоторой адресной границе. Как ты уже говорил упаковка дает солидный оверхед => в качестве универсального решения для хранения и передачи данных ее использовать нельзя.

А одинаковое вырванивание на разных компиляторах и разных платформах как я понимаю гарантироваться не может. Или приницпы выравнияния структуры в памяти описаны в стнадрате на Си ?

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

>Уверяю тебя - её там нет.

Не полненился, набрал в гугле packed struct sun compiler и первая ссылка http://docs.sun.com/source/806-3567/sun.html сказала что там #pragma есть. Однако, при не правильной упаковке доступ к такой структуре на спарках вызывает SIGBUS. Насколько я понял, упаковка по 1 байт работает вполне нормально "Interpret access and continue execution".

p.s. и эти люди будут рассказывать мне про сдвиги...

logIN
()

#define BIG 50000000

struct __s1p {
  unsigned char c1;
  unsigned short int i1;
  unsigned short int i2;
  unsigned int i3;
} __attribute__((packed));

struct __s1 : public __s1p {} __attribute ((aligned));

void loop1(__s1 *src)
{
  int i;
  __s1p *pkd;
  pkd = (__s1p*)malloc(sizeof(__s1p));
  for (i = 0; i < BIG; i++) {
    *pkd = *src;
  }
}

void loop2(__s1 *src)
{
  int i;
  unsigned char buf[1+2+2+4+8];
  for(i = 0; i<BIG;i++) {
    buf[0]=src->c1;
    buf[1]=(src->i1)&0xff;
    buf[2]=(src->i1)&0xff00>>8;
    buf[3]=(src->i2)&0xff;
    buf[4]=(src->i2)&0xff00>>8;
    
    buf[5]=(src->i3)&0xff;
    buf[6]=(src->i3)&0xff00>>8;
    buf[7]=(src->i3)&0xff0000>>16;
    buf[8]=(src->i3)&0xff000000>>24;
  }
}

void loop3(__s1 *src)
{
  int i;
  unsigned char buf[1+2+2+4+8];
  for(i = 0; i<BIG; i++) {
    *((unsigned char*)(buf+0)) = src->c1;
    *((unsigned short*)(buf+1)) = src->i1;
    *((unsigned short*)(buf+3)) = src->i2;
    *((unsigned int*)(buf+5)) = src->i3;
  }
}

Без оптимизации (чтоб цикл не съелся, на остальное она не влияет):

1: 6.392s
2: 1.217s
3: 0.586s

При этом, вариант 2 - единственный портабельный, 1 и 3 - опасны, причём 1 - ещё и C++-зависимый, на Си не сработает.

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

> работает вполне нормально

Нормально. Только система трапается и отрабатывает исключения. Так что такой вариант в несколько десятков тысяч раз медленне ручной побайтовой упаковки.

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

>упаковка дает солидный оверхед

Оверхед она, конечно, дает. Но какой, это смотря с чем сравнивать. Если сравнивать с read(struct.member1), read(struct.member2) то оверхед упаковки кажется не таким уж большим.

>Или приницпы выравнияния структуры в памяти описаны в стнадрате на Си

К сожалению, у меня нету стандарта под рукой.
Все что смог найти:

"Note that the alignment of any given struct or union type is required by the ISO C standard to be at least a perfect multiple of the lowest common multiple of the alignments of all of the members of the struct or union in question." -- gcc manual

"If n is the same or greater than the strictest alignment on your platform, (four on Intel, eight on SPARC v8, and 16 on SPARC v9), the directive has the effect of natural alignment. Also, if n is omitted, member alignment reverts to the natural alignment boundaries." -- это из той же доки о Sun compiler.

Судя по всему, по дефолту выравнивание происходит по разрядности архитектуры (имеет смысл и на практике подтверждается, можешь проверить сам, поиграться с разными структурами + sizeof). То есть с правильными (16, 32, 64..) структурами все будет хорошо на любой архитектуре c разрядностью 2^n, то есть на большинстве известных архитектурах. Может и существуют 24, 48-и разрядные, но их я не рассматриваю. И тут даже прагмы/атрибуты не нужны.

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

Кто ж тебя призывает читать поэлементно? Читай буффер, затем разбрасывай сдвигами и &.

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

> Оверхед она, конечно, дает. Но какой, это смотря с чем сравнивать. Если сравнивать с read(struct.member1), read(struct.member2) то оверхед упаковки кажется не таким уж большим.

Если использовать структуры только для записи/чтения данных, то - да. Если активно используешь структуры в расчетной части программы, то тут оверхед будет существенен. Причем еще непонятно что лучше получить солидный оверхед на обработке данных или на сериализации неупакованной структуры.

> То есть с правильными (16, 32, 64..) структурами все будет хорошо на любой архитектуре c разрядностью 2^n, то есть на большинстве известных архитектурах.

Тут возникнет проблема с совместимостью 16 -> 32 -> 64 и т.д. Опять оверхедить придется

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

#include <iostream>
#include <sys/time.h>

using namespace std;

class delta_time {
timeval past;
public:
delta_time();
double dt();
};

delta_time::delta_time() {
gettimeofday(&past, NULL);
}

double delta_time::dt() {
timeval now;
gettimeofday(&now, NULL);
double delta;
delta = now.tv_sec - past.tv_sec;
delta += (double)now.tv_usec/1000000 - (double)past.tv_usec/1000000;
past = now;
return delta;
}

#define BIG 50000000

struct __s1p {
unsigned char c1;
unsigned short int i1;
unsigned short int i2;
unsigned int i3;
} __attribute__((packed));

struct __s1 : public __s1p {} __attribute ((aligned));

void loop1(__s1 *src)
{
int i;
__s1p *pkd;
pkd = (__s1p*)malloc(sizeof(__s1p));
for (i = 0; i < BIG; i++) {
*pkd = *src;
}
}

void loop2(__s1 *src)
{
int i;
unsigned char buf[1+2+2+4+8];
for(i = 0; i<BIG;i++) {
buf[0]=src->c1;
buf[1]=(src->i1)&0xff;
buf[2]=(src->i1)&0xff00>>8;
buf[3]=(src->i2)&0xff;
buf[4]=(src->i2)&0xff00>>8;

buf[5]=(src->i3)&0xff;
buf[6]=(src->i3)&0xff00>>8;
buf[7]=(src->i3)&0xff0000>>16;
buf[8]=(src->i3)&0xff000000>>24;
}
}

void loop3(__s1 *src)
{
int i;
unsigned char buf[1+2+2+4+8];
for(i = 0; i<BIG; i++) {
*((unsigned char*)(buf+0)) = src->c1;
*((unsigned short*)(buf+1)) = src->i1;
*((unsigned short*)(buf+3)) = src->i2;
*((unsigned int*)(buf+5)) = src->i3;
}
}

int main () {
static __s1 src[BIG];
delta_time dt;
loop1(src);
cout << dt.dt() << endl;
loop2(src);
cout << dt.dt() << endl;
loop3(src);
cout << dt.dt() << endl;

return 0;
}

-------

$ gcc --version
gcc (GCC) 4.0.1

$ cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 8
model name : Pentium III (Coppermine)
stepping : 6
cpu MHz : 801.929
...

$ g++ c3.cxx && ./a.out
0.635939
1.71735
0.879816

---

А тут будет интереснее (BIG пришлось уменьшить до 500000, памяти всего 24 mb):

$ gcc --version
gcc (GCC) 3.4.4 (CRUX)

$ cat /proc/cpuinfo
processor : 0
vendor_id : AuthenticAMD
cpu family : 5
model : 0
model name : 02/00
stepping : 1
cpu MHz : 74.713
...


$ g++ c3.cxx && ./a.out
0.480727
1.30263
0.844148


---

Как видно, мой вариант 1 быстрее. C включенной оптимизацией, результаты всех трех близки к погрешности. ;-)

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

> C включенной оптимизацией, результаты всех трех близки к погрешности. ;-)

Нельзя включать оптимизацию. Она цикл съедает - надо тогда что либо делать на каждом шагу с результатом.

Да, кстати, на хера ты столько памяти под src выделяешь? :-O

Не надо этого делать!

Кстати, анализ ассемблерного кода вызывает серьёзные подозрения, что aligned ни фига не работает.

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

>Тут возникнет проблема с совместимостью 16 -> 32 -> 64 и т.д.

Нету тут проблемы, в том то и дело. Представь регистры, и как туда будет помещаться структуры размером 16, 32,... бит на разных архитектурах.

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

>Не надо этого делать!

oops, не посмотрел, что это пустые циклы

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

Вот и получается, что твой вариант неустойчивый. 

gcc4 на AMD64:

0.160088
1.11949
0.480003


gcc4 на P4 3GHz:

6.58253
1.3438
0.490208

gcc3.3.3 на P3 1GHz:

0.562048
1.23366
0.60753

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

>Вот и получается, что твой вариант неустойчивый.

Генту свою чини, у меня все ок. Или проверь не жрет ли у тебя что-нибуть cpu.

-------

cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 15
model : 2
model name : Intel(R) Pentium(R) 4 CPU 2.40GHz
stepping : 9
cpu MHz : 2411.817
cache size : 512 KB


gcc --version
gcc (GCC) 3.4.4 (CRUX)

./a.out
0.191963
0.448548
0.241885

-------

processor : 0
vendor_id : GenuineIntel
cpu family : 15
model : 2
model name : Intel(R) Pentium(R) 4 CPU 2.80GHz
stepping : 9
cpu MHz : 2793.349
cache size : 512 KB
...
processor : 1
vendor_id : GenuineIntel
cpu family : 15
model : 2
model name : Intel(R) Pentium(R) 4 CPU 2.80GHz
stepping : 9
cpu MHz : 2793.349
cache size : 512 KB


gcc --version
gcc (GCC) 3.4.4


./a.out
0.164145
0.37
0.202287

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

> Генту свою чини, у меня все ок.

 Да, это gcc4 из debian sid кривенький. gcc-3.3 там же:

0.232336
0.601899
0.383053

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

 И я всё равно не понимаю, как оно копирует структуры в первом варианте:

        movl    -12(%ebp), %eax
        movl    8(%ebp), %edx
        movl    %eax, %edi
        movl    %edx, %esi
        cld
        movl    $11, %ecx
        rep
        movsb
        leal    -16(%ebp), %eax
        incl    (%eax)


 Видно, что это тупое копирование 11 байт памяти. Бред!

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

Какой то ущербный вариант 1 получился по отношению к другим, я имею ввиду malloc. Почему бы просто не создать объект на стеке?

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

>я имею ввиду malloc

Он все равно не в цикле... а так, конечно, можно.

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

malloc не в цикле.

Ну так что, есть у кого доступ к неинтелам? Под ARM для Symbian собирать мне как-то лениво...

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

>Зарекался же с пионЭрами спорить...

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

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

>А теперь хотелось бы увидеть аналог варианта N1, но для pain C.

Создание двух структур (packed+aligned) + поэлементное копирование при i/o. К сожалению, так красиво как в вариант1 позволяет сделать наследование в С++, которое, естественно в Си отсутствует. Но это ничего не меняет. Запись/чтение в/из файлов/сокетов происходит теми же структурами, которые тут так втаптывали в землю.
И ещё раз: мы рассматриваем вариант неправильных структур, поэтому тут такие заморочки.

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

>так нельзя писать.

А я тут не причем, не я писал. ;-)

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

Естественно так нельзя. Этот вариант делался для сравнения ассемблерного кода с тем, что даёт копирование структуры. Оказалось, что ни фига не похоже.

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

Ты разработчикам форматов прогрузи про "неправильные структуры". Вот очень надо авторам формата JPEG о выравнивании думать, да?

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

>Вот очень надо авторам формата JPEG

Да что ты за этот JPEG ухватился. Ну используют там что-то другое (кстати, что именно? мне лениво смотреть). А в других форматах используют структуры. И что? Да ничего. Как удобнее так и делают. Никаких запретов тут нету.

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

а можно мнесто buf[3]=(src->i2)&0xff; buf[4]=(src->i2)&0xff00>>8; сделать вот так buf[3]= *((char*)&src->i2); buf[4]= *(((char*)&src->i2) + 1); ? (выдрал кусок из loop2)

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

а можно мнесто
buf[3]=(src->i2)&0xff;
buf[4]=(src->i2)&0xff00>>8;
сделать вот так
buf[3]= *((char*)&src->i2);
buf[4]= *(((char*)&src->i2) + 1);
?
(выдрал кусок из loop2)

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

Можно, конечно, но тогда теряется универсальность формата buf (при текущей реализации endianness фиксирован).

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

В любом универсальном формате проблема endianness остаётся.

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

народ, вы совсем забыли про хосты на которых байт состоит из 9 или 36 бит. предлагаю обсудить портабильность на такие архитектуры

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

Хотел было написать про совковые "Минск-32" и подобные, но потом подумал - ну нахрен ...

BottleHunter
()

Резюме по результатам проведённых тестов: C++ таки наконец хоть в чём-то зарулил. Такая фича не помешала бы в plain C, потому как C++ - злобный overkill. logIN реабилитирован, за исключением проблемы endianness.

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

Блин. Те же MIPS-ы до сих пор живы, а у них, внутри ОДНОЙ серии есть железки с РАЗНЫМ endianness!!! Так что конкретно этот вопрос портабельности очень даже важен.

anonymous
()

Имхо лучше посылать текстовые данные, хотя бы потому что в качестве клиента подойдет простой shell скрипт, не буду спорить что бинарные структуры посылать нельзя. Можно, но гораздо переносимей использовать какую-нибудь текстовую альтернативу. Это решает и облегчает сразу кучу проблем. (=

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

> Имхо лучше посылать текстовые данные

Куда там! Мы тут сочли неприемлимой разницу в 2 раза в скорости одной только обработки структур, без учёта ожидания I/O (ну не знает logIN, что это не bottleneck ни разу).

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

>Имхо лучше посылать текстовые данные, хотя бы потому что в качестве клиента подойдет простой shell

Не все данные предназначены для чтения человеком, или для обработки простейшими алгоритмами. Например, те же индексы. Или протоколы низкого уровня (tcp/ip, X11) - они не предусматривают чтение с экрана.


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

>Мы тут сочли неприемлимой разницу в 2 раза в скорости одной только обработки структур

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

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

>logIN реабилитирован, за исключением проблемы endianness.

А я и не дескредитировал себя никогда. Просто теоретики-пуристы сами не знают какие еще ограничения выдумать. То goto запрещают, теперь еще и структуры.

Что касается endianness, то эта проблема решается исключительно однобайтовыми текстовыми протоколами. И тут опять вылезает куча недостатков.

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

В общем, определенные результаты достигнуты, дальнейшее обсуждение излишне. Каждый сам сделает выводы и решит где и как использовать структуры. Текстовые протоколы это уже тема для другого обсуждения.

Парни, вы славно подрудились! Пойду коньячку выпью :-)

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

> эта проблема решается исключительно однобайтовыми текстовыми протоколами

Вах, слюшай, зачем бредишь, да? Проблема решается чёткой спецификацией порядка байт в слове в протоколе - и всё. Он остаётся таким же бинарным, только на том конце, где endianness платформы не совпадает с таковой в протоколе требуется дополнительное преобразование. Что ты заладил про "исключительно", "текстовыми". Бред какой-то!

И не гони про недостатки. Это реальный мир, в нём эта проблема будет всегда, и не тебе судить о недостатках.

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

>Проблема решается чёткой спецификацией порядка байт в слове в протоколе

Может будем читать весь тред? Про это я говорил в самом начале - это в случае бинарных протоколов (или текстовых с wchar).

>и не тебе судить о недостатках

Как программисту, именно мне об этом и судить.

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

> Как программисту, именно мне об этом и судить.

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

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