LINUX.ORG.RU

Контейнеры в C


2

3

Будучи сиплюсплюсником, меня давно интересует как те же задачи выполняются в C. Знаю, на ЛОРе есть множество приверженцев C, надеюсь они ответят на пару простых вопросов.

Я являюсь активным сторонником идеи «Алгоритм должен работать настолько быстро, насколько позволяет железо». С этой точки зрения C++ даёт потрясающие возможности из-за своей системы кодогенерации. Да, я имею в виду шаблоны.

Не будем зарываться в дебри boost'а, возьмём простую задачу - контейнеры.

Для примера я взял односвязные списки в GTK GSList и Qt QList. QList позволяет хранить как простые, так и сложные типы с конструкторами, деструкторами, типы с общими данными. При этом накладных расходов на выделение в куче не происходит, а при реаллокации выбирается нужный алгоритм в зависимости от QTypeInfo<T>, к примеру для int будет вызван memcpy(), а для QString оператор копирования. Кроме того блок данных заранее резервируется и для сложных типов вызывается placement constructor. Для удаления данных по указателям используется алгоритм qDeleteAll(from,to), который сам дёрнет нужные конструкторы. Беглый просмотр GSList показал, что в нём можно хранить только указатели, со всеми вытекающими накладными расходами на аллокацию/уничтожение памяти, ручное кастирование, слежение за утечками, фрагментацию памяти.

Аналогичная ситуация со связными списками GList и QLinkedList.

Это даже не затрагивая вопрос о типизации, в C++ компилятор сразу даст по рукам при попытке записать в контейнер неверный тип или с помощью неявного преобразования с explicit-конструктором.

Далее, счётчики ссылок и деструкторы.

К примеру, в C++ список строк будет выглядеть как QList<QString>, при этом можно забыть про внутреннюю структуру строки - конструктор, деструктор и операторы копирования сами занимаются подсчётом ссылок, разделением и уничтожением данных. Вернуть строку из функции проще простого:

QString foo()
{
    return listOfStrings.at( 5 );
}

Этот код абсолютно безопасен и оптимален с точки зрения использования памяти и процессора, поскольку время тратится только на увеличение счётчика и копирование указателя.

В GTK я сходу нашёл GString:

struct GString 
{
  gchar *str;
  gint len;
};

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

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

Или может GTK неудачный пример, тогда дайте ссылку на правильную C-библиотеку с контейнерами.

★★★★★
Ответ на: комментарий от Love5an

> Я не понял, ты с чем-то не согласен из перечисленного выше?

я написал, что из С++ можно использовать библиотеки на С с помощью того самого #include - ничего больше, ты решил мне доказать, что это не так - но не привел ни одного примера несовместимой библиотеки( даже самой криворукой, которой никто не пользуется )

П.С. и да - я знаю, что С++ с С не 100% совместим

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

Держи, вот моя библиотека:

//libhelloworld.h
void hello_world(void);
//libhelloworld.c
#include <stdio.h>

void hello_world(void)
{
    puts("Hello, world!");
}
Компилировать так:
gcc -c libhelloworld.c
ar cr libhelloworld.a libhelloworld.o

Love5an
()
Ответ на: комментарий от Love5an
extern "C" {
#include "libhelloworld.h"
}

int main()
{
    hello_world();
}

$ g++ ./1.cpp ./libhelloworld.a

$ ./a.out

Hello, world!

а в чем проблема то? или ты буквоедством решил занятся?

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

