Ruslan1 17 15 апреля, 2021 Опубликовано 15 апреля, 2021 · Жалоба Здравствуйте! Дано : есть конфигурационный файл, задающий формат генерируемой строки с использованием разных 'ключей'. Во время вывода эти ключи подменяются конкретными величинами. Количество ключей в строке может быть разное. Например: конфигурация String1 = "\dat \time, \SNum, V1=\Val1, V2=\Val2" превращается в выводимую строку "2021-04-01 00:22:15, 103567, V1=17.2, V2=0.4" Сейчас это делается парсингом строки конфигурации онлайн во время каждой печати строки, символ-за символом, с заменой встреченных ключей на из значения. Но это реально долго (у меня десятки ключей). Хочу ускорить и не делать парсинг каждый раз. Идеально было бы однажды пропарсить конфигурационную строку и создать что-то, что можно просто вызывать каждый раз, когда эту строку нужно напечатать. Для данного случая, например это будет: sprintf (txtOut, "%s_%s, %06d, V1=%.1f, V2=%.1f", date, time, SERNUM, Val1, Val2); но как это сделать? Нужно решить задачи: 1) создать строку формата (в примере это ["%s_%s, %06d, V1=%.1f, V2=%.1f"]) - это самое простое, это я понимаю. 2) создать строку аргументов (в примере это [date, time, SERNUM, Val1, Val2]) - не представляю как это сделать. Ну, могу создать массив указателей на нужные величины, но как это использовать? 3) запустить это как sprintf() - подозреваю, что если будет ответ на предыдущий вопрос, то этот уже просто решается. Вроде бы подходит vsprintf() и va_list ? Если я правильно понял, то для vsprintf() можно однажды создать список параметров va_list c указателями на расположение нужных мне величин в памяти, и дальше просто вызывать vsprintf() столько раз сколько нужно? Так? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
rkit 4 15 апреля, 2021 Опубликовано 15 апреля, 2021 · Жалоба sprintf точно так же парсит строку формата. Переливать из одного в другое будет еще медленее. "реально долго" и "десятки ключей" как-то не стоят рядом. Реальные измерения есть? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Ruslan1 17 15 апреля, 2021 Опубликовано 15 апреля, 2021 · Жалоба 1 hour ago, rkit said: sprintf точно так же парсит строку формата. Переливать из одного в другое будет еще медленее. "реально долго" и "десятки ключей" как-то не стоят рядом. Реальные измерения есть? 18.6 миллисекунды на строку. Но когда нужно сформировать тысячу-другую строк, набегает много. Время зависит от многих параметров, в основном- какой длины строка парсится и сколько ключей возможны: получается, что встретив новый ключ в строке, я его сравниваю с базой, применяя strstr(). То есть если у меня база из сейчас 120 ключей, то для каждого встреченного ключа это от одного до 120 вызовов strstr() . Можно оптимизировать сам метод, но лучше уйти от бесконечного парсинга. И меня не столько парсинг строки формата волнует во время sprintf(), сколько этот вот парсинг ключей для списка параметров, который и съедает все время. А формат и строка параметров у меня постоянные до следующего выключения, то есть создав один раз списки, надеюсь что vsprintf с ними быстрее будет работать чем я с исходными текстовыми ключами. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Edit2007 3 16 апреля, 2021 Опубликовано 16 апреля, 2021 · Жалоба Цитата Время зависит от многих параметров, в основном- какой длины строка парсится и сколько ключей возможны: получается, что встретив новый ключ в строке, я его сравниваю с базой, применяя strstr(). То есть если у меня база из сейчас 120 ключей, то для каждого встреченного ключа это от одного до 120 вызовов strstr() . Можно оптимизировать сам метод, но лучше уйти от бесконечного парсинга. Можно для каждого ключа посчитать что-то вроде хеша (или CRC) и потом сравнивать не строки а хеш-суммы (CRC). И только при совпадении хеш (CRC) вызывать сравнение строк, чтобы избежать случайного совпадения. Еще вариант все ключи отсортировать по алфавиту, тогда посимвольное сравнение будет более оптимальным в ручном режиме. Поскольку каждый следующий символ будет отсекать все предыдущие несовпадения. Ну например вводится первый символ 'd' - в сортированном списке находим первый и последний ключи начинающиеся на 'd'. Вводится второй символ из заданного диапазона находим первый и последний ключи с заданным символом и т.д. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 243 16 апреля, 2021 Опубликовано 16 апреля, 2021 · Жалоба 2 часа назад, Edit2007 сказал: Можно для каждого ключа посчитать что-то вроде хеша (или CRC) и потом сравнивать не строки а хеш-суммы (CRC). И только при совпадении хеш (CRC) вызывать сравнение строк, чтобы избежать случайного совпадения. Не нужны все эти ухищрения. Тупой простейший цикл по N первым 32-битным словам - и всех делов. Примерно так сделано у меня в аналогичном случае: enum {JK_cfg, JK_now, JK_control, JK_file, ... JK_n}; #define apiKeyLen 12 //[байт] #define apiKeysN ((sizeof(apiKeys) - 1) / apiKeyLen) #define __align4 __attribute__ ((aligned (4))) typedef __packed u32 u32p8; typedef __packed u64 u64p8; char const apiKeys[] __align4 = "cfg " //JK_cfg "now " //JK_now "control " //JK_control "file " //JK_file ... }; int F(char const *str) { char const *ss = &apiKeys[sizeof(apiKeys) - 1]; u64 q = ((u64p8 *)str)[0]; u32 j = ((u32p8 *)s)[2]; int n = apiKeysN - 1; do { ss -= apiKeyLen; if (q == ((u64p32 *)ss)[0]) if (j == ((u32 *)ss)[2]) break; } while (--n >= 0); return n; } Компилируется это в короткий оптимальный цикл. Если длина некоторых имён превышает apiKeyLen (хвост вылазит за apiKeyLen), то после выхода из цикла добавляем проверку хвоста и, если он не совпадает, возвращаемся в цикл. Ну или - в отдельный короткий список такие имена. PS: Использование strstr() для такой цели - это конечно автор старался как можно сильнее затормозить работу. Только этим можно объяснить её использование. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
rkit 4 16 апреля, 2021 Опубликовано 16 апреля, 2021 · Жалоба 19 hours ago, Ruslan1 said: получается, что встретив новый ключ в строке, я его сравниваю с базой, применяя strstr() Ну так вот и нашли проблему. Сделай префиксное дерево или хеш-таблицу. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ARV 1 16 апреля, 2021 Опубликовано 16 апреля, 2021 · Жалоба Вопрос-то был про переменное количество аргументов в функции, а не про парсинг строк... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Ruslan1 17 18 апреля, 2021 Опубликовано 18 апреля, 2021 · Жалоба Спасибо всем, буду думать в рабочее время. 1. если сделать как однажды сделанную функцию с установленным динамически числом аргументов- то все уже придумано до меня, это массив, созданный с помощью va_list() для vsprintf(). 2. Если упрощать "в лоб"- то отказаться от strstr(): переконвертировать строку, оставив признак ключа (у меня это '/') и после него один единственный байт, уникальный для каждого ключа. Метод работает для 253 ключей, что меня сейчас более чем устраивает. Тогда простым 'switch-case' решается. Первый метод быстрее, но посмотрю получится ли так как я хочу, и сколько занимает в памяти этот сформированный va_list массив: у меня строк много разных, и может быть критично если массив сильно (в дестки-сотни раз) больше чем сама оригинальная строка. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
xvr 12 18 апреля, 2021 Опубликовано 18 апреля, 2021 · Жалоба 18 minutes ago, Ruslan1 said: это массив, созданный с помощью va_list() для vsprintf(). Немного занудства - va_list это не массив, а некий псевдо указатель на параметр в вызове функции. Реализация его полностью зависит от архитектуры процессора и иногда от компилятора. Далеко не на всех архитектурах это реальный массив :( Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AlexRayne 7 19 апреля, 2021 Опубликовано 19 апреля, 2021 · Жалоба Имхо, наиболее производительоо Вам будет взять исходник принтера vprintf, и немного навернуть его, добавив в парсинг ваши параметры. еще большой головняк - передать эти самые параметры в printf, а переделав сам принтер, вы сможете запользовать глобальные переменные, или хитрые структуры передавать параметрами. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 243 19 апреля, 2021 Опубликовано 19 апреля, 2021 · Жалоба 3 минуты назад, AlexRayne сказал: Имхо, наиболее производительоо Вам будет взять исходник принтера vprintf, и немного навернуть его, добавив в парсинг ваши параметры. "Наворачивать исходник printf()" - нет необходимости. Если хочется получить максимально гибкий printf(), для этого есть его вариант с передачей ему в аргументах 2-х указателей: указателя на свою callback-функцию и указателя блок параметров для неё. extern "C" int _Printf(void *(*)(void *, int), void *, const char *, va_list *); Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AlexRayne 7 19 апреля, 2021 Опубликовано 19 апреля, 2021 · Жалоба Это где такой принтф окопался? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 243 19 апреля, 2021 Опубликовано 19 апреля, 2021 · Жалоба 45 минут назад, AlexRayne сказал: Это где такой принтф окопался? IAR. У других думаю есть аналогичные. По-крайней мере у CCS - точно есть. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Ruslan1 17 20 апреля, 2021 Опубликовано 20 апреля, 2021 · Жалоба On 4/18/2021 at 7:53 PM, xvr said: Немного занудства - va_list это не массив, а некий псевдо указатель на параметр в вызове функции. Реализация его полностью зависит от архитектуры процессора и иногда от компилятора. Далеко не на всех архитектурах это реальный массив :( Это очень важное занудство, спасибо за напоминание! :) И живет этот "псевдоуказатель" только между va_start() и va_end(). Вот нашел пример тут, как организуют независимую область памяти и хранят там va_list. Они через va_copy() создают копию, и делают проход два раза: первый раз для определения нужного размера памяти и потом, после malloc(), засовывают все в эту память. Получается char* на область памяти, в которой храниться va_list. char* vjoin(const char* delim, va_list ap) { va_list aq; va_copy(aq, ap); size_t dlen = strlen(delim); /* First pass. Use the copied va_list */ size_t needed = 1; /* NUL terminator */ const char* s = va_arg(aq, const char*); if (s) { needed += strlen(s); while ((s = va_arg(aq, const char*))) needed += dlen + strlen(s); } va_end(aq); /* Second pass. Use the original va_list */ char* rv = malloc(needed); size_t offset = 0; *rv = 0; s = va_arg(ap, const char*); if (s) { strcpy(rv, s); offset = strlen(s); while ((s = va_arg(ap, const char*))) { strcpy(rv + offset, delim); strcpy(rv + offset + dlen, s); offset += dlen + strlen(s); } } return rv; } char* join(const char* delim, ...) { va_list ap; va_start(ap, delim); char* rv = vjoin(delim, ap); va_end(ap); return rv; } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Ruslan1 17 21 апреля, 2021 Опубликовано 21 апреля, 2021 · Жалоба Ниасилил. Сложно у меня va_list идет, хакерство какое-то непереносимое (я его хотел сохранять для использования потом). Сделаю "в лоб" сотня функций, в каждую из которых передается от 0 до 100 переменных: mystring_0arg(char* out, char* format, void* arg[]) { sprintf (out, format);} mystring_1arg(char* out, char* format, void* arg[]) { sprintf (out, format, *arg[0]);} mystring_2args(char* out, char* format, void* arg[]) { sprintf (out, format, *arg[0], *arg[1]);} mystring_3args(char* out, char* format, void* arg[]) { sprintf (out, format, *arg[0], *arg[1], *arg[2]);} , а при формировании строки буду использовать указатель на строку формата, указатель на вызываемую функцию (с нужным количеством аргументов), и указатель на расположение списка аргумантов (точнее, список указателей на величины). То есть парсинг по-минимуму: один раз в начале работы при просмотре конфигурации. Тогда же и определяю какая из функций вызывается. во время работы получаю максимальное быстродействие. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться