Russian Qt Forum
Ноябрь 24, 2024, 00:28 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 2 [3]   Вниз
  Печать  
Автор Тема: тип size_t и сравнение указателей  (Прочитано 17394 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #30 : Март 20, 2011, 19:45 »

Код
C++ (Qt)
void test_virtual_fun(ListBase &l) {
   Iter it = l.begin();
   for(; it != l.end(); ++it) {
       if ((*it)->type() == TYPE_F) { // Один вызов виртуального метода
           DerivedF *obj = static_cast<DerivedF*>(*it); // Пренебрегаем издержками на вызов static_cast
           obj->myFunc(); // один вызов не виртуального метода!
       }
   }
}
 
На момент вызова myFunc() obj имеет тип DerivedF - значит никто не мешает компилятору вызвать DerivedF::myFunc() явно, минуя виртуальщину. Просто вызывайте (*it)->myFunc, пусть разбирается

А вообще все эти расходы пренебрежимо малы даже когда надо затачивать скорость. Др. дело типичная ошибка "лучших собаководов STL" - просмотр new/malloc в цикле вычислений (часто грубый, на push_back, insert)
« Последнее редактирование: Март 20, 2011, 19:46 от Igors » Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #31 : Март 20, 2011, 20:42 »

Код
C++ (Qt)
void test_virtual_fun(ListBase &l) {
   Iter it = l.begin();
   for(; it != l.end(); ++it) {
       if ((*it)->type() == TYPE_F) { // Один вызов виртуального метода
           DerivedF *obj = static_cast<DerivedF*>(*it); // Пренебрегаем издержками на вызов static_cast
           obj->myFunc(); // один вызов не виртуального метода!
       }
   }
}
 
На момент вызова myFunc() obj имеет тип DerivedF - значит никто не мешает компилятору вызвать DerivedF::myFunc() явно, минуя виртуальщину. Просто вызывайте (*it)->myFunc, пусть разбирается
Да нет же, myFunc() - не виртуальная функция) Я бы мог её и не писать вообще, результат бы не изменился. И кстати, я не могу вызвать (*it)->muFunc() поскольку класс Base не имеет этого метода.
Идея теста была сравнить что быстрей: dynamic_cast или вызов виртуальной функции.
Conclusions:
Хотя в данном случае dynamic_cast оказался быстрее, забивать голову и тратить время на подобные оптимизации - бредовая идея)
Лучше потратить его (время) на обдумывание архитектуры и рассмотрения возможных вариантов её расширения в будущем)

Код:
4) Спорно/проблематично кто там больше "затуманивает". Часто текст превращается в демонстрацию знания C++ и автор озабочен не содержательной частью а лишь той самой "формальной грамотностью". Продравшись через все навороты обнаруживается, что дела-то "пшик", ф-циональность слаба. А бывает и наоборот
Формальная грамотность, формальная грамотность.. Грамотность при написании кода - это элементарное уважение к читающему код и залог безопасности (не маловажный факт). Понты, по мне - это когда в коде встречаются сплошь и рядом сомнительные хаки и прочие грабли, на которые разве что не наступит (и то сомнительно) сам автор, в то время, как истинная причина всего этого безобразия - плохая архитектура и чрезмерный С-консерватизм.  Улыбающийся     
 
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Akon
Гость
« Ответ #32 : Март 20, 2011, 21:25 »

2 m_ax: попробуй увеличить иерархию наследования и рассматривать динамик-каст самого последнего потомка к самой первой базе.
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #33 : Март 20, 2011, 21:33 »

2 m_ax: попробуй увеличить иерархию наследования и рассматривать динамик-каст самого последнего потомка к самой первой базе.
Не спорю, вполне вероятно, что при этом ситуация может изменится, но мне как то фиолетово)
У меня реально используется обин базовый класс и два наследуемые от него. Раньше я считал, что в моём случае (пример приведённый выше лишь отражает общую картину) dynamic_cast - очень медленная штука по сравнению с вызовом виртуальной функции, однако, как оказалось для беспокойства нет причин и я оставил вариант с dynamic_cast.   
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #34 : Март 20, 2011, 22:23 »

Формальная грамотность, формальная грамотность.. Грамотность при написании кода - это элементарное уважение к читающему код и залог безопасности (не маловажный факт). Понты, по мне - это когда в коде встречаются сплошь и рядом сомнительные хаки и прочие грабли, на которые разве что не наступит (и то сомнительно) сам автор, в то время, как истинная причина всего этого безобразия - плохая архитектура и чрезмерный С-консерватизм.  Улыбающийся     
Ну давайте немного поспорим. Можно покритиковать Ваш кусочек кода?  Улыбающийся Вот прямо "близлежащий"
Код
C++ (Qt)
void test_virtual_fun(ListBase &l) {
   Iter it = l.begin();
   for(; it != l.end(); ++it) {
       if ((*it)->type() == TYPE_F) { // Один вызов виртуального метода
           DerivedF *obj = static_cast<DerivedF*>(*it); // Пренебрегаем издержками на вызов static_cast
           obj->myFunc(); // один вызов не виртуального метода!
       }
   }
}
 