extern «C» {

Это уже выкрутасы. ЧТД.

По желанию можешь добавить в заголовочный файл структуру:

struct template 
{
    int new;
    struct template* class;
};

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

> По желанию можешь добавить в заголовочный файл структуру:

еше раз, можешь сам погуглить такие ошибки - в библиотеках никто не использует эти ключевые слова в хедерах( что я и говорил ), а вот на что действительно можно напороться - это проблема несовместимых компиляторов С, в которых даже стандарт не реализован последний, а также есть куча расширений

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

> Это уже выкрутасы

это стандартная конструкция языка, а вот нестандартные прагмы, атрибуты и пр. в С ( и С++ ) - это уже действительно выкрутасы

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

> Это что, ты за всех разработчиков на Си говоришь?

это я озвучил текущее положение дел

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

а на мой комментарий зачем молчание? там человек даже свой велисопед вроде хотел писать.

все же уже сделано за нас.

вот пример из бустовской документации, заодно и на тему пула:

http://www.boost.org/doc/libs/1_44_0/libs/pool/doc/interfaces.html

...

pool_alloc

The pool_alloc interface is a Singleton Usage interface with Exceptions. It is built on the singleton_pool interface, and provides a Standard Allocator-compliant class (for use in containers, etc.).

Example:

void func()
{
  std::vector<int, boost::pool_allocator<int> > v;
  for (int i = 0; i < 10000; ++i)
    v.push_back(13);
} // Exiting the function does NOT free the system memory allocated by the pool allocator
  // You must call
  //  boost::singleton_pool<boost::pool_allocator_tag, sizeof(int)>::release_memory()
  // in order to force that
anonymous
()

Что-то у вас тут притихло ;) Подбросим дровишек.

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include <sys/queue.h>

struct person {
    char *nick;
    int id;

    SLIST_ENTRY(person) next;
};

static SLIST_HEAD(, person) persons;

int
main (int argc, char **argv)
{
  long count = atol (argv[2]), i;
  struct person *person;

  SLIST_INIT(&persons);

  for (i = 0; i < count; ++i)
    {
      person = malloc (sizeof (struct person));
      person->id = i;
      person->nick = strdup (argv[1]);

      SLIST_INSERT_HEAD(&persons, person, next);
    }

  while (!SLIST_EMPTY(&persons))
    {
      person = SLIST_FIRST(&persons);
      SLIST_REMOVE_HEAD(&persons, next);

      free (person->nick);
      free (person);
    }

  return 0;
}
$ gcc -o c_list main.c -Os -Wall
$ time ./c_list dendy 10000000

real    0m1.936s
user    0m1.728s
sys     0m0.208s
$ time ./c_list dendy 10000000

real    0m1.920s
user    0m1.717s
sys     0m0.199s
$ time ./c_list dendy 10000000

real    0m1.934s
user    0m1.701s
sys     0m0.228s

#include <string>
#include <list>
#include <stdlib.h>

struct person                                                                                           
{
  int id;
  std::string nick;

  person (int id_, std::string nick_)
    :id(id_), nick(nick_)
  {}
};

int main(int argc, char *argv[])
{
  std::list<person> list;

  int count = atoi(argv[2]);

  for(int i = 0 ; i < count; ++i)
  {
    list.push_back (person (i, argv[1]));
  }

  return 0;
} 
$ g++ -o cpp_list main.cc -Os -Wall
$ time ./cpp_list dendy 10000000

real    0m3.475s
user    0m3.149s
sys     0m0.320s
$ time ./cpp_list dendy 10000000

real    0m3.013s
user    0m2.677s
sys     0m0.333s
$ time ./cpp_list dendy 10000000

real    0m3.281s
user    0m2.970s
sys     0m0.306s

C++ слил, как и ожидалось.

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

> C++ слил, как и ожидалось.

ты толстый-толстый тролль :) или просто не умеешь писать на С++, т.к. очевидно, что у тебя на каждой итерации выполняется лишнее действие

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

Пардон ;) Забыл const std::string & nick_ в конструкторе. Слава богу это никак не повлияло на тормоза ;)

sceptik
()

Тут ещё не давали ссылку на ooc.pdf?

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

> за исключением преобразования сишной строки в приплюснутую

это обычное явление же? Не везде же ваши «приплюснутые» строки ;).

+ Таким макаром мы исключаем «читерство» с использованием строк с подсчётом ссылок. К тому же ничего страшного не должно быть в вашем «божетсвенном С++» ;) так как по идее std::string имеет прямой конструктор из const char * (т.е преобразование до смешного простое и «дешёвое»). Так что хватит ныть и признавайте свой слив, товарищ ;)

sceptik
()
Ответ на: комментарий от sceptik
#include <string> 
#include <list> 
#include <stdlib.h> 
 
