Оптимизация и INLINE

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Оптимизация и INLINE

Сообщение mike » 30.03.2010 02:13:19

Можно ли каким-нибудь образом оптимизировать вызов такого типа:
Код: Выделить всё
procedure AppendLog(const LogKind: TLogKind; const Text: sting);
begin
  if LogKind in LogKinds then WriteLn(LogFile, Text);
end;

//...
  AppendLog(lkDebug, 'ErrCode='+IntToStr(ErrCode)+' "'+SysErrorMessage(ErrCode)+'"');
//...

В чем суть. Если глобальное множество LogKinds не содержит элемент lkDebug, то в лог не будет попадать соответствующая строка. Но проблема в том, что она все равно будет сформирована, будут вызваны функции IntToStr() и SysErrorMessage(), несколько раз будет перевыделена память и произведена конкатенация элементов строки. И все это для того, чтобы уже внутри функции понять, что ничего сохранять не нужно. В итоге большие и совершенно неоправданные потери времени. Проверка множества перед вызовом AppendLog() конечно решает проблему, но загромождает код.

На первый взгляд должна была помочь директива inline, но ассемблерный листинг показал, что "разворачивание" процедуры производится просто в месте (и вместо) ее вызова, все подготовительные действия выполняются точно так же, хотя как раз в этом случае можно было бы сгенерить такой код:
Код: Выделить всё
//...
  if lkDebug in LogKinds then WriteLn(LogFile, 'ErrCode='+IntToStr(ErrCode)+' "'+SysErrorMessage(ErrCode)+'"');
//...

Которого я ожидал и который решил бы проблему...

Короче, нет ли в FPC чего-нибудь типа сишных макросов, которые просто подставляли бы в нужное место шаблонный код без предварительного вычисления переданных в качестве параметров выражений?
mike
новенький
 
Сообщения: 40
Зарегистрирован: 23.02.2007 17:25:00

Re: Оптимизация и INLINE

Сообщение sign » 30.03.2010 06:44:56

Нарисовать процедуру
Код: Выделить всё
procedure AppendMyLog(const LogKind: TLogKind; lkDeb: ...; ErrCode: Integer);
begin
  if lkDeb in LogKind then
    WriteLn(LogFile, 'ErrCode='+IntToStr(ErrCode)+' "'+SysErrorMessage(ErrCode)+'"');
end;
sign
энтузиаст
 
Сообщения: 1131
Зарегистрирован: 30.08.2009 09:20:53

Re: Оптимизация и INLINE

Сообщение AbakAngelSoft » 30.03.2010 09:54:11

Использовать функцию Format
Код: Выделить всё
procedure AppendLog(const LogKind: TLogKind; const AText: string; Params: array of const);
begin
  if LogKind in LogKinds then begin
    WriteLn(LogFile, Format(AText,  Params));
  end;
end;

В отличии от варианта sign сохранится гибкость первоначальной функции, а все преобразования типов, конкатенация и т.д. будут вызваны только при необходимости.
Вот правда SysErrorMessage(ErrCode) прийдется все таки вызвать заранее.

Добавлено спустя 3 минуты 31 секунду:
Возникла идея!
Можно в качестве Params передавать не готовые параметры а ссылки на функции или методы их возвращающие. Только тогда прийдется реализовать что-то вроде замыканий и простенькая функция вывода лога станет громадным монстром.
Последний раз редактировалось AbakAngelSoft 30.03.2010 12:01:07, всего редактировалось 1 раз.
Аватара пользователя
AbakAngelSoft
постоялец
 
Сообщения: 273
Зарегистрирован: 06.08.2008 19:28:26
Откуда: Краснодар

Re: Оптимизация и INLINE

Сообщение MageSlayer » 30.03.2010 11:35:47

AbakAngelSoft писал(а):Добавлено спустя 3 минуты 31 секунду:
Возникла идея!
Можно в качестве Params передавать не готовые параметры а ссылки на функции или методы их возвращающие. Только тогда прийдется реализовать что-то вроде замыканий и простенькая функция вывода лога станет громадным монстром.


И еще идея - создать новую/попатчить в компиляторе функцию типа WriteLn (например для файла StdErr) , чтоб пропускать код в зависимости от глобального флага :)))).
Сорри, не удержался :).

Собственно - да. Замыканий/делегатов тут не хватает.
MageSlayer
постоялец
 