Сынтаксыс
- имя "l" просто безобразно, я уже подслеповат и вижу "1"
- TYPE_F выглядит "ну очень в стиле С", (как впрочем и test_virtual_fun). Лучше смотрится Base::TYPE_F
- "type" весьма сомнительное название для метода, а myFunc лишено всякой определенности
- чего это подается ListBase & (а не const ListBase &) если содержимое контейнера указателей остается неизменным? Где же залог безопасности?  Улыбающийся

Архитектура:
Мне кажется я понял Вашу идею, но тест виртуалов выглядит странно. Зачем грузить тест static_cast и вызовом невиртуала myFunc? Что меряем если львиная доля посвящена другому? Логичнее так (в теле цикла)

Код
C++ (Qt)
Base * base = *it;
int sum = 0;
for (size_t i = 0; i < NUM_CALL; ++i)
sum += base->typeVirtual();
 
А для второго теста так
Код
C++ (Qt)
Base * base = *it;
int sum = 0;
for (size_t i = 0; i < NUM_CALL; ++i) {
DerivedF * derF = dynamic_cast <DerivedF *> (base);
if (derF)
 sum += derF->typeNonVirtial();
else {
 DerivedInt * derInt = dynamic_cast <DerivedInt *> (base);
 if (derInt)
  sum += derInt->typeNonVirtial();
}
}
 
Так что негусто ни с "уважением к читающему" ни с "архитектурой". Однако вынужден признать: с демагогией - все отлично  Улыбающийся
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #35 : Март 20, 2011, 22:41 »

Цитировать
Ну давайте немного поспорим. Можно покритиковать Ваш кусочек кода?  Улыбающийся Вот прямо "близлежащий"
Ну я исчо учусь)
Потом этот тест я написал на коленках за минут 10 чисто для себя, чтоб потом его нафиг удалить))
У меня ещё техника не доведена до такого автоматизма, чтоб так прям сразу, даже простой тест, писать идеологически правильно))
Логичнее критиковать то, откуда непосредственно ноги растут: libssc)

Цитировать
Так что негусто ни с "уважением к читающему" ни с "архитектурой". Однако вынужден признать: с демагогией - все отлично  Улыбающийся
Я исправлюсь  Улыбающийся  
« Последнее редактирование: Март 20, 2011, 23:19 от m_ax » Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #36 : Март 21, 2011, 11:39 »

А задумка у Вас была хорошая: проверить на деле вместо того чтобы гадать (или утверждать с чужих слов) . Мой тест (исходник приаттачен) печатает такое

Цитировать
TestVirtual time(sec):  0.28 sum = 157286400
TestNonVirtual time(sec):  3.192 sum = 157286400
Примечание "sum" нужна чтобы компилятор не схлопнул код в release

dynamic_cast вызывается дважды, поэтому второе время нужно поделить на 2. Но все равно, dynamic_cast ощутимо (в неск раз) медленнее вызова virtual. Согласен с Akon, что dynamic_cast зависит от сложности построения базового класса.

Немного глянув в отладчике: вроде для "не POD" типа static_cast поет примерно ту же песню что и dynamic_cast (это просто впечатление)

При всем этом: dynamic_cast "достаточно быстр" и экономить на нем неразумно (3 секунды на 200 миллионах вызовов)
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #37 : Март 21, 2011, 12:52 »

И всё же я считаю, что Ваш тест не совсем чист:
Во-первых, как Вы уже заметили, dynamic_cast в функции TestNonVirtual вызывается дважды.
Во-вторых,
Код
C++ (Qt)
void TestVirtual( const QVector <CBase *> & vec )
{
   QTime begT = QTime::currentTime();
 
       int sum = 0;
       for (int i = 0; i < vec.size(); ++i) {
               CBase * base = vec[i];
               for (size_t j = 0; j < NUM_CALL; ++j)
                       sum += base->typeVirtual(); // Один вызов виртуальной функции
       }
 
       int msecs = begT.msecsTo(QTime::currentTime());
   qDebug() << "TestVirtual time(sec): " << (msecs / 1000.0f) << "sum =" << sum;
}
 