struct person                                                                                            
{ 
  int id; 
  std::string nick; 
 
  person (int id_, char* nick_) 
    :id(id_), nick(nick_) 
  {} 
}; 
 
int main(int argc, char *argv[]) 
{ 
  std::list<person> list; 
 
  int count = atoi(argv[2]); 
 
  for(int i = 0 ; i < count; ++i) 
  { 
    list.push_back (person (i, argv[1])); 
  } 
 
  return 0; 
}
Dudraug ★★★★★
()
Ответ на: комментарий от Dudraug

И? Это ничего не меняет. Твой вариант и вариант с const std::string & равнозначны.

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

> Таким макаром мы исключаем «читерство» с использованием строк с подсчётом ссылок.

я тебя удивлю, но то что в std::string хранится длина строки, буфер выделяется с запасом, а также что в gcc есть подсчет ссылок - это одни из плюсов С++

Так что хватит ныть и признавайте свой слив, товарищ ;)


да конечно, специально криво написать решение - это не слив, хотите задачу, где плюсы С++ не так очевидны - давайте ее условие, а не специально ограничивайте решение

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

и да, хочешь убедится, что в «читерстве» С++ - только плюсы, вот тебе дополнение к задаче - нужно добавить символ '#' в конец каждой строки( ес-но после формирования списка )

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

> я тебя удивлю, но то что в std::string хранится длина строки, буфер выделяется с запасом, а также что в gcc есть подсчет ссылок - это одни из плюсов С++

Нет, наверное я тебя удивлю, но сделать строки с превентивным выделением буфера и посчётом ссылок можно и на C ;) Просто это никому не нужно и не потокобезопасно (а если будет и потокобезопасно то ещё и тормозов добавит).

да конечно, специально криво написать решение - это не слив, хотите задачу, где плюсы С++ не так очевидны - давайте ее условие, а не специально ограничивайте решение


Будет время подумаю :)

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

> Нет, наверное я тебя удивлю, но сделать строки с превентивным выделением буфера и посчётом ссылок можно и на C ;)

замечательно - я написал дополнение к задаче, покажи как это на С быстро и просто делается, а также как быстро это работает

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

замечательно - я написал дополнение к задаче, покажи как это на С быстро и просто делается, а также как быстро это работает

#include <string>
#include <iostream>
#include <list>
#include <stdlib.h>

struct person
{
  int id;
  std::string nick;

  person (int id_, const char *nick_)
    :id(id_), nick(nick_)
  {}
};

int main(int argc, char *argv[])
{
  std::list<person> list;

  int count = atoi(argv[2]);

  for(int i = 0 ; i < count; ++i)
  {
    list.push_back (person (i, argv[1]));
  }

  for (std::list<person>::iterator iter = list.begin ();
          iter != list.end (); ++iter)
  {
      (*iter).nick.push_back ('#');
  }

  return 0;                                                                                                            
}
$ time ./cpp_list dendy 10000000

real    0m6.178s
user    0m5.320s
sys     0m0.808s
$ time ./cpp_list dendy 10000000

real    0m6.185s
user    0m5.360s
sys     0m0.748s
$ time ./cpp_list dendy 10000000

real    0m6.204s
user    0m5.416s
sys     0m0.712s

код на Си (первый вариант, надо ещё подумать):

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include <sys/queue.h>

struct person {
    char *nick;
    int id;

    SLIST_ENTRY(person) next;
};

static SLIST_HEAD(, person) persons;

int
main (int argc, char **argv)
{
  long count = atol (argv[2]), i;
  struct person *person;

  SLIST_INIT(&persons);

  for (i = 0; i < count; ++i)
    {
      person = malloc (sizeof (struct person));
      person->id = i;
      person->nick = strdup (argv[1]);

      SLIST_INSERT_HEAD(&persons, person, next);
    }

  SLIST_FOREACH (person, &persons, next)
    {
      size_t len = strlen (person->nick);

      person->nick = realloc (person->nick, len + 1);
      person->nick[len] = '#'; person->nick[len + 1] = '\0';
    }

  while (!SLIST_EMPTY(&persons))
    {
      person = SLIST_FIRST(&persons);
      SLIST_REMOVE_HEAD(&persons, next);

      free (person->nick);
      free (person);
    }

  return 0;
}
$ time ./c_list dendy 10000000

