Aether
|
|
« : 06-09-2016 08:43 » |
|
Доброго дня.
Интересует вопрос: как лучше организовать анализ работы стандартного менеджера памяти malloc - free? То есть посмотреть: сколько раз и что выделялось, освобождалось, когда?
Поскольку сейчас активно пользуюсь комплектом tcc, то в нём нет особого внешнего функционала.
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #1 : 06-09-2016 09:22 » |
|
malloc, realloc, free и т.д. - обычные функции, импортируемые из glibc. Соотв., мы можешь в своем приложении написать собственные функции-обертки с той же сигнатурой и поведением. На старте надо динамически подключить glibc и получить адреса нужных функций управления памятью. Из своих оберток ты будешь вызывать эти библиотечные фукции. В обертках делай все нужные действия по контролю. Профилировщики памяти так и работают. Можно даже посмотреть их исходники. В списке модулей на линковку твой модуль с обертками должен стоять раньше библиотек.
Для не-POSIX ОС не подскажу. В каждой по своему.
|
|
« Последнее редактирование: 06-09-2016 09:25 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Aether
|
|
« Ответ #2 : 06-09-2016 12:50 » |
|
Хорошая мысль, спасибо. На самом деле я хотел бы узнать больше. Предположим, мне нужно хранить разнородные данные, полный размер которых мне изначально неизвестен. Что-то может загрузиться позже, что-то вставиться в середину и так далее. Пользоваться указателями не очень удобно, когда речь идёт о простоте их перебора и последующей обработки данных. Естественно, сначала пошёл от простого, строить некоторую абстракцию: 1. Создал структуру - Store, которая содержит число компонентов внутри себя и указатель на таблицу указателей элементов Box - ppTable. 2. При импорте новых данных таблица ppTable обновляется, и организуется новый Box. ... В общем это работает, кроме ppTable ничего лишний раз не переписывается, но потери в скорости и объёме эпичны, хорошо, что это для внутреннего пользования, которое не требует хранения ни огромных массивов, ни предполагает мгновенного результата. Однако, если задуматься, то многое, как мне кажется задублировано чрезмерно. Например, чисто для рассуждения: char* tpcString = malloc(strlen(tpcSourceString) + 1); Я создаю указатель tcpString, на который выделяется участок памяти под строку-источник, так вот размер выделенного участка уже где-то хранится (Так как иначе не сможет работать free.), и не имеет смысла создавать отдельно переменную для хранения его размера, например, так: typedef struct _String { unsigned int uiSize; char* pcString; } String; ... В общем иногда посещает мысль, что делаю очередной велосипед, вероятно, подобных подход уже решён, в каком-нибудь Ruby, которого я не знаю.
|
|
« Последнее редактирование: 06-09-2016 17:01 от Aether »
|
Записан
|
|
|
|
RXL
|
|
« Ответ #3 : 07-09-2016 12:57 » |
|
Называется это пул объектов с адресацией по индексу. В простейшем случае это массив. Такой подход сильно ускоряет код, но есть и обратная сторона: трудность переалокации массива: * это не tread safe; * надо обращаться к элементам массива только по индексу - нельзя хранить указатели.
Если только целевая система не embedded, то лучше переходи на C++. Он сложнее, зато большие возможности стандартной библиотеки.
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
darkelf
Молодой специалист
Offline
|
|
« Ответ #4 : 08-09-2016 10:18 » |
|
На самом деле я хотел бы узнать больше. Предположим, мне нужно хранить разнородные данные, полный размер которых мне изначально неизвестен. Что-то может загрузиться позже, что-то вставиться в середину и так далее. Пользоваться указателями не очень удобно, когда речь идёт о простоте их перебора и последующей обработки данных. Естественно, сначала пошёл от простого, строить некоторую абстракцию: 1. Создал структуру - Store, которая содержит число компонентов внутри себя и указатель на таблицу указателей элементов Box - ppTable. 2. При импорте новых данных таблица ppTable обновляется, и организуется новый Box. ... В общем это работает, кроме ppTable ничего лишний раз не переписывается, но потери в скорости и объёме эпичны, хорошо, что это для внутреннего пользования, которое не требует хранения ни огромных массивов, ни предполагает мгновенного результата. Однако, если задуматься, то многое, как мне кажется задублировано чрезмерно.
можно придумать как ускорить, разными способами. Например хеширование. У меня была задача, где надо было хранить сотни тысяч параметров, каждый из которых являлся структурой с полутора-двумя десятками разных полей. Был необходим поиск по этим параметрам, а точнее по их именам, представленных в виде ASCIIZ строк. Т.к. все параметры хранились в списке, то по верх него была ещё организована хэш-таблица. При добавлении параметра в список или удалении из него соответствующим образом модифицировалась и таблица. Всё было написано на C, с использованием макро-библиотеки списков из FreeBSD (если интересно см. sys/queue.h - с небольшими модификациями он может быть использована с любым компилятором C89 и выше, даже с msvc 6.0). Правда с точки зрения C++ конечно код был не сильно красивый - связки для списка и хеш-таблицы были в самих описателях параметров, но работало довольно быстро и перерасхода памяти, вроде как, не сильно наблюдалось. Если надо побороться за память, и кстати скорость выделения, можно посмотреть в сторону SLAB-аллокаторов. Например, чисто для рассуждения: char* tpcString = malloc(strlen(tpcSourceString) + 1); Я создаю указатель tcpString, на который выделяется участок памяти под строку-источник, так вот размер выделенного участка уже где-то хранится (Так как иначе не сможет работать free.), и не имеет смысла создавать отдельно переменную для хранения его размера, например, так: typedef struct _String { unsigned int uiSize; char* pcString; } String; ... посмотрите, есть-ли в вашей libc функция msize() - она позволяет определить размер выделенной памяти по переданному указателю.
|
|
|
Записан
|
|
|
|
Aether
|
|
« Ответ #5 : 08-09-2016 18:56 » |
|
* надо обращаться к элементам массива только по индексу - нельзя хранить указатели.
Вот тут немного спорный момент, я отметил, что для широкого спектра задач полезнее следующее: каждая запись, объект хранения, должен иметь: 1) Указатель на начало данных в записи. 2) Порядковый номер записи - индекс. 3) Уникальный номер записи - идентификатор.
То есть, когда я организую склад, и начинаю заполнять его коробками с товаром, то индекс и идентификатор должны начать расти одновременно, но при вставке данных в середину этого потока объектов должно произойти следующее: порядковый номер вставляемой записи должен стать тем, куда мы его вставляем, а индекс должен быть присвоен, как если бы он был поставлен в конец. Например, [Box: Index 0: ID0] [Box: Index 1: ID1] [Box: Index 2: ID2]
Вставляю на позицию 1 ещё одну коробку, получается: [Box: Index 0: ID0] [Box: Index 1: ID3] [Box: Index 2: ID1] [Box: Index 3: ID2]
Такой подход позволяет организовать быструю последовательную обработку объектов, в то же время, благодаря идентификаторам можно всегда найти нужный объект, где бы его новое место ни находилось. Идентификатор должен быть уникальным в пределах своего склада. Также, есть возможность обеспечить синхронность двух складов, например, как говорил darkelf, можно один склад заполнить данными структур, а второй склад заполнить названиями полей этих структур в строковом виде. Ну а далее поиск порядкового номера в складе 2, а затем запрос на данные из склада 1 - даёт возможность запрашивать данные по названию полей. По идее, имеет смысл, организовать запись Box, как: struct { void* PrevBox; // предположим это - коробка №5 void* NextBox; unsigned int uiSize; // для 32 битных систем unsigned int uiIndex; // = 5 Хотя это поле можно и не делать, // в процессе поиска всё равно будет перебор // по ряду указателей и оно будет автоматически // доступно, как результат счётчика с начала ряда. unsigned int uiID; unsigned char cData[] // на начало этих данных должны // указывать PrevBox от 6 коробки и NextBox от 4 коробки } Для определения количества коробок на складе, числа складов и начала складов нужна также специальная структура. Если надо побороться за память, и кстати скорость выделения, можно посмотреть в сторону SLAB-аллокаторов.
посмотрите, есть-ли в вашей libc функция msize() - она позволяет определить размер выделенной памяти по переданному указателю.
Спасибо, информацию изучу. В комплекте tcc <malloc.h> этой функции не наблюдаю, позже посмотрю ещё на свежую голову. Извиняюсь за много слов - это скорее мысли вслух.
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #6 : 09-09-2016 11:16 » |
|
Ничего спорного. Указатель на storage (не важно, какая у него внутренняя структура и метод индексации) есть постоянно, но он должен быть один - иначе ты не сможешь переалоцировать storage. А вот указатель на элемент не должен хранится и может существовать только в рамках операции с элементом, как результат функции по указателю на storage и ключу элемента. prev/next - не изобретай велосипед. Это старо как мир - достаточно просто погуглить. Именно по этому я рекомендую C++ с STL, как готовый инструментарий с множеством различных контейнеров и обобщенным интерфейсом для написания новых. Кстати, смотреть надо не malloc.h, а stdlib.h. http://pubs.opengroup.org/onlinepubs/009695399/basedefs/stdlib.h.htmlФункцию msize ваще не нашел. Только у MS нашел _msize. Не пользуйся нестандартными решениями. Если тебе нужен размер выделенной области, его должна хранить твоя программа, тем более именно она и говорила malloc, сколько нужно памяти. Стандартные функции работы с памятью: malloc, calloc, realloc, free.
|
|
« Последнее редактирование: 09-09-2016 11:28 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
darkelf
Молодой специалист
Offline
|
|
« Ответ #7 : 12-09-2016 05:06 » |
|
Функцию msize ваще не нашел. Только у MS нашел _msize. Не пользуйся нестандартными решениями. Если тебе нужен размер выделенной области, его должна хранить твоя программа, тем более именно она и говорила malloc, сколько нужно памяти. Стандартные функции работы с памятью: malloc, calloc, realloc, free.
В glibc есть свой вариант, называемый malloc_usable_size(). Но как Вы правильно заметили - функция нестандартная, так-что лучше не использовать.
|
|
|
Записан
|
|
|
|
Aether
|
|
« Ответ #8 : 22-01-2017 15:49 » |
|
Решил выложить небольшие бывшие зарисовки по теме: Называется это пул объектов с адресацией по индексу.
Отражают они лишь только то, что хотелось бы получить, как расширенные возможности стандартного менеджера, именно в "С", а получилось, как надстройка. Пусть для истории, наверное. Вырезка из AE_COMMON.h(click to show) typedef void* pV; typedef unsigned int ui; AE_STORE.h(click to show) // Windows - 1251 // Этот модуль позволяет организовывать хранение данных в системе "СКЛАД". // Склад - это список записей, каждая из которых ссылается на данные. // Данные имеют уникальные для данного склада индексы и порядковые ордера.
#ifndef _LIB_STORE_H #define _LIB_STORE_H
#define _AE_STORE_DEBUG
#include "AE_COMMON.h"
#define AE_STORE_BASE_OFFSET 0x0000 // 0000 0000 0000 0000 #define AE_STORE_WARN_OFFSET 0x0100 // 0000 0001 0000 0000 #define AE_STORE_ERR_ANDMASK 0x00FF // 0000 0000 1111 1111 #define AE_STORE_WARN_ANDMASK 0xFF00 // 1111 1111 0000 0000 #define AE_STORE_LAST_UI 0xFFFFFFFF
typedef enum _AE_STORE_ERR // Перечислитель ошибок и предупреждений // в работе склада. { // Ошибки AE_STORE_OK = AE_STORE_BASE_OFFSET, // Ошибки не обнаружены. AE_STORE_MEM, // Ошибка в работе памяти.
// Предупреждения AE_STORE_WARN_INPUT = AE_STORE_WARN_OFFSET, // Неправильные введённые данные. AE_STORE_WARN_MAX, // Достигнут максимум индексов или ордеров. AE_STORE_WARN_INDEX, // Несуществующий индекс. AE_STORE_WARN_FIND // Поиск не удался. } AE_STORE_ERR;
typedef struct _AE_REC // REC - это запись об одной ячейке данных // на складе. { ui uiIndex; // Индекс каждой записи уникален // в пределах одного склада, и он всегда растёт. // Исчерпание числа индексов выдаёт предупреждение // AE_STORE_WARN_INDEX. ui uiDataSize; // Размер хранимой в ячейке склада информации // в байтах. pV pData; // Указатель на ячейку склада. } REC, *pREC, **ppREC;
typedef struct _AE_STORE // STORE - это главная структура склада, // содержащая всю необходимую информацию о нём, // и о каждой из его ячеек. { AE_STORE_ERR ERR; // Состояние склада, наличие в нём ошибок // и предупреждений. ui uiStoreSize; // Текущий размер, занимаемый главной записью STORE. ui uiFreeIndex; // Значение последнего свободного индекса. ui uiFreeOrder; // Значение последнего свободного ордера. // Косвенно равно числу элементов на складе. REC psRec[]; // Массив записей о ячейках склада. } STORE, *pSTORE, **ppSTORE;
pSTORE store_create (void); // Конструктор. // Функция создаёт новый склад. // Возвращает 0 в случае ошибки.
void store_destroy (ppSTORE tppSTORE); // Деструктор. // Функция ликвидирует склад и всю информацию // в нём.
ui store_add (ppSTORE tppSTORE, pV pData, ui uiDataSize); // Функция создаёт на складе ячейку и // копирует туда информацию из источника. // Возвращает значение индекса.
void store_change_order (pSTORE tpSTORE, ui uiDstOrder, ui uiSrcOrder); // Функция меняет записи на складе местами. // Ордеры изменяются, индексы остаются.
pV store_data_by_index (pSTORE tpSTORE, ui uiIndex); // Функция запрашивает указатель на данные // по индексу.
pV store_data_by_order (pSTORE tpSTORE, ui uiOrder); // Функция запрашивает указатель на данные // по ордеру.
void store_del_by_index (ppSTORE tppSTORE, ui uiIndex); // Функция удаляет данные со склада по индексу.
void store_del_by_order (ppSTORE tppSTORE, ui uiOrder); // Функция удаляет данные со склада по ордеру.
ui store_find_by_index (pSTORE tpSTORE, ui uiIndex); // Функция возвращает ордер по индексу.
ui store_get_err (pSTORE tpSTORE); // Функция возвращает "1" если есть ошибки.
AE_STORE_ERR store_get_error (pSTORE tpSTORE); // Функция возвращает статус склада.
ui store_get_records (pSTORE tpSTORE); // Функция возвращает число элементов на складе.
ui store_get_warn (pSTORE tpSTORE); // Функция возвращает "1" если есть предупреждения. // И сбрасывает статус склада на AE_STORE_OK.
ui store_is_ok (pSTORE tpSTORE); // Функция возвращает "1" если есть ошибки или // предупреждения.
void store_print (pSTORE tpSTORE); // Функция выводит в stdout информацию о складе.
ui store_size_by_order (pSTORE tpSTORE, ui uiOrder); // Функция возвращает размер данных по ордеру. #endif AE_STORE.c(click to show) #include "AE_STORE.h"
pSTORE store_create (void) { #ifdef _AE_STORE_DEBUG printf("store_create: start\n"); #endif pSTORE tpSTORE = malloc(sizeof(STORE)); if (tpSTORE == 0) return 0; tpSTORE->ERR = AE_STORE_OK; tpSTORE->uiStoreSize = sizeof(STORE); tpSTORE->uiFreeIndex = 0; tpSTORE->uiFreeOrder = 0; #ifdef _AE_STORE_DEBUG printf("store_create: OK\n"); #endif return tpSTORE; }
void store_destroy (ppSTORE tppSTORE) { #ifdef _AE_STORE_DEBUG printf("store_destroy: start\n"); #endif if ((*tppSTORE) == 0) return; if ((*tppSTORE)->ERR & AE_STORE_ERR_ANDMASK) return; for (ui i = 0; i < (*tppSTORE)->uiFreeOrder; i++) { free((*tppSTORE)->psRec[i].pData); } free(*tppSTORE); *tppSTORE = 0; #ifdef _AE_STORE_DEBUG printf("store_destroy: OK\n"); #endif }
ui store_add (ppSTORE tppSTORE, pV pData, ui uiDataSize) { #ifdef _AE_STORE_DEBUG printf("store_add: start\n"); #endif if (((*tppSTORE) == 0) || (pData == 0) || (uiDataSize == 0)) { (*tppSTORE)->ERR = AE_STORE_WARN_INPUT; return 0; } if ((*tppSTORE)->ERR & AE_STORE_ERR_ANDMASK) return 0; if (((*tppSTORE)->uiFreeIndex == AE_STORE_LAST_UI) || ((*tppSTORE)->uiFreeOrder == AE_STORE_LAST_UI)) { (*tppSTORE)->ERR = AE_STORE_WARN_MAX; return 0; } void* tpData = malloc(uiDataSize); if (tpData == 0) { (*tppSTORE)->ERR = AE_STORE_MEM; return 0; } memcpy(tpData, pData, uiDataSize); if ((*tppSTORE)->uiStoreSize > (AE_STORE_LAST_UI - sizeof(REC))) { (*tppSTORE)->ERR = AE_STORE_MEM; free(tpData); return 0; } ui uiNewStoreSize = (*tppSTORE)->uiStoreSize + sizeof(REC); pSTORE tpSTORE = malloc(uiNewStoreSize); if (tpSTORE == 0) { (*tppSTORE)->ERR = AE_STORE_MEM; free(tpData); return 0; } memcpy(tpSTORE, (*tppSTORE), (*tppSTORE)->uiStoreSize); tpSTORE->uiStoreSize = uiNewStoreSize; ui uiCurIndex = tpSTORE->psRec[tpSTORE->uiFreeOrder].uiIndex = tpSTORE->uiFreeIndex++; tpSTORE->psRec[tpSTORE->uiFreeOrder].uiDataSize = uiDataSize; tpSTORE->psRec[tpSTORE->uiFreeOrder].pData = tpData; tpSTORE->uiFreeOrder++; free(*tppSTORE); *tppSTORE = tpSTORE; #ifdef _AE_STORE_DEBUG printf("store_add: OK\n"); #endif return uiCurIndex; }
void store_change_order (pSTORE tpSTORE, ui uiDstOrder, ui uiSrcOrder) { #ifdef _AE_STORE_DEBUG printf("store_change_order: start\n"); #endif if (tpSTORE == 0) return; if (tpSTORE->ERR & AE_STORE_ERR_ANDMASK) return; if ((tpSTORE->uiFreeOrder <= uiDstOrder) || (tpSTORE->uiFreeOrder <= uiSrcOrder)) { tpSTORE->ERR = AE_STORE_WARN_INPUT; return; } REC ts; ts.uiIndex = tpSTORE->psRec[uiDstOrder].uiIndex; tpSTORE->psRec[uiDstOrder].uiIndex = tpSTORE->psRec[uiSrcOrder].uiIndex; tpSTORE->psRec[uiSrcOrder].uiIndex = ts.uiIndex; ts.uiDataSize = tpSTORE->psRec[uiDstOrder].uiDataSize; tpSTORE->psRec[uiDstOrder].uiDataSize = tpSTORE->psRec[uiSrcOrder].uiDataSize; tpSTORE->psRec[uiSrcOrder].uiDataSize = ts.uiDataSize; ts.pData = tpSTORE->psRec[uiDstOrder].pData; tpSTORE->psRec[uiDstOrder].pData = tpSTORE->psRec[uiSrcOrder].pData; tpSTORE->psRec[uiSrcOrder].pData = ts.pData; #ifdef _AE_STORE_DEBUG printf("store_change_order: OK\n"); #endif }
pV store_data_by_index (pSTORE tpSTORE, ui uiIndex) { #ifdef _AE_STORE_DEBUG printf("store_data_by_index: start\n"); #endif if (tpSTORE == 0) return (pV)0; if (tpSTORE->ERR & AE_STORE_ERR_ANDMASK) return (pV)0; ui tuiOrder = store_find_by_index(tpSTORE, uiIndex); if (tpSTORE->ERR) return (pV)0; #ifdef _AE_STORE_DEBUG printf("store_data_by_index: OK\n"); #endif return tpSTORE->psRec[tuiOrder].pData; }
pV store_data_by_order (pSTORE tpSTORE, ui uiOrder) { #ifdef _AE_STORE_DEBUG printf("store_data_by_order: start\n"); #endif if (tpSTORE == 0) return (pV)0; if (tpSTORE->ERR & AE_STORE_ERR_ANDMASK) return (pV)0; if (tpSTORE->uiFreeOrder <= uiOrder) { tpSTORE->ERR = AE_STORE_WARN_INPUT; return (pV)0; } #ifdef _AE_STORE_DEBUG printf("store_data_by_order: OK\n"); #endif return tpSTORE->psRec[uiOrder].pData; }
void store_del_by_index (ppSTORE tppSTORE, ui uiIndex) { #ifdef _AE_STORE_DEBUG printf("store_del_by_index: start\n"); #endif if (*tppSTORE == 0) return; if ((*tppSTORE)->ERR & AE_STORE_ERR_ANDMASK) return; ui tuiOrder = store_find_by_index(*tppSTORE, uiIndex); if ((*tppSTORE)->ERR) return; store_del_by_order(tppSTORE, tuiOrder); #ifdef _AE_STORE_DEBUG printf("store_del_by_index: OK\n"); #endif }
void store_del_by_order (ppSTORE tppSTORE, ui uiOrder) { #ifdef _AE_STORE_DEBUG printf("store_del_by_order: start\n"); #endif if (*tppSTORE == 0) return; if ((*tppSTORE)->ERR & AE_STORE_ERR_ANDMASK) return; if ((*tppSTORE)->uiFreeOrder <= uiOrder) { (*tppSTORE)->ERR = AE_STORE_WARN_INPUT; return; } free((*tppSTORE)->psRec[uiOrder].pData); ui uiNewStoreSize = (*tppSTORE)->uiStoreSize - sizeof(REC); pSTORE tpSTORE = malloc(uiNewStoreSize); if (tpSTORE == 0) { (*tppSTORE)->ERR = AE_STORE_MEM; return; } tpSTORE->ERR = AE_STORE_OK; tpSTORE->uiStoreSize = uiNewStoreSize; tpSTORE->uiFreeIndex = (*tppSTORE)->uiFreeIndex; tpSTORE->uiFreeOrder = (*tppSTORE)->uiFreeOrder - 1; ui tui = 0; for (ui i = 0; i < (*tppSTORE)->uiFreeOrder; i++) { if (i == uiOrder) continue; tpSTORE->psRec[tui].uiIndex = (*tppSTORE)->psRec[i].uiIndex; tpSTORE->psRec[tui].uiDataSize = (*tppSTORE)->psRec[i].uiDataSize; tpSTORE->psRec[tui].pData = (*tppSTORE)->psRec[i].pData; tui++; } free(*tppSTORE); *tppSTORE = tpSTORE; #ifdef _AE_STORE_DEBUG printf("store_del_by_order: OK\n"); #endif }
ui store_find_by_index (pSTORE tpSTORE, ui uiIndex) { #ifdef _AE_STORE_DEBUG printf("store_find_by_index: start\n"); #endif if (tpSTORE == 0) return 0; if (tpSTORE->ERR & AE_STORE_ERR_ANDMASK) return 0; if (tpSTORE->uiFreeIndex <= uiIndex) { tpSTORE->ERR = AE_STORE_WARN_INDEX; return 0; } for (ui i = 0; i < tpSTORE->uiFreeOrder; i++) { if (tpSTORE->psRec[i].uiIndex == uiIndex) { #ifdef _AE_STORE_DEBUG printf("store_find_by_index: OK\n"); #endif tpSTORE->ERR = AE_STORE_OK; return i; } } #ifdef _AE_STORE_DEBUG printf("store_find_by_index: INDEX NOT FOUND\n"); #endif tpSTORE->ERR = AE_STORE_WARN_FIND; return 0; }
ui store_get_err (pSTORE tpSTORE) { #ifdef _AE_STORE_DEBUG printf("store_get_err: start\n"); #endif if (tpSTORE == 0) return 1; #ifdef _AE_STORE_DEBUG printf("store_get_err: OK\n"); #endif if (tpSTORE->ERR & AE_STORE_ERR_ANDMASK) return 1; else return 0; }
AE_STORE_ERR store_get_error (pSTORE tpSTORE) { #ifdef _AE_STORE_DEBUG printf("store_get_error: start\n"); #endif if (tpSTORE == 0) return AE_STORE_MEM; #ifdef _AE_STORE_DEBUG printf("store_get_error: OK\n"); #endif return tpSTORE->ERR; }
ui store_get_records (pSTORE tpSTORE) { #ifdef _AE_STORE_DEBUG printf("store_get_records: start\n"); #endif if (tpSTORE == 0) return 0; if (tpSTORE->ERR & AE_STORE_ERR_ANDMASK) return 0; #ifdef _AE_STORE_DEBUG printf("store_get_records: OK\n"); #endif return tpSTORE->uiFreeOrder; }
ui store_get_warn (pSTORE tpSTORE) { #ifdef _AE_STORE_DEBUG printf("store_get_warn: start\n"); #endif if (tpSTORE == 0) return 1; #ifdef _AE_STORE_DEBUG printf("store_get_warn: OK\n"); #endif if (tpSTORE->ERR & AE_STORE_WARN_ANDMASK) { tpSTORE->ERR = AE_STORE_OK; return 1; } else return 0; }
ui store_is_ok (pSTORE tpSTORE) { #ifdef _AE_STORE_DEBUG printf("store_is_ok: start\n"); #endif if (tpSTORE == 0) return 1; #ifdef _AE_STORE_DEBUG printf("store_is_ok: OK\n"); #endif if (tpSTORE->ERR) return 1; return 0; }
void store_print (pSTORE tpSTORE) { #ifdef _AE_STORE_DEBUG printf("store_print: start\n"); #endif if (tpSTORE == 0) return; printf(" *** STORE INFO ***\n"); printf(" Address : %u\n", (ui)tpSTORE); printf(" ERR : %u\n", (ui)tpSTORE->ERR); switch (tpSTORE->ERR) { case AE_STORE_OK: printf("<< AE_STORE_OK >>\n"); break; case AE_STORE_MEM: printf("<< AE_STORE_MEM >>\n"); return; case AE_STORE_WARN_INPUT: printf("<< AE_STORE_WARN_INPUT >>\n"); break; case AE_STORE_WARN_MAX: printf("<< AE_STORE_WARN_MAX >>\n"); break; case AE_STORE_WARN_INDEX: printf("<< AE_STORE_WARN_INDEX >>\n"); break; case AE_STORE_WARN_FIND: printf("<< AE_STORE_WARN_FIND >>\n"); break; default: printf("<< SOME ERROR >>\n"); } printf(" uiStoreSize : %u\n", tpSTORE->uiStoreSize); printf(" uiFreeIndex : %u\n", tpSTORE->uiFreeIndex); printf(" uiFreeOrder : %u\n", tpSTORE->uiFreeOrder); if (tpSTORE->uiFreeOrder == 0) { printf(" *** NO DATA ***\n *** END ***\n\n"); return; } printf(" *** DATA ***\n"); for (ui i = 0; i < tpSTORE->uiFreeOrder; i++) { printf(" Record <Order> : %u\n", i); printf(" Record <Index> : %u\n", tpSTORE->psRec[i].uiIndex); printf(" uiDataSize : %u\n", tpSTORE->psRec[i].uiDataSize); printf(" Data address : %u\n", tpSTORE->psRec[i].pData); printf(" STRING >>>\n"); char* tpc = (char*)tpSTORE->psRec[i].pData; ui k = 0; for (ui j = 0; j < tpSTORE->psRec[i].uiDataSize; j++) { if (*tpc == 0x0A) printf("%3i -X- ", *tpc); else printf("%3i -%c- ", *tpc, *tpc); tpc++; k++; if (k == 8) {printf("\n"); k = 0;} } if (k != 0) printf("\n"); } printf(" *** END ***\n\n"); #ifdef _AE_STORE_DEBUG printf("store_print: OK\n"); #endif }
ui store_size_by_order (pSTORE tpSTORE, ui uiOrder) { #ifdef _AE_STORE_DEBUG printf("store_size_by_order: start\n"); #endif if (tpSTORE == 0) return 0; if (tpSTORE->ERR & AE_STORE_ERR_ANDMASK) return 0; if (tpSTORE->uiFreeOrder <= uiOrder) { tpSTORE->ERR = AE_STORE_WARN_INPUT; return 0; } #ifdef _AE_STORE_DEBUG printf("store_size_by_order: OK\n"); #endif return tpSTORE->psRec[uiOrder].uiDataSize; }
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #9 : 22-01-2017 16:50 » |
|
Давайте все таки уважать друг друга и беречь глаза друг друга — не показывать такого кода и такого чудовищного форматирования! По сему от ответа воздержусь.
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Aether
|
|
« Ответ #10 : 22-01-2017 20:05 » |
|
Давайте все таки уважать друг друга и беречь глаза друг друга — не показывать такого кода и такого чудовищного форматирования! По сему от ответа воздержусь.
Ну, я походу нашёл эти зарисовки, обрадовался, и поспешил поделиться. Так что, если что не так, то сорри. И какое форматирование не чудовищное?
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #11 : 22-01-2017 21:19 » |
|
Не это точно. Если бы студент это накопипастил, я бы понял.
|
|
« Последнее редактирование: 22-01-2017 21:22 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Aether
|
|
« Ответ #12 : 23-01-2017 08:57 » |
|
Не, RXL, надо быть конструктивнее. Для меня часто оформление - дело десятое, если об этом речь? Так приведите пример: какой формат устроит, чтобы читалось удобнее?
Мои задачи - поддержка инженерных проектов, часто собственных. Сейчас, например, занимаюсь геодезией: перевожу топографию в 3D, оцениваю объёмы земляных работ, и предоставляю план местности до и после строительства. Естественно, когда работа по ряду объектов завершится, все остатки лягут где-то далеко и надолго, и вести кто-то другой их не будет. Поэтому, если есть нюансы, то давайте их обсудим. Критику я люблю, когда она развивает.
|
|
|
Записан
|
|
|
|
Джон
просто
Администратор
Offline
Пол:
|
|
« Ответ #13 : 23-01-2017 14:04 » |
|
Например такое pSTORE store_create(void) { #ifdef _AE_STORE_DEBUG printf("store_create: start\n"); #endif
pSTORE tpSTORE = malloc(sizeof(STORE));
if(tpSTORE == 0) return 0;
tpSTORE->ERR = AE_STORE_OK; tpSTORE->uiStoreSize = sizeof(STORE); tpSTORE->uiFreeIndex = 0; tpSTORE->uiFreeOrder = 0;
#ifdef _AE_STORE_DEBUG printf("store_create: OK\n"); #endif
return tpSTORE; } Не, RXL, надо быть конструктивнее. Для меня часто оформление - дело десятое
Нууу... тогда зачем показывать свой личный код другим?
|
|
« Последнее редактирование: 23-01-2017 14:06 от Джон »
|
Записан
|
Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома. "Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash "Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman "All science is either physics or stamp collecting." Ernest Rutherford "Wer will, findet Wege, wer nicht will, findet Gründe."
|
|
|
Aether
|
|
« Ответ #14 : 23-01-2017 15:06 » |
|
Нууу... тогда зачем показывать свой личный код другим?
а) Естественно, чтобы кто-то его почитал, но проблемы решаются по мере их поступления, и по мере желания. б) Иногда читая чужое, появляются новые мысли. Мало ли кому пригодится? в) В рамках темы для возможно лучшего представления. Хотя я бы лучше диаграмму нарисовал, но не вижу встречного интереса, поэтому не вижу целесообразности затрат. #include "AE_STORE.h"
pSTORE store_create(void) { #ifdef _AE_STORE_DEBUG printf("store_create: start\n"); #endif
pSTORE tpSTORE = malloc(sizeof(STORE));
if (tpSTORE == 0) return 0;
tpSTORE->ERR = AE_STORE_OK; tpSTORE->uiStoreSize = sizeof(STORE); tpSTORE->uiFreeIndex = 0; tpSTORE->uiFreeOrder = 0;
#ifdef _AE_STORE_DEBUG printf("store_create: OK\n"); #endif
return tpSTORE; }
void store_destroy(ppSTORE tppSTORE) { #ifdef _AE_STORE_DEBUG printf("store_destroy: start\n"); #endif
if ((*tppSTORE) == 0) return;
if ((*tppSTORE)->ERR & AE_STORE_ERR_ANDMASK) return;
for (ui i = 0; i < (*tppSTORE)->uiFreeOrder; i++) { free((*tppSTORE)->psRec[i].pData); }
free(*tppSTORE);
*tppSTORE = 0;
#ifdef _AE_STORE_DEBUG printf("store_destroy: OK\n"); #endif }
ui store_add(ppSTORE tppSTORE, pV pData, ui uiDataSize) { #ifdef _AE_STORE_DEBUG printf("store_add: start\n"); #endif
if (((*tppSTORE) == 0) || (pData == 0) || (uiDataSize == 0)) { (*tppSTORE)->ERR = AE_STORE_WARN_INPUT; return 0; }
if ((*tppSTORE)->ERR & AE_STORE_ERR_ANDMASK) return 0;
if (((*tppSTORE)->uiFreeIndex == AE_STORE_LAST_UI) || ((*tppSTORE)->uiFreeOrder == AE_STORE_LAST_UI)) { (*tppSTORE)->ERR = AE_STORE_WARN_MAX; return 0; }
void* tpData = malloc(uiDataSize);
if (tpData == 0) { (*tppSTORE)->ERR = AE_STORE_MEM; return 0; }
memcpy(tpData, pData, uiDataSize);
if ((*tppSTORE)->uiStoreSize > (AE_STORE_LAST_UI - sizeof(REC))) { (*tppSTORE)->ERR = AE_STORE_MEM; free(tpData); return 0; }
ui uiNewStoreSize = (*tppSTORE)->uiStoreSize + sizeof(REC);
pSTORE tpSTORE = malloc(uiNewStoreSize);
if (tpSTORE == 0) { (*tppSTORE)->ERR = AE_STORE_MEM; free(tpData); return 0; }
memcpy(tpSTORE, (*tppSTORE), (*tppSTORE)->uiStoreSize);
tpSTORE->uiStoreSize = uiNewStoreSize; ui uiCurIndex = tpSTORE->psRec[tpSTORE->uiFreeOrder].uiIndex = tpSTORE->uiFreeIndex++; tpSTORE->psRec[tpSTORE->uiFreeOrder].uiDataSize = uiDataSize; tpSTORE->psRec[tpSTORE->uiFreeOrder].pData = tpData; tpSTORE->uiFreeOrder++;
free(*tppSTORE);
*tppSTORE = tpSTORE;
#ifdef _AE_STORE_DEBUG printf("store_add: OK\n"); #endif
return uiCurIndex; }
void store_change_order(pSTORE tpSTORE, ui uiDstOrder, ui uiSrcOrder) { #ifdef _AE_STORE_DEBUG printf("store_change_order: start\n"); #endif
if (tpSTORE == 0) return;
if (tpSTORE->ERR & AE_STORE_ERR_ANDMASK) return;
if ((tpSTORE->uiFreeOrder <= uiDstOrder) || (tpSTORE->uiFreeOrder <= uiSrcOrder)) { tpSTORE->ERR = AE_STORE_WARN_INPUT; return; }
REC ts;
ts.uiIndex = tpSTORE->psRec[uiDstOrder].uiIndex; tpSTORE->psRec[uiDstOrder].uiIndex = tpSTORE->psRec[uiSrcOrder].uiIndex; tpSTORE->psRec[uiSrcOrder].uiIndex = ts.uiIndex;
ts.uiDataSize = tpSTORE->psRec[uiDstOrder].uiDataSize; tpSTORE->psRec[uiDstOrder].uiDataSize = tpSTORE->psRec[uiSrcOrder].uiDataSize; tpSTORE->psRec[uiSrcOrder].uiDataSize = ts.uiDataSize;
ts.pData = tpSTORE->psRec[uiDstOrder].pData; tpSTORE->psRec[uiDstOrder].pData = tpSTORE->psRec[uiSrcOrder].pData; tpSTORE->psRec[uiSrcOrder].pData = ts.pData;
#ifdef _AE_STORE_DEBUG printf("store_change_order: OK\n"); #endif }
pV store_data_by_index(pSTORE tpSTORE, ui uiIndex) { #ifdef _AE_STORE_DEBUG printf("store_data_by_index: start\n"); #endif
if (tpSTORE == 0) return 0;
if (tpSTORE->ERR & AE_STORE_ERR_ANDMASK) return 0;
ui tuiOrder = store_find_by_index(tpSTORE, uiIndex);
if (tpSTORE->ERR) return 0;
#ifdef _AE_STORE_DEBUG printf("store_data_by_index: OK\n"); #endif
return tpSTORE->psRec[tuiOrder].pData; }
pV store_data_by_order(pSTORE tpSTORE, ui uiOrder) { #ifdef _AE_STORE_DEBUG printf("store_data_by_order: start\n"); #endif
if (tpSTORE == 0) return 0;
if (tpSTORE->ERR & AE_STORE_ERR_ANDMASK) return 0;
if (tpSTORE->uiFreeOrder <= uiOrder) { tpSTORE->ERR = AE_STORE_WARN_INPUT; return 0; }
#ifdef _AE_STORE_DEBUG printf("store_data_by_order: OK\n"); #endif
return tpSTORE->psRec[uiOrder].pData; }
void store_del_by_index(ppSTORE tppSTORE, ui uiIndex) { #ifdef _AE_STORE_DEBUG printf("store_del_by_index: start\n"); #endif
if (*tppSTORE == 0) return;
if ((*tppSTORE)->ERR & AE_STORE_ERR_ANDMASK) return;
ui tuiOrder = store_find_by_index(*tppSTORE, uiIndex);
if ((*tppSTORE)->ERR) return;
store_del_by_order(tppSTORE, tuiOrder);
#ifdef _AE_STORE_DEBUG printf("store_del_by_index: OK\n"); #endif }
void store_del_by_order(ppSTORE tppSTORE, ui uiOrder) { #ifdef _AE_STORE_DEBUG printf("store_del_by_order: start\n"); #endif
if (*tppSTORE == 0) return;
if ((*tppSTORE)->ERR & AE_STORE_ERR_ANDMASK) return;
if ((*tppSTORE)->uiFreeOrder <= uiOrder) { (*tppSTORE)->ERR = AE_STORE_WARN_INPUT; return; }
free((*tppSTORE)->psRec[uiOrder].pData);
ui uiNewStoreSize = (*tppSTORE)->uiStoreSize - sizeof(REC);
pSTORE tpSTORE = malloc(uiNewStoreSize);
if (tpSTORE == 0) { (*tppSTORE)->ERR = AE_STORE_MEM; return; }
tpSTORE->ERR = AE_STORE_OK; tpSTORE->uiStoreSize = uiNewStoreSize; tpSTORE->uiFreeIndex = (*tppSTORE)->uiFreeIndex; tpSTORE->uiFreeOrder = (*tppSTORE)->uiFreeOrder - 1;
ui tui = 0; for (ui i = 0; i < (*tppSTORE)->uiFreeOrder; i++) { if (i == uiOrder) continue; tpSTORE->psRec[tui].uiIndex = (*tppSTORE)->psRec[i].uiIndex; tpSTORE->psRec[tui].uiDataSize = (*tppSTORE)->psRec[i].uiDataSize; tpSTORE->psRec[tui].pData = (*tppSTORE)->psRec[i].pData; tui++; }
free(*tppSTORE);
*tppSTORE = tpSTORE;
#ifdef _AE_STORE_DEBUG printf("store_del_by_order: OK\n"); #endif }
ui store_find_by_index(pSTORE tpSTORE, ui uiIndex) { #ifdef _AE_STORE_DEBUG printf("store_find_by_index: start\n"); #endif
if (tpSTORE == 0) return 0;
if (tpSTORE->ERR & AE_STORE_ERR_ANDMASK) return 0;
if (tpSTORE->uiFreeIndex <= uiIndex) { tpSTORE->ERR = AE_STORE_WARN_INDEX; return 0; }
for (ui i = 0; i < tpSTORE->uiFreeOrder; i++) { if (tpSTORE->psRec[i].uiIndex == uiIndex) { #ifdef _AE_STORE_DEBUG printf("store_find_by_index: OK\n"); #endif
tpSTORE->ERR = AE_STORE_OK; return i; } }
#ifdef _AE_STORE_DEBUG printf("store_find_by_index: INDEX NOT FOUND\n"); #endif
tpSTORE->ERR = AE_STORE_WARN_FIND; return 0; }
ui store_get_err(pSTORE tpSTORE) { #ifdef _AE_STORE_DEBUG printf("store_get_err: start\n"); #endif
if (tpSTORE == 0) return 1;
#ifdef _AE_STORE_DEBUG printf("store_get_err: OK\n"); #endif
if (tpSTORE->ERR & AE_STORE_ERR_ANDMASK) return 1; else return 0; }
AE_STORE_ERR store_get_error(pSTORE tpSTORE) { #ifdef _AE_STORE_DEBUG printf("store_get_error: start\n"); #endif
if (tpSTORE == 0) return AE_STORE_MEM;
#ifdef _AE_STORE_DEBUG printf("store_get_error: OK\n"); #endif
return tpSTORE->ERR; }
ui store_get_records(pSTORE tpSTORE) { #ifdef _AE_STORE_DEBUG printf("store_get_records: start\n"); #endif
if (tpSTORE == 0) return 0;
if (tpSTORE->ERR & AE_STORE_ERR_ANDMASK) return 0;
#ifdef _AE_STORE_DEBUG printf("store_get_records: OK\n"); #endif
return tpSTORE->uiFreeOrder; }
ui store_get_warn(pSTORE tpSTORE) { #ifdef _AE_STORE_DEBUG printf("store_get_warn: start\n"); #endif
if (tpSTORE == 0) return 1;
#ifdef _AE_STORE_DEBUG printf("store_get_warn: OK\n"); #endif
if (tpSTORE->ERR & AE_STORE_WARN_ANDMASK) { tpSTORE->ERR = AE_STORE_OK; return 1; } else return 0; }
ui store_is_ok(pSTORE tpSTORE) { #ifdef _AE_STORE_DEBUG printf("store_is_ok: start\n"); #endif
if (tpSTORE == 0) return 1;
#ifdef _AE_STORE_DEBUG printf("store_is_ok: OK\n"); #endif
if (tpSTORE->ERR) return 1;
return 0; }
void store_print(pSTORE tpSTORE) { #ifdef _AE_STORE_DEBUG printf("store_print: start\n"); #endif
if (tpSTORE == 0) return;
printf(" *** STORE INFO ***\n"); printf(" Address : %u\n", (ui)tpSTORE); printf(" ERR : %u\n", (ui)tpSTORE->ERR);
switch (tpSTORE->ERR) { case AE_STORE_OK: printf("<< AE_STORE_OK >>\n"); break;
case AE_STORE_MEM: printf("<< AE_STORE_MEM >>\n"); return;
case AE_STORE_WARN_INPUT: printf("<< AE_STORE_WARN_INPUT >>\n"); break;
case AE_STORE_WARN_MAX: printf("<< AE_STORE_WARN_MAX >>\n"); break;
case AE_STORE_WARN_INDEX: printf("<< AE_STORE_WARN_INDEX >>\n"); break;
case AE_STORE_WARN_FIND: printf("<< AE_STORE_WARN_FIND >>\n"); break;
default: printf("<< SOME ERROR >>\n"); }
printf(" uiStoreSize : %u\n", tpSTORE->uiStoreSize); printf(" uiFreeIndex : %u\n", tpSTORE->uiFreeIndex); printf(" uiFreeOrder : %u\n", tpSTORE->uiFreeOrder);
if (tpSTORE->uiFreeOrder == 0) { printf(" *** NO DATA ***\n *** END ***\n\n"); return; }
printf(" *** DATA ***\n");
for (ui i = 0; i < tpSTORE->uiFreeOrder; i++) { printf(" Record <Order> : %u\n", i); printf(" Record <Index> : %u\n", tpSTORE->psRec[i].uiIndex); printf(" uiDataSize : %u\n", tpSTORE->psRec[i].uiDataSize); printf(" Data address : %u\n", tpSTORE->psRec[i].pData); printf(" STRING >>>\n");
char* tpc = (char*)tpSTORE->psRec[i].pData; ui k = 0;
for (ui j = 0; j < tpSTORE->psRec[i].uiDataSize; j++) { if (*tpc == 0x0A) printf("%3i -X- ", *tpc); else printf("%3i -%c- ", *tpc, *tpc);
tpc++; k++; if (k == 8) { printf("\n"); k = 0; } }
if (k != 0) printf("\n"); }
printf(" *** END ***\n\n");
#ifdef _AE_STORE_DEBUG printf("store_print: OK\n"); #endif }
ui store_size_by_order(pSTORE tpSTORE, ui uiOrder) { #ifdef _AE_STORE_DEBUG printf("store_size_by_order: start\n"); #endif
if (tpSTORE == 0) return 0;
if (tpSTORE->ERR & AE_STORE_ERR_ANDMASK) return 0;
if (tpSTORE->uiFreeOrder <= uiOrder) { tpSTORE->ERR = AE_STORE_WARN_INPUT; return 0; }
#ifdef _AE_STORE_DEBUG printf("store_size_by_order: OK\n"); #endif
return tpSTORE->psRec[uiOrder].uiDataSize; } Так лучше?
|
|
« Последнее редактирование: 23-01-2017 15:23 от Aether »
|
Записан
|
|
|
|
RXL
|
|
« Ответ #15 : 23-01-2017 16:33 » |
|
Намного лучше! Не идеал, но не сравнить с тем, что было. Когда ежедневно читаешь кучу чужого кода, становишься очень требовательным к его читаемости. Алокатор — код, который легко может создать массу проблем. Писать его надо идеально. #ifdef _AE_STORE_DEBUG printf("store_create: start\n"); #endif Макрос для gcc (для VC тоже что-то подобное может быть): #ifdef _AE_STORE_DEBUG #define debug_printf(fmt, ...) warn(fmt, ##__VA_ARGS__) #else /* _AE_STORE_DEBUG */ #define debug_printf(fmt, ...) #endif /* _AE_STORE_DEBUG */ В итоге код выглядит понятно: debug_printf("store_create: start\n"); По алокаторам рекомендую (где-то с середины): https://habrahabr.ru/company/oleg-bunin/blog/310560/
Позволил себе переформатировать и поправить пару совсем кричащих мест: #include "AE_STORE.h"
#ifdef _AE_STORE_DEBUG #define debug_printf(fmt, ...) warn(fmt, ##__VA_ARGS__) #else /* _AE_STORE_DEBUG */ #define debug_printf(fmt, ...) #endif /* _AE_STORE_DEBUG */
pSTORE store_create(void) { debug_printf("store_create: start\n"); pSTORE tpSTORE = malloc(sizeof(STORE));
if (tpSTORE == 0) return 0;
tpSTORE->ERR = AE_STORE_OK; tpSTORE->uiStoreSize = sizeof(STORE); tpSTORE->uiFreeIndex = 0; tpSTORE->uiFreeOrder = 0; debug_printf("store_create: OK\n");
return tpSTORE; }
void store_destroy(ppSTORE tppSTORE) { debug_printf("store_destroy: start\n");
if ((*tppSTORE) == 0) return;
if ((*tppSTORE)->ERR & AE_STORE_ERR_ANDMASK) return;
for (ui i = 0; i < (*tppSTORE)->uiFreeOrder; i++) { free((*tppSTORE)->psRec[i].pData); }
free(*tppSTORE); *tppSTORE = 0; debug_printf("store_destroy: OK\n"); }
ui store_add (ppSTORE tppSTORE, pV pData, ui uiDataSize) { debug_printf("store_add: start\n");
if (((*tppSTORE) == 0) || (pData == 0) || (uiDataSize == 0)) { (*tppSTORE)->ERR = AE_STORE_WARN_INPUT; return 0; }
if ((*tppSTORE)->ERR & AE_STORE_ERR_ANDMASK) return 0;
if (((*tppSTORE)->uiFreeIndex == AE_STORE_LAST_UI) || ((*tppSTORE)->uiFreeOrder == AE_STORE_LAST_UI)) { (*tppSTORE)->ERR = AE_STORE_WARN_MAX; return 0; }
void* tpData = malloc(uiDataSize);
if (tpData == 0) { (*tppSTORE)->ERR = AE_STORE_MEM; return 0; }
memcpy(tpData, pData, uiDataSize);
if ((*tppSTORE)->uiStoreSize > (AE_STORE_LAST_UI - sizeof(REC))) { (*tppSTORE)->ERR = AE_STORE_MEM; free(tpData); return 0; }
ui uiNewStoreSize = (*tppSTORE)->uiStoreSize + sizeof(REC); pSTORE tpSTORE = malloc(uiNewStoreSize);
if (tpSTORE == 0) { (*tppSTORE)->ERR = AE_STORE_MEM; free(tpData); return 0; }
memcpy(tpSTORE, (*tppSTORE), (*tppSTORE)->uiStoreSize); tpSTORE->uiStoreSize = uiNewStoreSize; ui uiCurIndex = tpSTORE->psRec[tpSTORE->uiFreeOrder].uiIndex = tpSTORE->uiFreeIndex++; tpSTORE->psRec[tpSTORE->uiFreeOrder].uiDataSize = uiDataSize; tpSTORE->psRec[tpSTORE->uiFreeOrder].pData = tpData; tpSTORE->uiFreeOrder++; free(*tppSTORE); *tppSTORE = tpSTORE; debug_printf("store_add: OK\n");
return uiCurIndex; }
void store_change_order (pSTORE tpSTORE, ui uiDstOrder, ui uiSrcOrder) { debug_printf("store_change_order: start\n");
if (tpSTORE == 0) return;
if (tpSTORE->ERR & AE_STORE_ERR_ANDMASK) return;
if ((tpSTORE->uiFreeOrder <= uiDstOrder) || (tpSTORE->uiFreeOrder <= uiSrcOrder)) { tpSTORE->ERR = AE_STORE_WARN_INPUT; return; }
REC ts; ts.uiIndex = tpSTORE->psRec[uiDstOrder].uiIndex; tpSTORE->psRec[uiDstOrder].uiIndex = tpSTORE->psRec[uiSrcOrder].uiIndex; tpSTORE->psRec[uiSrcOrder].uiIndex = ts.uiIndex; ts.uiDataSize = tpSTORE->psRec[uiDstOrder].uiDataSize; tpSTORE->psRec[uiDstOrder].uiDataSize = tpSTORE->psRec[uiSrcOrder].uiDataSize; tpSTORE->psRec[uiSrcOrder].uiDataSize = ts.uiDataSize; ts.pData = tpSTORE->psRec[uiDstOrder].pData; tpSTORE->psRec[uiDstOrder].pData = tpSTORE->psRec[uiSrcOrder].pData; tpSTORE->psRec[uiSrcOrder].pData = ts.pData; debug_printf("store_change_order: OK\n"); }
pV store_data_by_index (pSTORE tpSTORE, ui uiIndex) { debug_printf("store_data_by_index: start\n");
if (tpSTORE == 0) return (pV)0;
if (tpSTORE->ERR & AE_STORE_ERR_ANDMASK) return (pV)0;
ui tuiOrder = store_find_by_index(tpSTORE, uiIndex);
if (tpSTORE->ERR) return (pV)0;
debug_printf("store_data_by_index: OK\n"); return tpSTORE->psRec[tuiOrder].pData; }
pV store_data_by_order (pSTORE tpSTORE, ui uiOrder) { debug_printf("store_data_by_order: start\n");
if (tpSTORE == 0) return (pV)0;
if (tpSTORE->ERR & AE_STORE_ERR_ANDMASK) return (pV)0;
if (tpSTORE->uiFreeOrder <= uiOrder) { tpSTORE->ERR = AE_STORE_WARN_INPUT; return (pV)0; }
debug_printf("store_data_by_order: OK\n"); return tpSTORE->psRec[uiOrder].pData; }
void store_del_by_index (ppSTORE tppSTORE, ui uiIndex) { debug_printf("store_del_by_index: start\n");
if (*tppSTORE == 0) return;
if ((*tppSTORE)->ERR & AE_STORE_ERR_ANDMASK) return;
ui tuiOrder = store_find_by_index(*tppSTORE, uiIndex);
if ((*tppSTORE)->ERR) return;
store_del_by_order(tppSTORE, tuiOrder); debug_printf("store_del_by_index: OK\n"); }
void store_del_by_order (ppSTORE tppSTORE, ui uiOrder) { debug_printf("store_del_by_order: start\n");
if (*tppSTORE == 0) return;
if ((*tppSTORE)->ERR & AE_STORE_ERR_ANDMASK) return;
if ((*tppSTORE)->uiFreeOrder <= uiOrder) { (*tppSTORE)->ERR = AE_STORE_WARN_INPUT; return; }
free((*tppSTORE)->psRec[uiOrder].pData); ui uiNewStoreSize = (*tppSTORE)->uiStoreSize - sizeof(REC); pSTORE tpSTORE = malloc(uiNewStoreSize);
if (tpSTORE == 0) { (*tppSTORE)->ERR = AE_STORE_MEM; return; }
tpSTORE->ERR = AE_STORE_OK; tpSTORE->uiStoreSize = uiNewStoreSize; tpSTORE->uiFreeIndex = (*tppSTORE)->uiFreeIndex; tpSTORE->uiFreeOrder = (*tppSTORE)->uiFreeOrder - 1; ui tui = 0;
for (ui i = 0; i < (*tppSTORE)->uiFreeOrder; i++) { if (i == uiOrder) continue;
tpSTORE->psRec[tui].uiIndex = (*tppSTORE)->psRec[i].uiIndex; tpSTORE->psRec[tui].uiDataSize = (*tppSTORE)->psRec[i].uiDataSize; tpSTORE->psRec[tui].pData = (*tppSTORE)->psRec[i].pData; tui++; }
free(*tppSTORE); *tppSTORE = tpSTORE; debug_printf("store_del_by_order: OK\n"); }
ui store_find_by_index (pSTORE tpSTORE, ui uiIndex) { debug_printf("store_find_by_index: start\n");
if (tpSTORE == 0) return 0;
if (tpSTORE->ERR & AE_STORE_ERR_ANDMASK) return 0;
if (tpSTORE->uiFreeIndex <= uiIndex) { tpSTORE->ERR = AE_STORE_WARN_INDEX; return 0; }
for (ui i = 0; i < tpSTORE->uiFreeOrder; i++) { if (tpSTORE->psRec[i].uiIndex == uiIndex) { debug_printf("store_find_by_index: OK\n"); tpSTORE->ERR = AE_STORE_OK; return i; } }
debug_printf("store_find_by_index: INDEX NOT FOUND\n"); tpSTORE->ERR = AE_STORE_WARN_FIND; return 0; }
ui store_get_err (pSTORE tpSTORE) { debug_printf("store_get_err: start\n");
if (tpSTORE == 0) return 1;
debug_printf("store_get_err: OK\n");
return tpSTORE->ERR & AE_STORE_ERR_ANDMASK ? 1 : 0; }
AE_STORE_ERR store_get_error (pSTORE tpSTORE) { debug_printf("store_get_error: start\n");
if (tpSTORE == 0) return AE_STORE_MEM;
debug_printf("store_get_error: OK\n"); return tpSTORE->ERR; }
ui store_get_records (pSTORE tpSTORE) { debug_printf("store_get_records: start\n");
if (tpSTORE == 0) return 0;
if (tpSTORE->ERR & AE_STORE_ERR_ANDMASK) return 0;
debug_printf("store_get_records: OK\n"); return tpSTORE->uiFreeOrder; }
ui store_get_warn (pSTORE tpSTORE) { debug_printf("store_get_warn: start\n");
if (tpSTORE == 0) return 1; debug_printf("store_get_warn: OK\n");
if (tpSTORE->ERR & AE_STORE_WARN_ANDMASK) { tpSTORE->ERR = AE_STORE_OK; return 1; } else { return 0; } }
ui store_is_ok (pSTORE tpSTORE) { debug_printf("store_is_ok: start\n");
if (tpSTORE == 0) return 1;
debug_printf("store_is_ok: OK\n");
return tpSTORE->ERR ? 1 : 0; }
void store_print (pSTORE tpSTORE) { debug_printf("store_print: start\n");
if (tpSTORE == 0) return;
printf(" *** STORE INFO ***\n"); printf(" Address : %u\n", (ui)tpSTORE); printf(" ERR : %u\n", (ui)tpSTORE->ERR);
switch (tpSTORE->ERR) { case AE_STORE_OK: printf("<< AE_STORE_OK >>\n"); break; case AE_STORE_MEM: printf("<< AE_STORE_MEM >>\n"); return; case AE_STORE_WARN_INPUT: printf("<< AE_STORE_WARN_INPUT >>\n"); break; case AE_STORE_WARN_MAX: printf("<< AE_STORE_WARN_MAX >>\n"); break; case AE_STORE_WARN_INDEX: printf("<< AE_STORE_WARN_INDEX >>\n"); break; case AE_STORE_WARN_FIND: printf("<< AE_STORE_WARN_FIND >>\n"); break; default: printf("<< SOME ERROR >>\n"); }
printf(" uiStoreSize : %u\n", tpSTORE->uiStoreSize); printf(" uiFreeIndex : %u\n", tpSTORE->uiFreeIndex); printf(" uiFreeOrder : %u\n", tpSTORE->uiFreeOrder);
if (tpSTORE->uiFreeOrder == 0) { printf(" *** NO DATA ***\n *** END ***\n\n"); return; }
printf(" *** DATA ***\n");
for (ui i = 0; i < tpSTORE->uiFreeOrder; i++) { printf(" Record <Order> : %u\n", i); printf(" Record <Index> : %u\n", tpSTORE->psRec[i].uiIndex); printf(" uiDataSize : %u\n", tpSTORE->psRec[i].uiDataSize); printf(" Data address : %u\n", tpSTORE->psRec[i].pData); printf(" STRING >>>\n");
char* tpc = (char*)tpSTORE->psRec[i].pData; ui k = 0;
for (ui j = 0; j < tpSTORE->psRec[i].uiDataSize; j++) { if (*tpc == 0x0A) { printf("%3i -X- ", *tpc); } else { printf("%3i -%c- ", *tpc, *tpc); }
tpc++; k++;
if (k == 8) { printf("\n"); k = 0; } }
if (k != 0) printf("\n"); }
printf(" *** END ***\n\n"); debug_printf("store_print: OK\n"); }
ui store_size_by_order (pSTORE tpSTORE, ui uiOrder) { debug_printf("store_size_by_order: start\n");
if (tpSTORE == 0) return 0;
if (tpSTORE->ERR & AE_STORE_ERR_ANDMASK) return 0;
if (tpSTORE->uiFreeOrder <= uiOrder) { tpSTORE->ERR = AE_STORE_WARN_INPUT; return 0; }
debug_printf("store_size_by_order: OK\n"); return tpSTORE->psRec[uiOrder].uiDataSize; } Чуть-чуть критики: 1. tpSTORE — очень плохо. tpStore — намного лучше. store — идеально. pStore — допустимо, если в коде функции есть Store store и Store* pStore. M$-style с указанием типа в имени — нехорошо. 2. pSTORE — туда же. Store* — намного лучше (С++ стиль). Или store_t* — стиль стандартной библиотеки Си. Есть четкие стили выбора имен идентификаторов для Си и С++. Есть из чего выбрать и не надо ничего придумывать. Создавать типы для указателей не гуд: больше путают, чем упрощают код. 3. ui — типы тоже должны быть понятны постороннему человеку. Есть полно стандартных типов. Для длины, например, size_t. И не встанет вопрос, какого типа аргументы и как их сравнивать. И тем более не нужно будет писать мусорные префиксы. Собственные типы должны быть осмысленными. Пример: typedef uint8_t LocaleId; typedef uint32_t LocationId; typedef uint32_t InfoId; typedef uint8_t ContinentId; typedef uint16_t CountryId; typedef uint16_t RegionId; typedef uint32_t CityId; Теперь LocationId говорит куда больше, чем ui или uint32_t. Функцию store_print можно было бы совсем исключить (здесь нигде не используется и для примера ну нужна) или передвинуть в самый конец. Заголовок не лучше. Все внимание уделено комментариям, когда как должно быть уделено коду. Не понимаю я стремления сделать все "в колоночку" и плевать на читаемость главного - кода. При написании такого кода стоит задуматься о еще о целевой платформе. Если это 32-битная, стоит подумать, о совместимости с 64-битной или хотя бы о простой миграции. И обязательно тесты. Вернусь к твоему посту: лучше сперва объяснить свою идею на словах. Для чего нужны свои алокаторы? Возможные причины: 1. снизить затраты на обслуживание памяти при выделении и освобождении; 2. снизить потери памяти на выравнивание и сегментацию. А какую цель ты преследовал этот код? И на всякий случай: я ни в коем случае не наезжаю, пишу только по коду.
|
|
« Последнее редактирование: 23-01-2017 19:50 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
darkelf
Молодой специалист
Offline
|
|
« Ответ #16 : 23-01-2017 17:51 » |
|
Aether, посмотрел немного Ваш код - мне кажется в функции ui store_add() вещи типа ui uiNewStoreSize = (*tppSTORE)->uiStoreSize + sizeof(REC);
pSTORE tpSTORE = malloc(uiNewStoreSize);
if (tpSTORE == 0) { (*tppSTORE)->ERR = AE_STORE_MEM; free(tpData); return 0; }
memcpy(tpSTORE, (*tppSTORE), (*tppSTORE)->uiStoreSize);
tpSTORE->uiStoreSize = uiNewStoreSize; ui uiCurIndex = tpSTORE->psRec[tpSTORE->uiFreeOrder].uiIndex = tpSTORE->uiFreeIndex++; tpSTORE->psRec[tpSTORE->uiFreeOrder].uiDataSize = uiDataSize; tpSTORE->psRec[tpSTORE->uiFreeOrder].pData = tpData; tpSTORE->uiFreeOrder++; не самый лучший выбор, т.к., как мне кажется, при увеличении числа элементов psRec в struct _AE_STORE время добавления будет расти очень сильно. Можно, например, при добавлении одного реально память выделять сразу для 10, или 100, соответственно глобальная перепись Вашего массива psRec будет происходить раз в 10 или 100 добавлений, а не при каждом, аналогично уменьшится число вызовов выделения памяти, которые тоже очень даже могут быть не дешевыми. Плюс при добавлении Вам необходимо памяти почти в два раза больше. Можно попробовать использовать realoc(), правда на быстродействии и потреблении памяти в пиках оно не сильно скажется. Я бы всё-таки почитал про SLAB-аллокаторы - нечто похожее реализовывал следующим образом - у меня был описатель области, содержащей карту занятых слотов области и соответственно сами слоты. При необходимости выделения памяти я смотрел - есть ли в области свободные слоты, если есть - просто в карте отмечал их занятость и отдавал на слот указатель вызывающей программе. Если нет свободных слотов - выделялась новая область, со всеми свободными слотами, а далее алгоритм брал первый слот из области. Чуть позже я сделал оптимизацию - в общем описателе, в котором содержался список областей, сделал отдельно двусвязный список из областей со свободными слотами. Таким образом не надо было просматривать все области на предмет нахождения таковой со свободными слотами. Освобождение памяти реализовывалось поиском области, которой принадлежит слот и отметкой в битовой карте, что слот уже свободен.
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #17 : 23-01-2017 17:55 » |
|
Я бы всё-таки почитал про SLAB-аллокаторы Ссылка в мое посте выше. Там же описаны алокаторы для структур одного размера.
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Джон
просто
Администратор
Offline
Пол:
|
|
« Ответ #18 : 23-01-2017 21:03 » |
|
Нууу... тогда зачем показывать свой личный код другим?
а) Естественно, чтобы кто-то его почитал, но проблемы решаются по мере их поступления, и по мере желания. б) Иногда читая чужое, появляются новые мысли. Мало ли кому пригодится? в) В рамках темы для возможно лучшего представления. Хотя я бы лучше диаграмму нарисовал, но не вижу встречного интереса, поэтому не вижу целесообразности затрат. Ну тогда просто НЕОБХОДИМО позаботиться о том, чтобы ДРУГИЕ тоже могли прочитать код. Ну я не знаю, элементарно показать уважение к оппонентам. Плохо, или ваще, не форматированный код, в моём представлении, сравним с текстом на грязном помятом клочке туалетной газетки. Кто захочет такой текст читать? А уж разбираться в нём...
|
|
|
Записан
|
Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома. "Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash "Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman "All science is either physics or stamp collecting." Ernest Rutherford "Wer will, findet Wege, wer nicht will, findet Gründe."
|
|
|
Aether
|
|
« Ответ #19 : 23-01-2017 21:21 » |
|
Критику постараюсь впредь учесть, сложившийся подход выработался эволюционно, например, выравнивание в колонку удобно, когда весь код чёрно-белый, префиксы типа - наследие NASM, а не M$, когда забываешь где-то в середине о чём идёт речь. Вернусь к твоему посту: лучше сперва объяснить свою идею на словах. Для чего нужны свои алокаторы? Возможные причины: 1. снизить затраты на обслуживание памяти при выделении и освобождении; 2. снизить потери памяти на выравнивание и сегментацию. А какую цель ты преследовал этот код?
В обратном порядке: 1. Приведённый код показывает пример простейшего контейнера в рамках "С", в котором присутствует минимальный набор требуемого функционала. Я привёл его, как точку отсчёта. 2. Для меня главная задача - снизить трудоёмкость в решении типовых задач. Например: вот сейчас мне нужно моделировать поверхность, которая образуется, как результат интерполяции горизонталей(сплайнов по системе Безье). Так вот, как известно сплайн - это некий набор проходных и ориентирующих точек произвольной численности. То есть, сам сплайн - это контейнер точек. Реализовывать сплайн на базе malloc/free - увеличивать трудоёмкость, снижать надёжность. В то же время сплайны входят с слой - контейнер сплайнов. Слой входит в макет - контейнер слоёв. Таким образом, требуется модель единого качественного абстрактного контейнера - менеджера памяти. Да, можно поговорить о том, что есть "С++", у него есть стандартные шаблоны контейнеров, каких угодно. Тем не менее, я бы не хотел расширять дискуссию по вопросу: почему именно "С"? Может неверный инструмент? Так как мы погрузимся в перетягивание каната, которое будет непродуктивно. 3. Итак, как уже выделил darkelf, есть места, которые не просто не оптимальны, а преступны. Когда дело касается 1-100 кривых по 10-100 точек, всё работает хорошо и незаметно на современной машине, однако, стоит перейти на вычисление больших масштабов, и затраты на обслуживание постоянно копируемых элементов стремительно растут, становясь ограничивающим скорость барьером. Поэтому, для будущих проектов, хотелось бы создать инструмент, а не одноразовую библиотеку. Инструмент должен попутно предусмотреть меры по снижению фрагментации, и обеспечивать высокое быстродействие. 4. 64-битные вычисления - настоящая реальность, действительно, хотелось бы ориентироваться именно на это. 1. Можно, например, при добавлении одного реально память выделять сразу для 10, или 100, соответственно глобальная перепись Вашего массива psRec будет происходить раз в 10 или 100 добавлений, а не при каждом, аналогично уменьшится число вызовов выделения памяти, которые тоже очень даже могут быть не дешевыми. Плюс при добавлении Вам необходимо памяти почти в два раза больше.
2. Можно попробовать использовать realoc(), правда на быстродействии и потреблении памяти в пиках оно не сильно скажется.
3. Я бы всё-таки почитал про SLAB-аллокаторы - нечто похожее реализовывал следующим образом - у меня был описатель области, содержащей карту занятых слотов области и соответственно сами слоты.
4. При необходимости выделения памяти я смотрел - есть ли в области свободные слоты, если есть - просто в карте отмечал их занятость и отдавал на слот указатель вызывающей программе. Если нет свободных слотов - выделялась новая область, со всеми свободными слотами, а далее алгоритм брал первый слот из области.
5. Чуть позже я сделал оптимизацию - в общем описателе, в котором содержался список областей, сделал отдельно двусвязный список из областей со свободными слотами. Таким образом не надо было просматривать все области на предмет нахождения таковой со свободными слотами.
1. Совершенно верно, идеально совершать минимальное число перезаписей, и ни в коем случае не дублировать память. Строго говоря, память, как я понимаю, выделяется страницами, поэтому некий хвост будет оставаться в большинстве ситуаций. Однако, стоит отметить, что поле описателей - растущий элемент, и поле данных - растущий элемент. С данными проще, а вот описатели - непрерывный массив, и стратегия работы у них получается различная. 2. С realloc в одной из версий tcc проявлялась нестабильность, поэтому, прочитав в своё время комментарии, стал избегать. Вообще-то, скорость должна чуть-чуть возрасти, так как хвост он должен учитывать, но в рамках общих положений кода прирост будет незначителен. 3. Почитал, но в общих чертах. 4. Таким образом улучшалось положение с фрагментацией? А слоты были одного размера или пропорциональны степени двойки? 5. Тут есть такая проблема, что чем более глубокий контейнер по вложенности, тем принципиально большее число переходов происходит в ходе запроса. И как без единого менеджера памяти следить: кому какая карта принадлежит? В идеале, для пользователя, должна быть доступна некая библиотека, подключая которую он получает простой интерфейс, который абстрактен, и не требует от него знания о своём внутреннем устройстве. Использование malloc/free прекращается, и всё происходит на базе нового решения. К слову, приведённый код избыточен: в качестве индекса может служить сам адрес - то даже лучше, а функциям лучше работать с промежуточными структурами, то есть, например, при запросе: найди на складе 2 запись по ордеру 4 ответ: <код ошибки> - успешно, <размер записи> - 24 байта, <адрес начала> - ... Если ещё и реализовать это, как __fastcall, то было бы замечательно, ведь стек - тоже в некотором смысле зло. Да и часть методов была бы не нужна. Я думаю, одним постом обо всём не получится, в завершении, есть ещё тема про упаковку маленьких записей, я уже говорил про различные размеры логических блоков. RXL, видео посмотрел, довольно познавательно, спасибо. В принципе, локальная СУБД на базе ОЗУ - это более тяжёлый продукт, но суть идей послушать интересно. Также было бы неплохо это посмотреть человеку из соседней темы, который интересовался БД большого объёма, и архитектурой таких решений.
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #20 : 23-01-2017 22:55 » |
|
Aether, я видео не смотрел, текст повторяет слова и более компактен. В нем есть ссылка к коду (Си), его можно посмотреть и использовать. Суть там в многоуровневых алокаторах. Верхние уровни представлены slab-алокаторами (slab — плитка). Они выделяют память только блоками размером степени двойки. Базовый алокатор slab_arena и только он общается с системой, получая у нее блоки по 4 МБ. Алокаторы следующего уровня запрашивают память у него и только целыми блоками. Алокатор slab_cache последовательно делит блоки по палам, позволяя алоцировать блоки от 2 МБ до 4 кБ. Алокатор mempool позволяет создавать пул объектов одного размера, память получает у slab_cache. Следующий — small — использует наборы пулов разного размера для обслуживания запросов на произвольный размер, не более максимального пула. Они предлагают готовые универсальные решения с высокой производительностью.
Если у тебя структура имеет фиксированный размер, подойдут решения типа mempool.
Разница между индексом и указателем чувствительна при переходе на 64-битный код. Если для индекса может хватить 16 или 32 бита, то указатель всегда 64.
За принадлежностью памяти следить не нужно. Это функционал отладочный, но никак не релизный. Если хранить по каждому кусочку памяти инфу, кто его занял, оверхед будет дикий, и по памяти, и по производительности. Да и что в этом толку, если выделение чаще происходит в функциях типа some_create и some_add, а логика освобождения уровнем выше. Поиск утечек (для отладки) можно делать подсчетом числа выделений и освобождений для приложения, библиотеки или модуля. Для сужения поиска ошибки в коде. Но некоторые вещи даже так не найти. Ошибка преждевременного освобождения памяти с последующей повторной попыткой освобождения, скажем, при использовании контроля за удалением по счетчику ссылок. По этому алокатор должен быть идеальным кодом.
|
|
« Последнее редактирование: 23-01-2017 23:11 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
darkelf
Молодой специалист
Offline
|
|
« Ответ #21 : 24-01-2017 07:55 » |
|
4. 64-битные вычисления - настоящая реальность, действительно, хотелось бы ориентироваться именно на это.
Тогда, как посоветовал RXL желательно использовать для размеров и индексов типы а-ля size_t и uintptr_t, а не unsigned int, т.к. в 32-х разрядной системе их размер совпадает, а в 64-х разрядной уже нет. Кроме того могут быть ньюансы даже в пределах разных 64-х разрядных ОС (Windows/Linux - IPL64 - LP64). 1. Совершенно верно, идеально совершать минимальное число перезаписей, и ни в коем случае не дублировать память. Строго говоря, память, как я понимаю, выделяется страницами, поэтому некий хвост будет оставаться в большинстве ситуаций. Однако, стоит отметить, что поле описателей - растущий элемент, и поле данных - растущий элемент. С данными проще, а вот описатели - непрерывный массив, и стратегия работы у них получается различная.
Не обязательно страницами, всё зависит от ОС и системной библиотеки. Изначально в UNIX был такой системный вызов sbrk() который просто изменял границу "кучи" и принимал в качестве параметров размер в байтах. При помощи этого вызова и были реализованы malloc()/free(). По мере развития, как я понимаю, стали активно переходить на mmap() - отображение страницы памяти в адресное пространство процесса, которое могло отображать области памяти кратные размеру страницы процессорной архитектуры. Если не путаю, в Linux при определённых размерах выделяемой памяти malloc() на самом деле вызывает mmap(), т.е. , а не пытается бегать изменять размер "кучи". 2. С realloc в одной из версий tcc проявлялась нестабильность, поэтому, прочитав в своё время комментарии, стал избегать. Вообще-то, скорость должна чуть-чуть возрасти, так как хвост он должен учитывать, но в рамках общих положений кода прирост будет незначителен.
Нечто подобное было и в Windows 2000/XP, в msvc 6.0, которым приходится активно пользоваться. Была непонятная проблема - если постоянно вызывать realloc() с увеличивающимся на 1 байт размером (таким образом "в лоб" были реализованы "динамические" строки на C), в определённый момент что-то там ломалось, и программа падала. Было найдено решение, обходящее данную проблему, а заодно и увеличивающее скорость работы таких "динамических" строк - мы стали увеличивать не по байту, а сразу на определённое значение 32 или 64 байта. 4. Таким образом улучшалось положение с фрагментацией? А слоты были одного размера или пропорциональны степени двойки?
Да, слоты все были одинакового размера. В нашем случае SLAB мог иметь внутреннюю фрагментацию, но не большую, особенно после указанной ранее оптимизации все области были задействованы максимально. Внешнюю фрагментацию можно было отключать (была опция отключающая удаление полностью пустых областей). У нас получилось такое смешение slab с mempool (т.е. внутри был SLAB, а предоставлялся интерфейс mempool). Интерфейс библиотеки, если интересно, имел следующий вид: #ifndef __SLAB_H #define __SLAB_H
#include <stddef.h>
#ifdef __cplusplus extern "C" { #endif typedef void* SLAB;
#define SLAB_VER "0.0.1.0"
#define SLAB_FLAG_DONTPACK 1 /*не удалять пустые области slab_entry при выполнении slab_free*/
/*создание slab-аллокатора ВХОД: num - число элементов в области - slab_entry (0 - выбирается автоматически) size - размер одного элемента flags - флаги создания аллокатора SLAB_FLAG_* ctx - контекст, передаваемый в конструктор/деструктор ctor - указатель на функцию конструктора dtor - указатель на функцию деструктора align - задание выравнивания адресов получаемых элементов, 0 - нет выравнивания ВЫХОД: !NULL - описатель slab-аллокатора, NULL - ошибка */ #define slab_new(n, s, f, ctx, ctor, dtor) slab_new_v2(n, s, f, ctx, ctor, dtor, 0) SLAB slab_new_v2(unsigned int num, size_t size, unsigned int flags, void* ctx, void (*ctor)(void* p, void* ctx), void (*dtor)(void* p, void* ctx), size_t align);
/*выделение памяти через slab-аллокатор ВХОД: s - описатель slab-аллокатора ВЫХОД: !NULL - выделенная память, NULL - ошибка выделения */ void* slab_alloc(SLAB s);
/*освобождение памяти через slab-аллокатор ВХОД: s - описатель slab-аллокатора p - описатель памяти раннее выделенной через slab-аллокатор ВЫХОД: 0 - нормальное завершение, память освобождена, -1 - ошибка в параметрах вызова, -2 - память по указателю p уже освобождена */ int slab_free(SLAB s, void* p);
/*освобождение памяти занимаемой slab-аллокатором ВХОД: s - описатель slab-аллокатора ВЫХОД: нет */ void slab_destroy(SLAB s);
/*получение i-го элемента из slab, функция предоставляет возможность рассматривать SLAB как массив переменной длины ВХОД: s - описатель slab-аллокатора idx - индекс элемента в массиве ВЫХОД: !NULL - указатель на элемента, NULL - ошибка в параметрах */ void* slab_at(SLAB s, size_t idx);
/*статистика slab-аллокатора*/ struct slab_stat { size_t ss_slabs_total; /*число областей*/ size_t ss_slabs_free; /*число областей в которых есть свободные элементы*/
size_t ss_elems_total; /*всего элементов в областях*/ size_t ss_elems_free; /*число свободных элементов в областях*/
size_t ss_sysmem_used; /*объём памяти запрошенной у системы в байтах*/ };
/*получение статистики аллокатора ВХОД: s - описатель slab-аллокатора st - заполняемая структура статистики ВЫХОД: нет */ void slab_stat(SLAB s, struct slab_stat* st);
#ifdef __cplusplus } #endif #endif /*__SLAB_H*/ 5. Тут есть такая проблема, что чем более глубокий контейнер по вложенности, тем принципиально большее число переходов происходит в ходе запроса. И как без единого менеджера памяти следить: кому какая карта принадлежит?
Аналогично RXL не совсем понял, зачем надо следить. У нас, например, для каждого типа структур создавался собственный SLAB-аллокатор. Т.е. сам тип структуры подразумевает использование аллокатора, выделяющего память размером с эту структуру. В идеале, для пользователя, должна быть доступна некая библиотека, подключая которую он получает простой интерфейс, который абстрактен, и не требует от него знания о своём внутреннем устройстве. Использование malloc/free прекращается, и всё происходит на базе нового решения.
У нас в проекте мы на SLAB перевели не всё, только те структуры, которых много (сотни тысяч, а то и миллионы). Для структур, число которых совсем не велико вполне себе можно использовать и обычные malloc()/free(). Можно даже в объектно-ориентированном виде обернуть их в свои функции. Т.е. если структур мало и они тривиальные, то там будет просто вызов malloc()/calloc(), если что-то более сложное - то там уже будут mempool-ы и прочие SLAB-ы. При этом при переводе некоторых структур на те-же SLAB-ы для остальной программы интерфейс меняться не будет, т.к. программа выделяет память ТОЛЬКО при помощи этих обёрток, а не напрямую через malloc()/calloc().
|
|
« Последнее редактирование: 24-01-2017 07:57 от darkelf »
|
Записан
|
|
|
|
RXL
|
|
« Ответ #22 : 24-01-2017 15:00 » |
|
У нас в проекте мы на SLAB перевели не всё, только те структуры, которых много (сотни тысяч, а то и миллионы). Для структур, число которых совсем не велико вполне себе можно использовать и обычные malloc()/free().
Поддерживаю! Универсальный и специализированный алокаторы решают разные задачи. И дело не только в алокаторах. Если есть оптимизированная версия какого-то интерфейса, не значит, что ее надо всегда использовать, а универсальный не использовать. Все должно быть логично, обосновано и соответствовать цели, а не просто пострижено и покрашено. Но при использовании нескольких интерфейсов надо следить, чтобы они не перемешались. Нельзя выделить память одним алокатором и освободить другим. Использование C++ может упростить эту задачу: каждый класс может определить свой new и delete, а шаблоны STL позволяют указать алокатор для каждого формируемого типа.
|
|
« Последнее редактирование: 24-01-2017 15:07 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Aether
|
|
« Ответ #23 : 24-01-2017 16:59 » |
|
Многое зависит от глубины проработки. Вот смотрите, ядро использует свои функции для распределения памяти, оно предоставляет интерфейс - системные вызовы для приложений - это уже уровень №2. Далее, эти вызовы mmap/munmap обёртываются в функции стандартной библиотеки malloc/free - уровень №3. Затем, мы строим наш менеджер надстройкой, получаем уровень №4.
Вот те ребята предлагают многоуровневую систему №5, №6, №7. В итоге, сам пользователь, будет оперировать слоями с восьмого. Так вот, каждый слой - это переходы, которые расходуют время. Это служебные данные, которые расходуют память. В итоге, получается сложная система, медленная и не такая уж компактная.
Вывод: главное цель. Мне понравился вопрос, который был в материале: - Вот, Вы, говорите, что на полезную работу современные СУБД тратят 12% своей скорости. А в чём эта полезная работа состоит? - Индексы, работа с ними, поиск. Можно было бы просто хранить файлы на диске...
Мало того, что они предлагают ещё: для производительности выполнять множество запросов, а потом записывать лог на диск, и потом уведомлять внешнего пользователя. То есть, они ориентированы на оптимизацию работы сервера, сделать его дешевле, а для клиента всё пройдёт незаметно - они не ощутят особой скорости и быстродействия. Следствием этого подхода и стали промежуточные уровни, полагаю, сделаны они для распределения задач, как между потоками одной машины, так и между машинами.
Моя цель другая: мне важна скорость вычислений. То есть, преобразование ордера в целевой адрес начала данных должно происходить с максимальной скоростью. При этом, выделение новых позиций не должно давать осадку в виде: удвоенного потребления памяти, и значительных потерь в скорости при выделении и освобождении. Пишу, возможно, не совсем грамотно, просто для меня полезная работа - это не индексирование, а расчёт. При рендеринге я могу произвольно обратиться к памяти тысяч раз за секунду, при этом любое лишнее обращение к служебным структурам, стеку при вызове функций - зло - эффект мультипликации, всё равно, что расчёт констант внутри цикла.
Таким образом, для исключения каких-либо задержек, потребуется работать с функциями ОС, поближе к архитектуре - заниматься запросом страниц и формировать своё пространство. Если в таком режиме задействовать другой менеджер, типа malloc/free или new/delete - он не будет знать что происходит, и вызовет конфликт.
В свете этого, разумным видится переопределение функции main, и блокировка стандартных менеджеров памяти заглушками в виде макроса. Одновременно, в main подключаемой библиотеки должна быть добавлена секция инициализации системы, которая одновременно избавит от необходимости использовать в функциях чрезмерное число аргументов. Например, функцию создания склада можно убрать вовсе, просто вызывая запись в новый склад будет сверка с глобальной для приложения таблицей складов. В случае успеха, вернётся подтверждение о записи.
По поводу индексов и адресов, да экономию байт можно сделать, реализуя 32-битное виртуальное пространство индексов, однако, в этом варианте добавятся дополнительные вычисления, дополнительное поле хранения индексов, и, что важно - ограничения и коды ошибок. Я думаю, индексы делать отдельными или хитрыми имеет смысл, когда необходимо сделать их уникальность между разными машинами, здесь же всё в пределах физически одной памяти.
Суть идеи SLAB, если правильно понял: 1) Предположим у нас есть объект Вася, который занимает 51 байт. 2) Определяем ближайшее вмещающее его число байт, являющееся степенью двойки. То есть - 64 байта. В странице таких помещается 4096 / 64 = 64. 3) Нам пока нужен один объект Вася, поэтому арендуем одну страницу целиком, с запасом на возможное число объектов, то есть используем 1 / 64. 4) Нам добавились ещё 5 объектов Вася. Не создаём страницу, а лишь заполняем резерв - 6 / 64. 5) Нам добавилось ещё 100 объектов Вася. Страницы не хватает, заказываем ещё одну - 106 / 128. 6) Предположим первые 70 записей освободились. Отдаём первую страницу системе - 36 / 64. То есть каждый такой SLAB - это склад объектов строго одного размера, выровненный так, чтобы преобразовывать ордер через таблицу описателей было просто и надёжно. Пока вызывают вопросы в организации самой таблицы описателей.
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #24 : 24-01-2017 18:09 » |
|
Многослойность не понижает производительность, а повышает! На каждом уровне находится оптимальный для своей задачи алокатор. Он не делает ни одно лишнего действия! Алокатор нижнего уровня обращается к верхнему только когда у него нет больше свободной памяти, а она выделяется большими блоками. Нагрузка верхних уровней амортизируется: на множество запросов к алокатору очень мало запросов алокатора выше. Большинство запросов будет обработано по схеме: взять свободный блок, пометить его как занятый и вернуть указатель. Что касается границы ядро-процесс: для x86 мы видим виртуальное адресное пространство процесса, куда ядро мапирует отдельные страницы 4 кБ или 2 МБ. Управление мапированием не простое. Одной и той же страницей может владеть несколько процессов (в режимах read/write, read only или copy on write). Добавлено через 26 минут и 21 секунду:Вывод: главное цель. Мне понравился вопрос, который был в материале: - Вот, Вы, говорите, что на полезную работу современные СУБД тратят 12% своей скорости. А в чём эта полезная работа состоит? - Индексы, работа с ними, поиск. Можно было бы просто хранить файлы на диске...
Знал я программистов, утверждавших, что FoxPro — самая быстрая на свете база. Ну, может она и быстрая, но для очень небольшого объема данных, когда индекс может быть загружен целиком в память и чтение идет в одном потоке. Чуть усложнились условия и FoxPro пропал. Мало того, что они предлагают ещё: для производительности выполнять множество запросов, а потом записывать лог на диск, и потом уведомлять внешнего пользователя. То есть, они ориентированы на оптимизацию работы сервера, сделать его дешевле, а для клиента всё пройдёт незаметно - они не ощутят особой скорости и быстродействия. Следствием этого подхода и стали промежуточные уровни, полагаю, сделаны они для распределения задач, как между потоками одной машины, так и между машинами.
Посмотри, что значит ASIC. База, следующая этому соглашению, дает определенные гарантии. Без них реализация транзакционности и целостности ложится на прикладной уровень и становится набором костылей. Реализация ASIC, естественно, требует накладных расходов. Только при чем тут вообще базы? Мы ж обсуждали алокаторы. Добавлено через 1 минуту и 47 секунд:Что ты называешь "ордером"? В свете этого, разумным видится переопределение функции main, и блокировка стандартных менеджеров памяти заглушками в виде макроса. Переопределение main? Ты и так ее сам определяешь. Только к алокатору это не имеет отношения. Блокировка макросами? Это уже не смешно. Вызовешь ты какую-либо функцию из любой библиотеки, которая вернет тебе выделенную через malloc память, а ты ее отдашь в свой free. Будет жопа. Добавлено через 2 минуты и 49 секунд:По поводу индексов и адресов, да экономию байт можно сделать, реализуя 32-битное виртуальное пространство индексов, однако, в этом варианте добавятся дополнительные вычисления, дополнительное поле хранения индексов, и, что важно - ограничения и коды ошибок. А откуда вообще взялись "коды ошибок"? Кому они нужны? Ошибка одна — отказ. Индексы дают существенную экономию памяти, когда нужно много связей. Например в структурах типа дерево или других графах. Обычное бинарное дерево требует 3-х указателей (или трех индексов), что дает 3*8=24 байта (в случае индексов: 12 или 6). Для малоразмерных узлов это большая экономия. По поводу индексов и адресов, да экономию байт можно сделать, реализуя 32-битное виртуальное пространство индексов, однако, в этом варианте добавятся дополнительные вычисления, дополнительное поле хранения индексов, и, что важно - ограничения и коды ошибок. Я думаю, индексы делать отдельными или хитрыми имеет смысл, когда необходимо сделать их уникальность между разными машинами, здесь же всё в пределах физически одной памяти. Хосты и адресное пространство — какая тут взаимосвязь? Что-то мне кажется, статья в твоей голове не улеглась, а подняла нешуточную бурю. Отстранись от баз данных, отстранись от хостов. Ссылку я привел только ради примера алокаторов. Добавлено через 14 минут и 39 секунд:Суть идеи SLAB, если правильно понял: Это ты описал mempool. Slab — это плитка. Суть slab в простой и быстрой фрагментации и дефрагментации. Представь себе пол, покрытый плиткой единого размера. Мы можешь выбрать только плитку целиком и никак иначе. Т.к. нам может понадобиться плитка меньше, есть slab-алокатор следующего уровня, который обслуживает запросы на плитки меньших размеров. Плитка всегда делится пополам. Пример: у slab_cache есть 4 плитки по 4 МБ. Программа попросила плитку 256 кБ. В итоге у slab_cache будет в наличии: 3 х 4М, 1 х 2М, 1 х 1М, 1 х 512к, 2 х 256к. Далее программа попросит еще плитку 256к — будет выдана имеющаяся свободная. Когда она попросит третью плитку 256к, будет поделена ближайшая большая плитка — 512к. При освобождении плитки, если рядом есть другая свободная плитка, которые можно объединить в бОльшую, это будет сделано. Для работы с мелкими объектами это неудобно. Slab нужен для обслуживания алокаторов следующего уровня, таких как mempool. Использовать Slab в прикладной программе ваще не обязательно. Алокатор типа mempool может запрашивать блоки памяти у malloc или сразу у ОС.
|
|
« Последнее редактирование: 24-01-2017 19:24 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Aether
|
|
« Ответ #25 : 24-01-2017 20:21 » |
|
Надо цепь восстановить, иначе будет каша. Приложение в начале своей работы инициализирует менеджер памяти, после этого, оно получает возможность просить в аренду у менеджера склады. Каждый склад - это упорядоченный список записей(описателей). Каждая запись содержит в себе указатель на данные, и их размер. Индекс - это уникальный номер по которому я могу на складе найти свои данные. Сначала я отделил его от адреса, а сейчас склонен, что в пределах приложения лучше использовать адрес - он уникален. Ордер - поскольку склад упорядочен, то каждая запись в нём имеет своё место, и свой номер. Номера можно изменять, при этом индекс останется прежним, зато функции получат возможность перечислять данные склада по очереди, и сама очередь имеет значение. Простой пример: typedef struct _point /* Структура трёхмерной точки для реализации сплайна */ { char type; /* Тип точки: настоящая или вспомогательная */ double X; double Y; double Z; double B; /* Толщина линии сплайна в точке в графическом представлении */ } point; Вот эта точка и есть те самые данные фиксированного размера, а сам склад - это сплайн, который из них состоит, и точек в нём может быть до тысяч. typedef struct _memret /* Структура возвращаемая менеджером при выделении данных*/ { char err; /* Код ошибки */ void* addr; /* Указатель на данные */ unsigned int order; /* Номер записи по порядку*/ } memret; Функция, которая будет его вычерчивать получит на входе номер склада и всё. Далее она в процессе построения будет запрашивать у менеджера узлы по номерам, получая адреса на точки. Если мне необходимо заменить точку сплайна, поменять их местами, то я не переписываю данные, а просто изменяю очерёдность записи в структуре склада. Я многое упростил, чтобы ближе к сути, и это требуется не только для сплайнов, тут хоть строки обрабатывай, хоть полигоны, таблички разные. Программист, который реализует библиотеку, и который её использует - это несколько разные люди. Второй, может быть инженером, и хотеть, например, обсчитать балочку на прогиб, он знает математику, но его нужно оградить от внутренностей реализации, поэтому я и говорю про переопределение main. То есть, его main замещается на, например, u_main, а библиотека вставляет в начало свой main и инициализирует в нём всё необходимое, чтобы упростить рутинные операции. А заглушка должна отсечь malloc/free и вызывать ошибку на этапе компиляции, чтобы случайно в какой-нибудь подключаемой ещё библиотеки не произошёл нарушающий работу вызов. Хосты и адресное пространство, связь такая, что 64 бит - это очень большой объём данных, думаю, не реальный. Так вот, разработчики БД вполне могут использовать старшую часть 64-битного индекса, например 8 бит, для идентификации машины в группе, а младшую для представления адреса, используя маски. Таким образом, адресное пространство в группе может быть как бы общим, когда по маске происходит исключение, то одна машина запрашивает часть данных у другой, как-то происходит при подкачке с диска. Это просто мысль такая прошла. Вот в идеале и нужно запрашивать у ОС, только грамотно. У меня больше непонимания: как эффективно организовать записи на складе. С одной стороны они в том же пространстве памяти, что и данные. А с другой, они представляют нумерованный стек. Например: 0(807) - 1(504) - 2(208) - 3(117) - 4(201) - 5(607) - ордера записей на складе 147. В скобках индекс. Удаляю со склада 147 запись 3. Новый порядок: 0(807) - 1(504) - 2(208) - 3(201) - 4(607) То есть дырку нужно либо помечать свободной, и не переписывать стек - это даст прирост на этапе выделения и освобождения памяти. Но при этом будет невозможно быстро вычислить адрес по ордеру, потребуется перебор. Другая стратегия - переписывать стек каждый раз. Освобождение и выделение при большом размере склада будет медленный. Зато вычисление адреса по ордеру - быстрейшее. Может быть есть другой подход?
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #26 : 24-01-2017 21:21 » |
|
В общем у тебя не универсальный инструмент, а специфический для задачи. Предлагаю более простое решение. У тебя структура point весит 40 байт, 7 из которых ушли на выравнивание. Туда легко вписать 32-битное значение. И так, предлагаю более простой вариант: 1. Пул points: динамический массив. Преалокация на фиксированное число значений (скажем 1000) или на программно заданное число. При алокации может расширяться с шагом, скажем, 100 или с прогрессивным шагом, с возможным перемещением данных. 2. Адресация через индекс в этом массиве. 3. В структуру вводим поле next — индекс следующей точки в списке. Используем маркер 0x0 или 0xffffffff (как нравится, но с 0x0 придется потерять точку #0) для обозначения конца списка. 4. Пул имеет free_points — индекс некой свободной точки. Эта точка в свою очередь ссылается на следующую свободную точку. При освобождение точки она добавляется в начало этого списка (константная операция). При выделении берется точка из начала списка (тоже константная операция), в редких случаях требуется расширить буфер и добавить новые точки в список свободных. 5. В программных структурах ты тоже делаешь аналогичные списки с терминирующим маркером. Добавлять будешь, вероятно, в конец, а перебирать с начала: нужно хранить индекс начала и конца списка, добавление/удаление в начале и в конце константное, в остальных позициях — линейное. Итого. Не тестил. #pragma once
#include <stdint.h>
typedef uint32_t point_index;
typedef struct { char type; point_index next; double X; double Y; double Z; double B; } point;
#define POINT_END_INDEX 0xffffffff #define POINT_POOL_PREALLOC 1000 #define POINT_POOL_REALLOC_STEP 1000
typedef struct { point *points; point_index free_points; size_t total; size_t step; } point_pool;
inline point* get_point(point_pool* pool, point_index idx) { return pool->points[idx]; }
inline void add_point_to_list(point_index *head, point *p, point_index idx) { p->next = *head; *head = idx; }
inline void remove_point_from_list(point_index *head, point *p) { *head = p->next; }
#define point_list_for_each(pool, first_idx) \ for (\ point_index idx = (first_idx), point* p = get_point((pool), idx);\ idx != POINT_END_INDEX;\ idx = p->next, p = get_point((pool), idx)\ )
inline free_point_list(point_pool *pool, point_index *head) { point_list_fot_each(pool, *head) { remove_point_from_list(head, p); free_point(pool, idx); } }
extern point_pool* create_point_pool(size_t prealocate_count = POINT_POOL_PREALLOC, size_t step = POINT_POOL_REALLOC_STEP);
extern void destroy_point_pool(point_pool* pool);
extern point_index alloc_point(point_pool* pool);
inline void free_point(point_pool* pool, point_index idx) { add_point_to_list(&(pool->free_points), &(pool->points[idx]), idx); } #include "points.h" #include <stdlib.h>
static void add_points_to_pool(point_pool* pool, point_index new_points_index, size_t count) { point_index n = new_points_index + (point_index)count - 1; pool->points[n].next = pool->free_points;
for (--n; n > new_points_index - 1; --n) { pool->points[n].next = n + 1; }
pool->free_points = new_points_index; }
static int extend_point_pool(point_pool* pool) { size_t new_total = pool->total + pool->step; points *new_points = (points*)realloc((void*)pool->points, sizeof(point) * new_total);
if (new_points == 0) { return 0; }
pool->points = new_points; add_points_to_pool(pool, pool->total, pool->step); pool->total = new_total; return 1; }
point_pool* create_point_pool(size_t prealocate_count, size_t step) { point_pool* pool = (point_pool*)malloc(sizeof(point_pool));
if (pool == 0) return 0;
pool->points = (point*)malloc(sizeof(point) * prealocate_count);
if (pool->points == 0) { free((void*)pool); return 0; }
pool->free_points = POINT_END_INDEX; pool->total = prealocate_count; pool->step = step; add_points_to_pool(pool, 0, prealocate_count); return pool; }
void destroy_point_pool(point_pool* pool) { free((void*)pool->points); free((void*)pool); }
point_index alloc_point(point_pool* pool) { if (pool->free_points == POINT_END_INDEX) { if (extend_point_pool(pool) == 0) { return POINT_END_INDEX; /* жаль, что Си, а то бы exception бросил */ }
}
idx = pool->free_points; remove_point_from_list(&(pool->free_points), &(pool->points[idx])); return idx; } UPD: Подчистил, оптимизировал, добавил сервисные штуки get_point, add_point_to_list, remove_point_from_list, free_point_list и point_list_for_each. Последнее содрал отсюда. Еще такая мысль мелькнула: ты знаешь, сколько у тебя точек будет в балке? Почему сразу не сделать типа create_point_array с выделением по malloc массива и инициализацией структур? Несказанно проще и быстрее.
|
|
« Последнее редактирование: 26-01-2017 16:11 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Aether
|
|
« Ответ #27 : 25-01-2017 10:56 » |
|
Пока посмотрел бегло, получается как-то так, но могу ошибиться. Не проще: 1. Данные могут считываться из файла, например, SCAD или NASTRAN или набранного вручную, причём не мною. 2. В процессе обработки, например, один большой полигон может триангулироваться в маленькие, таким образом, один пропадёт, а пару сотен новых прибавится. 3. Хочется на будущее, именно сделать менеджер, который будет работать с абстрактным объектом, а не привязанным к какому-либо фиксированному объекту. Строго говоря, в складе может храниться не массив точек, а массив примитивов, и функция обработки сплайна, получив на вход неправильный номер склада всё попортит, тем не менее, это устраивает - такие ошибки лечить не сложно, главное, чтобы простота была в использовании библиотеки - её надёжность.
|
|
« Последнее редактирование: 25-01-2017 10:59 от Aether »
|
Записан
|
|
|
|
Aether
|
|
« Ответ #28 : 25-01-2017 19:58 » |
|
Сейчас пересматривать начал, и запутался. // ВХОД: // pool - указатель на управляющую структуру нашего бассейна // new_points_index - по смыслу это то количество точек, которое уже есть. // count - по смыслу то число, которое нужно добавить.
static void add_points_to_pool(point_pool* pool, point_index new_points_index, size_t count) { point_index n = new_points_index + (point_index)count - 1; pool->points[n] = pool->free_points;
for (--n; n > new_points_index - 1; --n) { pool->points[n] = n + 1; }
pool->free_points = new_points_index; } Предположим, мы сейчас имеем 500 точек, выделяем ещё 100. n = 599 - номер последней точки. Далее записываем в pool->points[599] некое число? Возможно имелось ввиду pool->points[599].next? И тоже в цикле, который заполняет номера от 598 до 499. Так вот, 499 он как бы занят уже, но предполагаю, что тут нужно его next переписать. В то же время free_points - координата (индекс) первой свободной точки, стало быть зачем её записывать в конец?
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #29 : 25-01-2017 21:17 » |
|
n = 500 + 100 - 1 = 599 Далее опечатки. pool->points[n].next = pool->free_points;
for (--n; n > new_points_index - 1; --n) { pool->points[n].next = n + 1; } Цикл перебирает n от 598 до 500. Любая свободная, а не первая свободная. Порядок может быть любой. Но в текущей логике к моменту расширения free_index будет всегда равен терминатору (нет свободных). Функция же поддерживает расширение в любой момент. Поправил код в посте. За одно еще ошибки нашел.
|
|
« Последнее редактирование: 25-01-2017 21:26 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
|