Почему мое приложение C++ быстрее, чем мое приложение C (пользующийся той же библиотекой) на Core i7

Мне записали библиотеку в C, и у меня есть 2 приложения, записанные в C++ и C. Эта библиотека является коммуникационной библиотекой, таким образом, один из вызовов API похож на это:

int source_send( source_t* source, const char* data );

В приложении C код делает что-то вроде этого:

source_t* source = source_create();
for( int i = 0; i < count; ++i )
    source_send( source, "test" );

Где, поскольку приложение C++ делает это:

struct Source
{
    Source()
    {
        _source = source_create();
    }

    bool send( const std::string& data )
    {
        source_send( _source, data.c_str() );
    }

    source_t* _source;
};

int main()
{
    Source* source = new Source();
    for( int i = 0; i < count; ++i )
        source->send( "test" );
}

На Intel Core i7 код C++ создает почти точно на 50% больше сообщений в секунду.. Принимая во внимание, что на Intel Core 2 Duo это производит почти точно ту же сумму сообщений в секунду. (Core i7 имеет 4 ядра с 2 потоками обработки каждый),

Мне любопытно, какое волшебство аппаратные средства выполняют для осуществления этого. У меня есть некоторые теории, но я думал, что получу реальный ответ :)

Править: Дополнительная информация из комментариев

Компилятор является Visual C++, таким образом, это, окна упаковывают (они оба)

Реализация коммуникационной библиотеки создает новый поток для пересылки сообщений. source_create - то, что создает этот поток.

6
задан Charles 13 January 2010 в 05:10
поделиться

5 ответов

От изучения вашего исходного кода, я не вижу причину, почему код C ++ должен быть быстрее.

Следующее, что я бы сделал, это проверить код монтажа, который генерируется. Если вы используете инструментарий GNU, у вас есть пара способов сделать это.

Вы можете задать GCC и G ++ для вывода кода сборки через аргумент командной строки . Убедитесь, что другие затем добавляют этот аргумент, вы используете то же самое общие аргументы командной строки, которые вы делаете для регулярного компиляции.

Второй вариант - загрузить программу с GDB и использовать команду .

Удачи.

Обновление

Вы можете сделать то же самое с Microsoft Toolchain.

Чтобы получить компилятор в узел вывода, вы можете использовать либо / FA или / FAS . Первый должен выводить сборку только в то время как второй будет смешивать сборку и источник (что должно облегчить следовать).

Что касается использования отладчика, после того, как вы получите отладчик, запущенный в Visual Studio, перейдите к «Debug | Windows | разборка» (проверено на Visual Studio 2005, другие версии могут отличаться).

7
ответ дан 10 December 2019 в 00:38
поделиться

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

Почему вы думаете, что вам нужно сделать это?

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


Обновление:

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

Однако я не уверен, что это простой вопрос перехвата нового/удаления для класса, так как могут возникнуть ситуации, когда delete не вызывается и, поскольку mark/sweep зависит от количества ссылок, необходимо иметь возможность перехватывать назначения указателей, чтобы он работал правильно.

Думали ли вы о том, как с этим справиться?

Классический пример:

myobject *x = new xclass();
x = 0;

не приведет к вызову удаления.

Как вы также обнаружите, что указатель на один из экземпляров находится в стеке? Перехват новых и удаление может позволить вам сохранить, является ли сам объект стеком или на основе кучи, но я в проигрыше относительно того, как вы скажете, где будет назначен указатель, особенно с кодом типа:

myobject *x1 = new xclass();  // yes, calls new.
myobject *x2 = x;             // no, it doesn't.
-121--1282603-

Хакерский способ:

struct Detect {
   Detect() {
      int i;
      check(&i);
   }

private:
   void check(int *i) {
      int j;
      if ((i < &j) == ((void*)this < (void*)&j))
         std::cout << "Stack" << std::endl;
      else
         std::cout << "Heap" << std::endl;
   }
};

Если объект был создан в стеке, он должен жить где-то в направлении переменных стека внешних функций. Куча обычно растет с другой стороны, так что стопка и куча встречались бы где-то посередине.

(Существуют системы, в которых это не работает)

-121--1282616-

Не видя полный код или сборку, я думаю, что компилятор c++ встроен для вас. Одной из красот компиляторов c++ является возможность встроить почти что угодно для скорости, а компиляторы Microsoft хорошо известны, чтобы безвозмездно встроить почти до точки необоснованного вздутия конечных исполняемых файлов.

2
ответ дан 10 December 2019 в 00:38
поделиться

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

Является ли версия на C копированием чего-то ненужного (это может быть тонкая или не очень тонкая оптимизация, например, оптимизация возвращаемого значения).

Это должно проявиться в хорошем профайлере, если у вас есть VS SKU более высокого уровня, профилировщик на основе сэмплов - это хорошо, если вы ищете хороший бесплатный профайлер, то Windows Performance Analyzer невероятно мощный для Vista, и вот - вот обзор использования опции стековой ходьбы

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

Я бы проследил за этим с профилем, если бы не было чего-то явно очевидного (например, дополнительной копии).

Еще одна вещь, если вы беспокоитесь о том, что гиперпотоки будут мешать, сродни этому процессу с ядром, отличным от HT. Вы можете сделать это либо через менеджер задач в GUI, либо через SetThreadAffinityMask.

-Rick

.
1
ответ дан 10 December 2019 в 00:38
поделиться

CORE I7 - это гиперпоток - у вас есть HT?

Может быть, код C ++ как-то скомпилирован для воспользовавшись преимуществами HT, тогда как код C не делает. Как выглядит диспетчер задач, когда вы запускаете свой код? Равномерно распределить нагрузку по количеству ядер, или несколько ядер максимально выделены?

0
ответ дан 10 December 2019 в 00:38
поделиться

Просто дикое предположение: если вы собираете источник библиотеки вместе с вашим приложением, а функции API C API не объявлены Extern «C», то, возможно, версия C ++ использует версию C ++ Разные и как-то быстрее призвать конвенцию ??

Кроме того, если вы собираете источник библиотеки вместе с вашим приложением, то, возможно, компилятор C ++ составляют свой источник библиотеки в виде C ++ и как-то лучше на оптимизации вашего компилятора?

0
ответ дан 10 December 2019 в 00:38
поделиться