real    0m3.618s
user    0m3.284s
sys     0m0.284s
$ time ./c_list dendy 10000000

real    0m3.619s
user    0m3.208s
sys     0m0.372s
$ time ./c_list dendy 10000000

real    0m3.618s
user    0m3.324s
sys     0m0.248s

С++ вариант делал на скорую руку, так что уверен, что ты сможешь предложить оптимальный вариант.

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

> С++ вариант делал на скорую руку

и опять «забыл» про argv[ 1 ], компилируешь наверное опять с -Os, и кстати твой «sys/queue.h» под самую популярную ОС всех времен и народов с их компилятором присутсвует?

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

> С++ вариант делал на скорую руку, так что уверен, что ты сможешь предложить оптимальный вариант.

У меня на 2Gb RAM плюснутый вариант сожрал всю память и своп...

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

> У меня на 2Gb RAM плюснутый вариант сожрал всю память и своп...

для 100 000 000( у sceptik в 10 раз меньше ) элементов у меня он «съел» 1.88Гб, очевидно либо вы что-то путаете, либо у вас и так память была забита

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

> либо у вас и так память была забита

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

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

и опять «забыл» про argv[ 1 ]

Мы же решили не «читерить»? ;) Во первых такой код будет не переносимый, те на gcc оно может будет и быстро, но в других реализациях, в которых нет подсчёта ссылок, ты схватишь не хилые тормоза ;). Во вторых, таки ладно, не будем вас убогих обижать, сделал я вариант без argv[1] и с -O2: http://codepad.org/chwfxqgR вот результаты:

$ g++ -o cpp_list main.cc -O2 -Wall
$ time ./cpp_list dendy 10000000

real    0m3.839s
user    0m3.292s
sys     0m0.504s
$ time ./cpp_list dendy 10000000

real    0m3.945s
user    0m3.428s
sys     0m0.476s
$ time ./cpp_list dendy 10000000

real    0m3.830s
user    0m3.280s
sys     0m0.512s

Вариант на Си собранный с -O2:

$ gcc -o c_list main.cc -O2 -Wall
$ time ./c_list dendy 10000000

real    0m3.133s
user    0m2.828s
sys     0m0.292s
$ time ./c_list dendy 10000000

real    0m3.146s
user    0m2.828s
sys     0m0.304s
$ time ./c_list dendy 10000000

real    0m3.155s
user    0m2.772s
sys     0m0.356s

Т.е. реализация на C++ всё равно медленнее.

и кстати твой «sys/queue.h» под самую популярную ОС всех времен и народов с их компилятором присутсвует?

Это ты про MSVC? можешь взять отсюда: http://fxr.watson.org/fxr/source/sys/queue.h?v=OPENBSD если нет

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

> Т.е. реализация на C++ всё равно медленнее.

Можно взглянуть на суммарный вывод strace -C от обеих программ?

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

В первом варианте похоже c++ «не читерит» и очень сильно грузит память.

strace -C ./c_list dendy 5000000
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00    0.000050           0      1186           brk
  0.00    0.000000           0         1           read
  0.00    0.000000           0         2           open
  0.00    0.000000           0         2           close
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1         1 access
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         3           mprotect
  0.00    0.000000           0         6           mmap2
  0.00    0.000000           0         2           fstat64
  0.00    0.000000           0         1           set_thread_area
------ ----------- ----------- --------- --------- ----------------
100.00    0.000050                  1206         1 total

strace -C ./cpp_list dendy 5000000
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 78.85    0.000082           0      2962           brk
 21.15    0.000022           6         4           read
  0.00    0.000000           0         5           open
  0.00    0.000000           0         5           close
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1         1 access
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         5           mprotect
  0.00    0.000000           0        15           mmap2
  0.00    0.000000           0         5           fstat64
  0.00    0.000000           0         1           set_thread_area
------ ----------- ----------- --------- --------- ----------------
100.00    0.000104                  3005         1 total

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

Только ключ не -C, а -c.

