Russian Qt Forum

Программирование => С/C++ => Тема начата: __Heaven__ от Январь 19, 2016, 12:55



Название: reinterpret_cast класса в массив
Отправлено: __Heaven__ от Январь 19, 2016, 12:55
Привет, друзья!
Интересуюсь такой темой. Возможно ли гарантировано перевести массив QVector3D в массив float?
Для наполнения буфера QOpenGLBuffer нужно в качестве void *data передавать координаты вершин (xyzxyzxyz...). Но если я храню вершины именно в таком виде, то проводить какие-либо операции с таким массивом неудобно. Постоянно приходится индексы домножать на 3... Вот и интересуюсь возможностью передавать в void *data указатель на массив QVector3D.


Название: Re: reinterpret_cast класса в массив
Отправлено: Igors от Январь 19, 2016, 13:19
Но если я храню вершины именно в таком виде, то проводить какие-либо операции с таким массивом неудобно.
Та неужели ??? И вумные итераторы никак не помогают?  :)

Интересуюсь такой темой. Возможно ли гарантировано перевести массив QVector3D в массив float?
Таких средств в языке нет, все приводят. Для очистки совести вставьте
Код
C++ (Qt)
Q_ASSERT(sizeof(QVector3D) == sizeof(float) * 3);


Название: Re: reinterpret_cast класса в массив
Отправлено: __Heaven__ от Январь 19, 2016, 13:27
Нет, итераторы помогают в другой задаче.
В этой же я рассматриваю вариант иметь QVector<QVector3D> vertices и в функцию наполнения буфера вставлять vertices.data(). Но я боюсь так делать, потому что завтра класс QVector3D может обзавестись новыми членами...


Название: Re: reinterpret_cast класса в массив
Отправлено: Igors от Январь 19, 2016, 14:13
Но я боюсь так делать, потому что завтра класс QVector3D может обзавестись новыми членами...
Ну это вряд ли, такие "конкретные" классы практически нерасширяемы. На всякий случай гляньте что они используют в Qt3D - там тоже надо скармливать OpenGL флоты


Название: Re: reinterpret_cast класса в массив
Отправлено: ssoft от Январь 19, 2016, 17:14
Да, можно.

Структура Vertex, например, плотно пакуется, поэтому можно использовать

Код
C++ (Qt)
struct Vertex
{
  float x;
  float y;
  float z;
}
 
Q_STATIC_ASSERT( sizeof( Vertex ) == 3 * sizeof( floet ) );
 
QVector< Vertex > vertices;
QOpenGLBuffer buffer;
 
...
 
buffer.write( offset, vertices.constData(), 3 * vertices.count() );
 

Все же нужно гарантировать размер, поэтому используем Q_STATIC_ASSERT


Название: Re: reinterpret_cast класса в массив
Отправлено: Old от Январь 20, 2016, 07:51
Но я боюсь так делать, потому что завтра класс QVector3D может обзавестись новыми членами...
Код
C++ (Qt)
typedef QVector3D Vector3D;
 

а если завтра поломают кютешный, то пишете свой класс, и меняете только тайпдеф:
Код
C++ (Qt)
typedef MyValidVector3D Vector3D;
 


Название: Re: reinterpret_cast класса в массив
Отправлено: __Heaven__ от Январь 20, 2016, 09:27
Old, мне нравится ваш вариант.
А нам стандарт гарантирует, что x и z не будут поменяны местами в памяти? Ну это так, уже перфекционизм :)


Название: Re: reinterpret_cast класса в массив
Отправлено: Igors от Январь 20, 2016, 12:05
А нам стандарт гарантирует, что x и z не будут поменяны местами в памяти?
Да

Напомню что QVector3D - мягко говоря, далеко не самая лучшая реализация класса вектор/точка. Напр сейчас большинство таких классов в различных приложениях имеют не 3, а 4 float. Возможно есть смысл подыскать более продвинутый класс, зависимостей он не тянет


Название: Re: reinterpret_cast класса в массив
Отправлено: __Heaven__ от Январь 20, 2016, 12:35
Igors, вы можете посоветовать какой-то конкретный класс?
Для 4 float можно же взять QVector4D