Сообщения: 216
Зарегистрирован: 07.09.2006 12:30:44

Re: Оптимизация и INLINE

Сообщение AbakAngelSoft » 30.03.2010 12:09:49

MageSlayer писал(а):делегатов тут не хватает

Делегат - это я так понимаю типобезопасный указатель на функцию. А в паскале указатель на функцию разве не имеет определенный тип с указанием параметров вызова и указанием возвращаемого значения? Поправьте если я ошибаюсь.

Кстати замыкания в паскале можно реализовать не расширяя язык. Через объекты и ссылки на методы этих объектов. По моему в delphi так и сделали, разбавив немного синтаксическим сахаром.
Аватара пользователя
AbakAngelSoft
постоялец
 
Сообщения: 273
Зарегистрирован: 06.08.2008 19:28:26
Откуда: Краснодар

Re: Оптимизация и INLINE

Сообщение MageSlayer » 30.03.2010 14:02:44

AbakAngelSoft писал(а):
MageSlayer писал(а):делегатов тут не хватает

Делегат - это я так понимаю типобезопасный указатель на функцию. А в паскале указатель на функцию разве не имеет определенный тип с указанием параметров вызова и указанием возвращаемого значения? Поправьте если я ошибаюсь.


Э-э. Это дурацкий разброд в терминах.

Я имел ввиду делегаты/lazy в том виде, который реализован в D.
Там это, означает, что вместо непосредственного вычисления выражения перед вызовом функции, компилятор генерирует функцию-обертку, которая вызывается, только в том случае, когда значение-параметра действительно используется.

См. http://www.digitalmars.com/d/2.0/lazy-evaluation.html Кстати, там пример как раз с логгированием :)

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


Не расширяя язык, это, имхо, преувеличение. Хотя, да, в Дельфе вроде сделано через объекты. Тяжелая и универсальная реализация, короче.
Мне бы, чего-нить попроще :).
См. viewtopic.php?f=1&t=5507
MageSlayer
постоялец
 
Сообщения: 216
Зарегистрирован: 07.09.2006 12:30:44

Re: Оптимизация и INLINE

Сообщение mike » 30.03.2010 14:33:36

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

Придется писать условия по месту, а это превратит простой код:
Код: Выделить всё
//...
if ErrCode <> 0 then
  AppendLog(lkError, 'ErrCode='+IntToStr(ErrCode)+' "'+SysErrorMessage(ErrCode)+'"')
else
  AppendLog(lkDebug, 'Opeation: Success')
//...
в этот:
Код: Выделить всё
//...
if ErrCode <> 0 then
  if lkError in LogKinds then
    AppendLog('ErrCode='+IntToStr(ErrCode)+' "'+SysErrorMessage(ErrCode)+'"')
  else
else
  if lkDebug in LogKinds then
    AppendLog('Opeation: Success')
  else
//...