void TestNonVirtual( const QVector <CBase *> & vec )
{
   QTime begT = QTime::currentTime();
 
       int sum = 0;
       for (int i = 0; i < vec.size(); ++i) {
               CBase * base = vec[i];
               for (size_t j = 0; j < NUM_CALL; ++j) {
                       CBaseA * a = dynamic_cast<CBaseA *> (base); // Раз: вызов dynamic_cast
                       if (a)
                               sum += a->typeNonVirtual(); // + Нужно учитывать дополнительное время от вызова обычной функции
                       else {
                               CBaseB * b = dynamic_cast<CBaseB *> (base); // Два: Вызов dynamic_cast
                               if (b)
                                       sum += b->typeNonVirtual(); // Не учитываем добавочное время от этой операции?
                       }
 
               }
       }
 
       int msecs = begT.msecsTo(QTime::currentTime());
   qDebug() << "TestNonVirtual time(sec): " << (msecs / 1000.0f) << "sum =" << sum;
}
 
В функции TestVirtual Виртуальная функция вызывается один (T_v - время вызова виртуальной функции) раз + время на суммирование (T_sum). Всё хорошо.
В функции TestNonVirtual Дважды вызывается dynamic_cast (T_dyn - время вызова) + время вызова не виртуальной функции (T_nv) и + операция суммирования (T_sum).

Я хочу сказать, что нужно сравнивать чистый вызов dynamic_cast и чистый вызов виртуальной функции. Зачем вообще все эти операции суммирования? Оптимизатор можно просто отключить) Например в debug версии.
Кстати, в debug версии мой тест показывает следующие результаты (при трёх запусках):
Код
C++ (Qt)
dynamic_cast:    (1.56, 1.54, 1.54) sec;  <t> = 1.547 sec
virtual_fun:          (1.49, 1.49, 1.48) sec;  <t> = 1.487 sec
 
И получается, что виртуальная функция быстрее dynamic_cast (в данном конкретном случае) в 1.04 раз.
« Последнее редактирование: Март 21, 2011, 14:28 от m_ax » Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #38 : Март 21, 2011, 13:56 »

Да, хочу добавить вот что:
Запустил Ваш тест в release и в debug версии со следующим изменением кода:
Код
C++ (Qt)
void TestVirtual( const QVector <CBase *> & vec )
{
   QTime begT = QTime::currentTime();
 
       int sum = 0;
       for (int i = 0; i < vec.size(); ++i) {
               CBase * base = vec[i];
               for (size_t j = 0; j < NUM_CALL; ++j)
                       sum += base->typeVirtual(); // Без изменений
       }
 
       int msecs = begT.msecsTo(QTime::currentTime());
   qDebug() << "TestVirtual time(sec): " << (msecs / 1000.0f) << "sum =" << sum;
}
 
void TestNonVirtual( const QVector <CBase *> & vec )
{
   QTime begT = QTime::currentTime();
 
       int sum = 0;
       for (int i = 0; i < vec.size(); ++i) {
               CBase * base = vec[i];
               for (size_t j = 0; j < NUM_CALL; ++j) {
                        sum += base->typeNonVirtual(); // Для сравнения: оставим только вызов не виртуальной функции
               }
       }
 
       int msecs = begT.msecsTo(QTime::currentTime());
   qDebug() << "TestNonVirtual time(sec): " << (msecs / 1000.0f) << "sum =" << sum;
}
 
Посмотрим, для начала, какую ошибку мы допускаем пренебрегая временем вызова не виртуальной функции. Т.е. сейчас мы сравниваем вирт. и не вирт. функции при прочих равных условиях.
Результат (release):
Код
C++ (Qt)
TestVirtual time(sec):  0.673 sum = 157286400
TestNonVirtual time(sec):  0 sum = 157286400
 
А в debug:
Код
C++ (Qt)
TestVirtual time(sec):  1.529 sum = 157286400
TestNonVirtual time(sec):  0.995 sum = 157286400
 
Сравнивая, можно сделать вывод, что оптимизатор не так просто обмануть, во всяком случае на трюк с суммированием он не ведётся))

Теперь запомним это отношение: время вирт.ф / время не вирт. ф. = 1.529/0.995 = 1.54

И запустим в debug Ваш изначальный вариант:
Код
C++ (Qt)
TestVirtual time(sec):  1.696 sum = 157286400
TestNonVirtual time(sec):  5.348 sum = 157286400
 
Теперь, вспоминая, что время TestNonVirtual нужно поделить на два:
t = 5.348/2 = 2.674
и далее его ещё нужно поделить на отношение, которое было найдено выше, т.е. на 1.54:
В результате имеем:
Код
C++ (Qt)
TestVirtual time(sec):  1.696
TestNonVirtual time(sec):  1.736
 
Отношение этих значений есть: 1.736/1.696 =1.02
Что находится в хорошем согласии с моими результатами в debug версии. 
« Последнее редактирование: Март 21, 2011, 14:24 от m_ax » Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Страниц: 1 2 [3]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.513 секунд. Запросов: 23.