Название: Re: reinterpret_cast класса в массив
Отправлено: Igors от Январь 20, 2016, 13:21
Igors, вы можете посоветовать какой-то конкретный класс?
Напр очень неплох btVector3 (open sources Bullet). Или погуглите Vec3, Vector3, Vec3f
Для 4 float можно же взять QVector4D
Тут дело не просто в "четырех", подумайте зачем так сейчас делают (Вы знаете ответ, даже создавали такие темы)


Название: Re: reinterpret_cast класса в массив
Отправлено: __Heaven__ от Январь 20, 2016, 14:17
Честно говоря, я не увидел преимуществ у btVector3 над QVector3D...

Тут дело не просто в "четырех"
Не понимаю, на что именно вы намекаете. Четвёртая координата - так называемый множитель. Я всегда использую w=1.


Название: Re: reinterpret_cast класса в массив
Отправлено: __Heaven__ от Январь 20, 2016, 14:18
у btVector3 w=0 в конструкторе


Название: Re: reinterpret_cast класса в массив
Отправлено: Igors от Январь 20, 2016, 14:48
Не понимаю, на что именно вы намекаете. Четвёртая координата - так называемый множитель. Я всегда использую w=1.
Видимо Вы просмотрели doхygen документацию, а Вы сам хедер-то откройте (btVector3.h), там "аж кишит", какие уж тут "намеки".

Честно говоря, я не увидел преимуществ у btVector3 над QVector3D...
А Вы поизучайте, сравните напр доступ к членам, конструкторы по умолчанию, операции скалярного и векторного произведения и.т.п. Возможно Вы обнаружите что btVector3 многое делает "совсем не так как в книжке" :) Он заточен на профессионала, а QVector3D - на читателя ассыстента.


Название: Re: reinterpret_cast класса в массив
Отправлено: Old от Январь 20, 2016, 15:13
Он заточен на профессионала, а QVector3D - на читателя ассыстента.
"Ты посмотри какой котон, какие лейблы..." (c) СОиП
:)


Название: Re: reinterpret_cast класса в массив
Отправлено: Racheengel от Январь 20, 2016, 22:58
QVector3D богомерзок... Юзайте флоты и будэ щастя...


Название: Re: reinterpret_cast класса в массив
Отправлено: Igors от Январь 21, 2016, 04:58
QVector3D богомерзок...
А на чем основывается это мнение ?


Название: Re: reinterpret_cast класса в массив
Отправлено: Racheengel от Январь 21, 2016, 12:16
Ну вот нужно, например, glVertexPointer использовать.
Массив из QVector3D надо сперва преобразовать в массив флотов.
Потому что чего-то типа QVector3DList у нас нету, и никто не гарантирует, что эти вектора будут лежать в памяти последовательно.


Название: Re: reinterpret_cast класса в массив
Отправлено: Old от Январь 21, 2016, 12:21
Ну вот нужно, например, glVertexPointer использовать.
Массив из QVector3D надо сперва преобразовать в массив флотов.
Потому что чего-то типа QVector3DList у нас нету, и никто не гарантирует, что эти вектора будут лежать в памяти последовательно.
QVector3D сейчас это:
Код
C++ (Qt)
struct
{
   float x;
   float y;
   float z;
}
 
со всеми гарантиями, что вектор таких структур будет хранить их последовательно.

То, что завтра этот класс может изменить свою внутреннюю структуру, это другой вопрос.


Название: Re: reinterpret_cast класса в массив
Отправлено: Igors от Январь 21, 2016, 12:37
Ну вот нужно, например, glVertexPointer использовать.
Массив из QVector3D надо сперва преобразовать в массив флотов.
Потому что чего-то типа QVector3DList у нас нету, и никто не гарантирует, что эти вектора будут лежать в памяти последовательно.
glVertexPointer (как и др ф-ции передачи геометрии) имеет stride, который как раз для данных "не идущих подряд". Так что это не проблема

Еще аргументы против QVector3D? Или Вы просто так сказали про "богомерзкость"?  :)


Название: Re: reinterpret_cast класса в массив
Отправлено: Racheengel от Январь 21, 2016, 12:51
То, что завтра этот класс может изменить свою внутреннюю структуру, это другой вопрос.

Как минимум это уже потенциальная проблема.
К тому же при аллокации вектора будут вызываться конструкторы класса, соответственно деструкторы при деаллокации. Производительность будет страдать.

stride может сам по себе и не проблема, но это еще один дополнительный параметр, который придется принимать во внимание.



Название: Re: reinterpret_cast класса в массив
Отправлено: __Heaven__ от Январь 21, 2016, 12:55
Так а конструктор в этом классе ничем не отличается от обычного копирования float, деструктор по умолчанию. Думаю, что все потенциально лишние копирования компилятор соптимизирует.


Название: Re: reinterpret_cast класса в массив
Отправлено: Racheengel от Январь 21, 2016, 13:05
Так а конструктор в этом классе ничем не отличается от обычного копирования float, деструктор по умолчанию. Думаю, что все потенциально лишние копирования компилятор соптимизирует.

Хорошо, если так. Но если вдруг разработчик решит еще пару фич туда запихать?


Название: Re: reinterpret_cast класса в массив
Отправлено: Old от Январь 21, 2016, 13:07
Хорошо, если так. Но если вдруг разработчик решит еще пару фич туда запихать?
А вот тогда, его придется заменить своим, с нужным устройством и поведением. :)
Но это "тогда", а чем он вам сейчас не нравиться?


Название: Re: reinterpret_cast класса в массив
Отправлено: __Heaven__ от Январь 21, 2016, 13:08
Ну тогда
а если завтра поломают кютешный, то пишете свой класс, и меняете только тайпдеф:
Код
C++ (Qt)
typedef MyValidVector3D Vector3D;
 


Название: Re: reinterpret_cast класса в массив
Отправлено: Racheengel от Январь 21, 2016, 13:14
Но это "тогда", а чем он вам сейчас не нравиться?

Не люблю неожиданностей и неопределенности.
Нет, как отдельное представление трехмерного вектора, класс неплох :)
distanceToLine там всякое умеет и т.д.
Но если идет речь о приложении, где производительность превыше всего - хочется иметь больше контроля над процессом.


Название: Re: reinterpret_cast класса в массив
Отправлено: Igors от Январь 21, 2016, 13:20
stride может сам по себе и не проблема, но это еще один дополнительный параметр, который придется принимать во внимание.
Воткнуть вместо нуля sizeof - ой как много работы  :)

К тому же при аллокации вектора будут вызываться конструкторы класса, соответственно деструкторы при деаллокации. Производительность будет страдать.
Совершенно верно
Так а конструктор в этом классе ничем не отличается от обычного копирования float, деструктор по умолчанию. Думаю, что все потенциально лишние копирования компилятор соптимизирует.
Нет, конструктору велено заполнить нулями - он и заполнит
Код
C++ (Qt)
std::vector <QVector3D> vec(n);
<QVector3D> temp[32];  // или так
<QVector3D> v0, v1, v2;  // или даже так
 
Все это будет притормаживать за счет дефаултного конструктора. И на хорошей кратности это быстро становится ощутимым. Тут не UI где на такие вещи можно спокойно забивать. Поэтому грамотная реализация - пустой конструктор по умолчанию.

Ну это еще цветочки. Что еще плохо в QVector3D ?





Название: Re: reinterpret_cast класса в массив
Отправлено: Old от Январь 21, 2016, 13:25
Не люблю неожиданностей и неопределенности.
Вы переживаете, что изменится внутреннее устройство?

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

Не обижайтесь, но пока это похоже на капризы, основанные на страхе. ;)
Ну и да, я не призываю использовать QVector3D. :)


Название: Re: reinterpret_cast класса в массив
Отправлено: Racheengel от Январь 21, 2016, 13:33
"Сам по себе" QVector3D не является чем-то ужасно плохим :)
Просто, скажем так, все зависит от области применения.
Если надо посчитать несколько десятков 3д-векторов в пространстве - то не проблема. Сами юзаем иногда.
Но когда речь о высокопроизводительном приложении с большими объемами данных - то для подобных целей считаю данный класс неуместным.
Раньше приложение у нас использовало массивы из QVector3D. Когда перешли на plain float array - производительность выросла, а расход памяти уменьшился. Плюс стало возможно "напрямую" юзать всякие там sse-интринзики :)


Название: Re: reinterpret_cast класса в массив
Отправлено: Igors от Январь 21, 2016, 13:37
"Сам по себе" QVector3D не является чем-то ужасно плохим :)
Является

производительность выросла, а расход памяти уменьшился. Плюс стало возможно "напрямую" юзать всякие там sse-интринзики :)
производительность - верю. Но расход памяти и, особенно, "sse-интринзики" - ой заливаете  :)


Название: Re: reinterpret_cast класса в массив
Отправлено: Racheengel от Январь 21, 2016, 13:44
Является
Почему? :)

производительность - верю. Но расход памяти и, особенно, "sse-интринзики" - ой заливаете  :)

"вай, мамай клянус!"
А как прикажете применить интринзики к QVector3D  ???


Название: Re: reinterpret_cast класса в массив
Отправлено: __Heaven__ от Январь 21, 2016, 13:50
Racheengel,
Код
C++ (Qt)
#include <QVector>
#include <QtDebug>
 
struct A{
   A(){
       qDebug() << "Constructed";
       a_ = 0;
   }
   A(const A &other) {
       qDebug() << "Copied";
       a_ = other.a_;
   }
   float a_;
};
 
 
int main()
{
   QVector<A> vec;
   vec.reserve(3);
   vec.append(A());
 
   qDebug() << sizeof(A);
   return 0;
}
 

Цитировать
Constructed
Copied
4

Вроде, ничего лишнего


Название: Re: reinterpret_cast класса в массив
Отправлено: Old от Январь 21, 2016, 13:52
А как прикажете применить интринзики к QVector3D  ???
В памяти вектор QVector3D это последовательность float: x, y, z, x, y, z, ...
С помощью плохих кастов можно получить float*


Название: Re: reinterpret_cast класса в массив
Отправлено: Old от Январь 21, 2016, 13:55
Вроде, ничего лишнего
Имеется ввиду:
Код
C++ (Qt)
QVector3D vec[ 100 ];
 
// или
 
QVector<QVector3D> vec( 100 );
 

В этих случаях 100 раз будет вызываться дефолтный конструктор QVector3D.



Название: Re: reinterpret_cast класса в массив
Отправлено: Racheengel от Январь 21, 2016, 13:59
В памяти вектор QVector3D это последовательность float: x, y, z, x, y, z, ...
С помощью плохих кастов можно получить float*

Или x,y,z, [gap], x,y,z, [gap]

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


Название: Re: reinterpret_cast класса в массив
Отправлено: __Heaven__ от Январь 21, 2016, 14:01
Код
C++ (Qt)
#include <QVector>
#include <QVector3D>
#include <QtDebug>
#include <QTime>
 
 
int main()
{
   QTime time;
   time.start();
   QVector<QVector3D> vec(9999999);
   qDebug() << time.elapsed();
   return 0;
}

Цитировать
30

Насчёт гапов тоже интересуюсь. Только не нашёл ссылок на стандарт. В c99 они возможны. А что насчёт c++11?


Название: Re: reinterpret_cast класса в массив
Отправлено: Old от Январь 21, 2016, 14:12
Кто гарантирует, что на разных платформах не появится никакого чудо-выравнивания, например?
Стандарт. Структуры в памяти располагаются строго друг за другом.
Вот в самой структуре поля могут выравниваться, но они будут одинаково выравнены, что для:
Код
C++ (Qt)
struct vec
{
   float x;
   float y;
   float z;
};
 
vec v;
 

что для:
Код
C++ (Qt)
float v[ 3 ];
 


Название: Re: reinterpret_cast класса в массив
Отправлено: Igors от Январь 21, 2016, 15:04
Здесь заклинивает Qt "идеология", которая на первое место ставит "удобство использования". Это конечно хорошо, но в случае QVector3D это просто идет в ущерб качеству кода. Плохо просто все, начиная с имени класса. QVecоr3D - оказывается "3D" надо понимать как  "трехмерный", а не "3 double ". Напр QVec3f было бы уместнее. Имена методов не лучше, напр

QVector3D::dotProduct
QVector3D::crossProduct

Эти операции настолько ходовые что часто их делают даже операторами. А тут длинные static портянки которые задолбают через неделю работы.

Ладно, не будем придираться к именам, содержательная часть
Код:
QVector3D QVector3D::normalized() const
{
    // Need some extra precision if the length is very small.
    double len = double(xp) * double(xp) +
                 double(yp) * double(yp) +
                 double(zp) * double(zp);
    if (qFuzzyIsNull(len - 1.0f)) {
        return *this;
    } else if (!qFuzzyIsNull(len)) {
        double sqrtLen = sqrt(len);
        return QVector3D(float(double(xp) / sqrtLen),
                         float(double(yp) / sqrtLen),
                         float(double(zp) / sqrtLen));
    } else {
        return QVector3D();
    }
}
Отак от! Оказывается выбор между normalize() и normalize_safe() (нормализация с проверкой) Qt пользователю не нужен. Будем просто сами всегда проверять (удобство использования), а не удается нормировать - вернем нулевой, пусть будет Nan на следующей операции. Перлы с fuzzy  из той же оперы. Как можно было накосячить в том что прекрасно известно десятки лет - хз.

Идиотское setX(Y, Z) - нет слов

distanceToXXX - на мой взгляд, просто оскорбление. Типа "ты ничего в геометрии не рубишь, записать 1 строку тебе тяжело. Не беда - мы вот сделали такой чудесный класс что и без всяких познаний (чисто на букваре) все сделаешь! Кормись, лошок, кормись!". Зачем же так плохо думать о пользователе?  :'( :'(

SSE нет и намека, хотя люди его уже лет 10 имеют. По нынешним временам это просто неприлично. Кстати, именно поэтому не спешите городить свой класс.

Зато да, для человека с этим никогда не работавшего - все прекрасно, просто отлично. Но ведь это надувательство.  Ладно, будем надеяться что таких классов в Qt немного, и на солнце есть пятна


Название: Re: reinterpret_cast класса в массив
Отправлено: __Heaven__ от Январь 21, 2016, 15:21
У вас очень тонкая грань между "удобство использования" и "Кормись, лошок"


Название: Re: reinterpret_cast класса в массив
Отправлено: Racheengel от Январь 21, 2016, 15:29
Насчет "одинакового выравнивания везде и всегда", увы, не соглашусь.

http://stackoverflow.com/questions/5397447/struct-padding-in-c

Да и практика иное показывает.


Название: Re: reinterpret_cast класса в массив
Отправлено: __Heaven__ от Январь 21, 2016, 15:44
А для float, float, float такие примеры есть?


Название: Re: reinterpret_cast класса в массив
Отправлено: Old от Январь 21, 2016, 15:58
Насчет "одинакового выравнивания везде и всегда", увы, не соглашусь.

http://stackoverflow.com/questions/5397447/struct-padding-in-c

Да и практика иное показывает.
Еще раз.
Структуры в памяти всегда располагаются без паддинга, одна к одной.
Паддинг внутри структуры может отличаться в зависимости от архитектуры, но ровно такой-же паддинг будет и при упаковки массива таких типов. Проще говоря структура из 3 float всегда будет выравниваться также как и массив из 3 float.


Название: Re: reinterpret_cast класса в массив
Отправлено: gil9red от Январь 21, 2016, 16:16
/offtop

"reinterpret_cast класса в массив"
Из-за подобных хитростей программист с++ и может цитирую
Цитировать
...стрелять себе в ногу, стрелять себе в ногу с выдумкой, в другую ногу, одной ногой стрелять в другую ногу...

Продолжайте срач :)