$ strace -c ./cpp_list dendy 10000000
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00    0.000114           0      4145           brk
  0.00    0.000000           0         4           read
  0.00    0.000000           0         5           open
  0.00    0.000000           0         5           close
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         6         6 access
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         8           mprotect
  0.00    0.000000           0        14           mmap2
  0.00    0.000000           0         5           fstat64
  0.00    0.000000           0         1           set_thread_area
------ ----------- ----------- --------- --------- ----------------
100.00    0.000114                  4195         6 total

$ strace -c ./c_list dendy 10000000
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00    0.000022           0      2370           brk
  0.00    0.000000           0         1           read
  0.00    0.000000           0         2           open
  0.00    0.000000           0         2           close
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         3         3 access
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         4           mprotect
  0.00    0.000000           0         6           mmap2
  0.00    0.000000           0         2           fstat64
  0.00    0.000000           0         1           set_thread_area
------ ----------- ----------- --------- --------- ----------------
100.00    0.000022                  2393         3 total

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

В целом ничего удивительного я не вижу, вполне обычная картина для STL.

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

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

которых нет подсчёта ссылок, ты схватишь не хилые тормоза ;)

не будем вас убогих обижать



ты дебил - код полностью переносимый и подсчет ссылок не «бесплатен», и если он дает выигрыш на копировании, то при этом дает тормоза в других местах ( и да - gcc тут поступает не по стандарту ), и, сюрприз, если собрать MSVC - будет работать быстрее

Т.е. реализация на C++ всё равно медленнее.


а теперь попробуй дать на вход строку в несколько раз длиннее

можешь взять отсюда: http://fxr.watson.org/fxr/source/sys/queue.h?v=OPENBSD если нет


чтоб тебе все программы приходилось так собирать

ahonimous
()

съездил в отпуск - все упустил :(

к чему пришли, дорогие мои?

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

ты дебил - код полностью переносимый и подсчет ссылок не «бесплатен», и если он дает выигрыш на копировании, то при этом дает тормоза в других местах ( и да - gcc тут поступает не по стандарту ), и, сюрприз, если собрать MSVC - будет работать быстрее

Во-первых, я тебя дебилом не называл, и тебе не советую так называть меня и других. Так ты будешь меньше выдавать себя за прыщавого школьника.

Во-вторых, согласен я не очень корректно выразился насчёт «переносимости кода», я имел ввиду то что тормоза могут быть на другом компиляторе с другой реализацией STL.

В-третьих, утверждение о том что на MSVC будет быстрее у меня вызывает скепсис ;) в интернетах полно бенчмарков где MSVC`шный компилятор сливает GCC/G++, а если и не сливает то совсем ненамного.

а теперь попробуй дать на вход строку в несколько раз длиннее

попробовал, результат тот же — C++ слил:

$ time ./c_list dendydendydendydendy 10000000

real    0m3.666s
user    0m3.232s
sys     0m0.408s

$ time ./cpp_list dendydendydendydendy 10000000

real    0m4.168s
user    0m3.384s
sys     0m0.736s

чтоб тебе все программы приходилось так собирать

проблем то ;)

$ curl 'http://ftp5.usa.openbsd.org/pub/OpenBSD/src/sys/sys/queue.h' -o bsd_queue.h

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

Ах да, забыл отметить:

и да - gcc тут поступает не по стандарту

в стандарте это отдано на откуп реализациям ;) Поэтому gcc ничего не нарушает.

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

> Во-первых, я тебя дебилом не называл, и тебе не советую так называть меня и других. Так ты будешь меньше выдавать себя за прыщавого школьника.

это ответ на твое оскорбление - у тебя короткая память

имел ввиду то что тормоза могут быть на другом компиляторе с другой реализацией STL.


в данном случае все наоборот, подсчет ссылок + отложенное копирование было не лучшее решение

В-третьих, утверждение о том что на MSVC будет быстрее у меня вызывает скепсис ;)


я не предположил - я проверил через shark, все оказалось банально - подсчет ссылок не помог gcc, т.к. копия все-равно создается после push_back, при этом в gcc-м варианте слишком долго отрабатывал reserve( в том числе и перед push_back - что странно, т.к. должно было выделится минимум 8 байт на буфер при создании строки )

попробовал, результат тот же — C++ слил:


но с меньшей разницей - и при длине строки уже в 80 символов у меня вариант на С++, даже для g++, оказался быстрей

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

> в стандарте это отдано на откуп реализациям ;) Поэтому gcc ничего не нарушает.

в следующем стандарте вроде явно собираются запретить, но это конечно пока ничего не значит

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

но с меньшей разницей - и при длине строки уже в 80 символов у меня вариант на С++, даже для g++, оказался быстрей

не знаю как ты мерил, у меня C++STL всё так же плох. разница таже ~0.6-0.9sec причём эта константа наблюдается уже в трёх тестах подряд с разными длинами строк

$ time ./cpp_list dendydendydendydendydendydendydendydendydendydendydendydendydendydendydendydendy 10000000

real    0m6.827s
user    0m4.736s
sys     0m1.992s

$ time ./c_list dendydendydendydendydendydendydendydendydendydendydendydendydendydendydendydendy 10000000

real    0m6.184s
user    0m5.140s
sys     0m0.920s
sceptik
()
Ответ на: комментарий от ahonimous

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

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

> .6-0.9sec причём эта константа

похоже ты говоришь неправду ;) во-первых это показывают твои измерения( разница у тебя сократилась ), а во-вторых - ты хочешь сказать, что strlen в цикле на 10000000 итераций отрабатывает одинаково быстро для строки из 5 символов и 80-ти?

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

> C++STL вариант стабильно делает больше выделений памяти, что на мой взгляд может приводить к фргаментации памяти процесса, через некоторое время.

как я уже говорил - вариант с g++ тут действительно хуже сишного, в том числе из-за того, что тот же вариант на msvc не тратит время на дополнительное резервирование памяти

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

> К тому же, как видно из strace -C C++STL вариант стабильно делает больше выделений памяти, что на мой взгляд может приводить к фргаментации памяти процесса, через некоторое время.

Надо бы еще valgrind с massif/cachegrind натравить - какова разница в используемой памяти, нагрузке на кэш интересно.

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

Вот вариант с кэшированием длины строки:

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include <sys/queue.h>

struct string {
    char *data;
    size_t length;
};

struct person {
    struct string nick;
    int id;

    SLIST_ENTRY(person) next;
};

static SLIST_HEAD(, person) persons;

int
main (int argc, char **argv)
{
  long count = atol (argv[2]), i;
  struct person *person;

  size_t slen = strlen (argv[1]);

  SLIST_INIT(&persons);

  for (i = 0; i < count; ++i)
    {
      person = malloc (sizeof (struct person));
      person->id = i;
      person->nick.length = slen;

      person->nick.data = malloc (slen + 1);
      memmove (person->nick.data, argv[1], slen + 1);

      SLIST_INSERT_HEAD(&persons, person, next);
    }

  SLIST_FOREACH (person, &persons, next)
    {
      person->nick.data = realloc (person->nick.data, person->nick.length + 1);
      person->nick.data[person->nick.length] = '#';
      person->nick.data[person->nick.length + 1] = '\0';
    }

  while (!SLIST_EMPTY(&persons))
    {
      person = SLIST_FIRST(&persons);
      SLIST_REMOVE_HEAD(&persons, next);

      free (person->nick.data);
      free (person);
    }

  return 0;
}

Результаты под грифом «Нас не догонят!»

$ time ./c_list dendydendydendydendydendydendydendydendydendydendydendydendydendydendydendydendy 10000000

real    0m3.926s
user    0m2.864s
sys     0m1.036s
$ time ./c_list dendydendydendydendydendydendydendydendydendydendydendydendydendydendydendydendy 10000000

real    0m3.911s
user    0m2.856s
sys     0m1.020s
$ time ./c_list dendydendydendydendydendydendydendydendydendydendydendydendydendydendydendydendy 10000000

real    0m3.905s
user    0m2.808s
sys     0m1.060s

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

Делалось на скорую руку пока с работы шёл. добавить одну строку кода и всё будет корректно.

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