Совсем иная читаемость :(
mike
новенький
 
Сообщения: 40
Зарегистрирован: 23.02.2007 17:25:00

Re: Оптимизация и INLINE

Сообщение скалогрыз » 30.03.2010 15:41:41

mike писал(а):Придется писать условия по месту, а это превратит простой код:
Код: Выделить всё
//...выше написан...

Совсем иная читаемость :(


Зачем писать "по месту вызова"? сделай отдельную процедуру. Сделай её inline чтобы не было обидно за "потерю времени на вызов процедуры". И читабельность вырастает до небес.

Код: Выделить всё
const
  AllowedToLog  = [lkError];

function AllowErrorLog(kind: TLogKind): Boolean; inline;
begin
  Result:=kind*AllowedToLog<>[];
end;

procedure AppendSysErrLog(kind: TLogKind; errCode: integer); inline;
begin
  if errCode=0 then
    AppendLog(kind, 'Opeation: Success')
  else if AllowErrorLog(kind) then
    AppendLog('ErrCode='+IntToStr(ErrCode)+' "'+SysErrorMessage(ErrCode)+'"')
end;


соответсвенно вызов:
Код: Выделить всё
  AppendSysErrLog(lkDebug, errCode);
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Оптимизация и INLINE

Сообщение AbakAngelSoft » 30.03.2010 16:03:58

mike писал(а):не избавит от неоправданного вызова IntToStr() и SysErrorMessage()

От неоправданного вызова IntToStr избавит!
скалогрыз писал(а):сделай отдельную процедуру

Совершенно верный подход!
Аватара пользователя
AbakAngelSoft
постоялец
 
Сообщения: 273
Зарегистрирован: 06.08.2008 19:28:26
Откуда: Краснодар

Re: Оптимизация и INLINE

Сообщение mike » 30.03.2010 16:29:04

скалогрыз, у меня в AppendLog() может передаваться что угодно, нет единого шаблона. Это может быть не только код и расшифровка ошибки, это бывает и информация об IP-адресе, и диагностическое сообщение о потребленной памяти и т.п. Предлагаете на каждый единичный случай логирования строки отдельную процедуру писать?
mike
новенький
 
Сообщения: 40
Зарегистрирован: 23.02.2007 17:25:00

Re: Оптимизация и INLINE

Сообщение скалогрыз » 30.03.2010 16:36:01

mike писал(а): Предлагаете на каждый единичный случай логирования строки отдельную процедуру писать?

Конечно! нужно же как-то заменять макросы с параметрами ;)
Я бы рад повторить дельное предложение с использованием Format, но не буду.

Кстати, таких "единичных случаев", на самом деле, по пальцам пересчитать.

Всяко лучше использовать структурированный код, чем писать всё в кучу?! или я заблуждаюсь?!

Код: Выделить всё
unit Log.

procedure AllowToLog(kind: TLogKind);

procedure AppendLog(kind: TLogKind; const S: AnsiStirng);
procedure AppendLog(kind: TLogKind; const Fmt: AnsiString; const Params: array of const);

---
unit LogEx;

uses  Log;

// каждая из процедур вызывает AppendLog, с предварительной проверкой kind
procedure AppendSysErrLog(kind: TLogKind; Err: Integer);
procedure AppendIPLog(kind: TLogKind; const Cmd: AnsiString; IPAddr: TIPAddr);
procedure AppendMemUsage(kind: TLogKind; const Cmd: AnsiString; Mem: PtrUInt);
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Оптимизация и INLINE

Сообщение mike » 30.03.2010 17:24:38

скалогрыз писал(а):Я бы рад повторить дельное предложение с использованием Format, но не буду.

Миллион-другой ненужных stralloc'ов (или как их там зовут) в секунду тоже очень плохо (даже без учета внутренностей SysErrorMessage() и иже с ним).

скалогрыз писал(а):Кстати, таких "единичных случаев", на самом деле, по пальцам пересчитать.

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

P.S. Уже давно все переписал под проверку перед вызовом.
mike
новенький
 
Сообщения: 40
Зарегистрирован: 23.02.2007 17:25:00

Re: Оптимизация и INLINE

Сообщение скалогрыз » 30.03.2010 17:35:33

mike писал(а):Миллион-другой ненужных stralloc'ов (или как их там зовут) в секунду тоже очень плохо (даже без учета внутренностей SysErrorMessage() и иже с ним).

чуть оффтопа.
если данные нужно собирать с очень большой скоростью, так SysErrorMessage нельзя использовать никак! Даже при условной компиляции или других ухищрениях.

SysErrorMessage (и другие человек ориентированные функции) нужно применять уже по завершению программы, при приведении лога в удобный для чтения вид.

А во время работы программы собирать только необходимые данные (н.р. код ошибки, но не текст ошибки)

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

а что можно записать и в каких комбинациях!? что есть такого, с чем Format не справиться?! format('%s %x %d', [s, 4, 5]);
н.р.
Код: Выделить всё
TIPAddr = record
   b1,b2,
   b3,b4: byte;
end;

writeln(format('удалённый сервер %d.%d.%d.%d не ответил во время', [ip.b1,ip.b2,ip.b3,ip.b4]));
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Оптимизация и INLINE

Сообщение alexrayne » 30.03.2010 21:57:12

А во время работы программы собирать только необходимые данные (н.р. код ошибки, но не текст ошибки)

+1
сам делал журналирование, милионов событий небуло, всего несколько сотен\сек но формирование сообщения из данных (оно же форматирование) + подсистема вывода в банальный файл давала серьезные лаги в журналируемом коде. посему делал специальный буффер сообщений и несколько раз\сек вызывал задачу которая етот буффер печатала в файл все свободное время.
alexrayne
постоялец
 
Сообщения: 125
Зарегистрирован: 03.12.2008 16:56:26


Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 8

Рейтинг@Mail.ru