Название: Re: reinterpret_cast класса в массив
Отправлено: Igors от Январь 21, 2016, 16:18
У вас очень тонкая грань между "удобство использования" и "Кормись, лошок"
А почему Вы думаете она вообще существует?
Цитировать
Отважный разведчик - подлый шпион
Опытный профессионал - продажная сука
и.т.п.
Очень разная эмоциональная окраска - но смысл-то одинаков :)

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


Название: Re: reinterpret_cast класса в массив
Отправлено: Old от Январь 21, 2016, 16:24
/offtop

"reinterpret_cast класса в массив"
Из-за подобных хитростей программист с++ и может цитирую
Цитировать
...стрелять себе в ногу, стрелять себе в ногу с выдумкой, в другую ногу, одной ногой стрелять в другую ногу...

Продолжайте срач :)
Ну на самом деле это действительно очень гадкий хак и я бы им не пользовался. :)


Название: Re: reinterpret_cast класса в массив
Отправлено: __Heaven__ от Январь 21, 2016, 16:26
Old, а что бы вы сделали?


Название: Re: reinterpret_cast класса в массив
Отправлено: Racheengel от Январь 21, 2016, 16:44
Ах как мы блещем знанием хабровских статеек! Пусть вопрос не стоит выеденного яйца (всегда можно использовать stride) - но не напрасно же читал!  :)

Вот есть у нас 3 флота. На 3д вектор это 12 байт, верно? Допустим, у нас 10 миллионов векторов. Это 120 мегабайт (примерно). Каждый страйд плюс 1 байт означает, что как минимум 10 мб будут уходить в нирвану. Много это или мало? Вроде бы мало, да. Но тут 10, там 10, и кто знает, как оно через пару месяцев будет, с какими данными (какого объема) придет зоказчег...

Ну то есть, мелочь, конечно же... А потом народ удивляется - купили комп с 4 гигами памяти, только винда голая стоит, а 3 гига уже заняты с прыжка :)


Название: Re: reinterpret_cast класса в массив
Отправлено: Igors от Январь 21, 2016, 16:48
Каждый страйд плюс 1 байт означает, что как минимум 10 мб будут уходить в нирвану.
Не один а 4 (40 Mb). Но все SSE (AVX и.т.п) требует чтобы адрес был выровнен на 16, иначе exception. Поэтому на эти расходы идут, скорость важнее


Название: Re: reinterpret_cast класса в массив
Отправлено: Old от Январь 21, 2016, 16:49
Old, а что бы вы сделали?
Ниже мысли без долгих обдумываний, так сказать, что сразу в голову пришло. :)

Была бы коллекция точек, на нижнем уровне они хранились бы в обычном векторе float, что позволяло бы легко кастить ее в float*.
И был бы класс СсылкаНаТочку, объект которого мог-бы работать с данными из вышеуказанной коллекции.

Набросок:
Код
C++ (Qt)
class VecRef
{
public:
   VecRef( float *v ) : m_data( v ) {}
 
   float    &x() const { return m_data[ 0 ]; }
   float    &y() const { return m_data[ 1 ]; }
   float    &z() const { return m_data[ 2 ]; }
 
private:
   float    *m_data;
};
 

Т.е. сама ссылка данные о точке не хранит, а имеет указатель на первый float точки из коллекции.


Название: Re: reinterpret_cast класса в массив
Отправлено: Racheengel от Январь 21, 2016, 17:19
Каждый страйд плюс 1 байт означает, что как минимум 10 мб будут уходить в нирвану.
Не один а 4 (40 Mb). Но все SSE (AVX и.т.п) требует чтобы адрес был выровнен на 16, иначе exception. Поэтому на эти расходы идут, скорость важнее

Очень спорно. Прирост скорости будет от нулевого до ничтожного, если использовать команды "с выравниванием" вместо команд "без выравнивания". Кэш все-таки свою работу делает)


Название: Re: reinterpret_cast класса в массив
Отправлено: Igors от Январь 21, 2016, 17:36
Очень спорно.
Спорно или нет - но так делают.

Прирост скорости будет от нулевого до ничтожного,
Прирост может быть очень приличным (конечно зависит от задачи)