powered by simpleCommunicator - 2.0.59     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / C++ [игнор отключен] [закрыт для гостей] / inline extern из библиотеки, как правильно декларировать-имплементировать?
1 сообщений из 276, страница 12 из 12
inline extern из библиотеки, как правильно декларировать-имплементировать?
    #39431750
Пётр Седов
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
nojavaпри этом даже гуру C++ прямо говорят - идея, что давайте все раскопипастим, а компилятор все это чудесно заоптимизирвет - оказалась пшиком, никакого прироста производительности никто так и не смог достичь, зато исполнимый код бухнет, вымывая L2/L3 кеши просто на ура.Не пшик, реально есть такая оптимизация. В Visual C++ она называется «identical comdat folding» (ICF) и реализована уже давно, в Visual C++ 6 точно есть. Например, есть программа:
test_icf.cpp
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
extern "C" void test_parts();
extern "C" void test_roots();

int main() {
  test_parts();
  test_roots();
  return 0;
}

parts.cpp
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
#include <assert.h>
#include <stdio.h>

struct part_t;

// список частей
struct parts_t {
  part_t* first;
  part_t* last;
};

struct part_t {
  part_t* prev;
  part_t* next;
  int id;
};

// добавляет часть в список
extern "C" void add_part(parts_t* parts, part_t* part) {
  part_t* last = parts->last;
  part->prev = last;
  part->next = NULL;
  if (last != NULL) {
    assert(last->next == NULL);
    last->next = part;
  } else {
    assert(parts->first == NULL);
    parts->first = part;
  }
  parts->last = part;
}

extern "C" void test_parts() {
  parts_t parts = {NULL, NULL};
  part_t p1; p1.id = 1; add_part(&parts, &p1);
  part_t p2; p2.id = 2; add_part(&parts, &p2);
  part_t p3; p3.id = 3; add_part(&parts, &p3);

  printf("parts:\n");
  for (part_t* p = parts.first; p != NULL; p = p->next) {
    printf("%i\n", p->id);
  }
}

roots.cpp
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
#include <assert.h>
#include <stdio.h>

struct root_t;

// список корней
struct roots_t {
  root_t* first;
  root_t* last;
};

struct root_t {
  root_t* prev;
  root_t* next;
  double value;
};

// добавляет корень в список
extern "C" void add_root(roots_t* roots, root_t* root) {
  root_t* last = roots->last;
  root->prev = last;
  root->next = NULL;
  if (last != NULL) {
    assert(last->next == NULL);
    last->next = root;
  } else {
    assert(roots->first == NULL);
    roots->first = root;
  }
  roots->last = root;
}

extern "C" void test_roots() {
  roots_t roots = {NULL, NULL};
  root_t r1; r1.value = 1; add_root(&roots, &r1);
  root_t r2; r2.value = 2; add_root(&roots, &r2);
  root_t r3; r3.value = 3; add_root(&roots, &r3);

  printf("roots:\n");
  for (root_t* r = roots.first; r != NULL; r = r->next) {
    printf("%f\n", r->value);
  }
}

«extern "C"» использовал, чтобы link-овочные имена были читабельные, без mangling-а. Смотрим сгенерированный map-файл (конфигурация Release):
test_icf.map
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
 ...
  Address         Publics by Value              Rva+Base     Lib:Object

 0001:00000000       _test_parts                00401000 f   parts.obj
 0001:00000090       _add_root                  00401090 f   roots.obj
 0001:00000090       _add_part                  00401090 f   roots.obj
 0001:000000c0       _test_roots                004010c0 f   roots.obj
 0001:00000170       _main                      00401170 f   test_icf.obj
 ...
 0001:00000305       _fast_error_exit           00401305 f   LIBC:crt0.obj

 48 bytes saved through ICF

Функции add_part и add_root компилируются в одинаковый машинный код, поэтому linker разместил их в одном месте. Тут ещё наверняка важно, что нигде в C++-коде не берётся адрес этих функций, а значит не требуется идентичность (уникальность адреса).

Скорее всего, эта оптимизация была сделана из-за шаблонов. Функции std::list<part_t*>::push_back и std::list<root_t*>::push_back компилируются в одинаковый машинный код, а их адрес обычно не берут.

Тут правда есть одна тонкость. Если операционная система пишет что-нибудь типа «случился crash по адресу 00401093», то вы не сможете узнать, в какой функции, в add_part или add_root. То же самое со stack trace, который строится по адресам функций.
...
Рейтинг: 0 / 0
1 сообщений из 276, страница 12 из 12
Форумы / C++ [игнор отключен] [закрыт для гостей] / inline extern из библиотеки, как правильно декларировать-имплементировать?
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]