Я записал, что Delphi отлаживает visualizer для TDataSet для отображения значений текущей строки, источник + снимок экрана: http://delphi.netcode.cz/text/tdataset-debug-visualizer.aspx. Работа хорошего, но очень медленного. Я сделал некоторый optimalization (как получить имена полей), но все еще только для 20 полей занимает 10 секунд для показа - очень плохо.
Основная проблема, кажется, медленный IOTAThread90. Оцените используемый основным кодом, показанным ниже, эта стоимость процедуры большую часть времени, строка с ** приблизительно 80%-е время. FExpression является названием TDataset в коде.
procedure TDataSetViewerFrame.mFillData;
var
iCount: Integer;
I: Integer;
// sw: TStopwatch;
s: string;
begin
// sw := TStopwatch.StartNew;
iCount := StrToIntDef(Evaluate(FExpression+'.Fields.Count'), 0);
for I := 0 to iCount - 1 do
begin
s:= s + Format('%s.Fields[%d].FieldName+'',''+', [FExpression, I]);
// FFields.Add(Evaluate(Format('%s.Fields[%d].FieldName', [FExpression, I])));
FValues.Add(Evaluate(Format('%s.Fields[%d].Value', [FExpression, I]))); //**
end;
if s<> '' then
Delete(s, length(s)-4, 5);
s := Evaluate(s);
s:= Copy(s, 2, Length(s) -2);
FFields.CommaText := s;
{ sw.Stop;
s := sw.Elapsed;
Application.MessageBox(Pchar(s), '');}
end;
Теперь я понятия не имею, как улучшить производительность.
У меня еще не было возможности поиграть с визуализаторами отладки, поэтому я не знаю, работает ли это, но пробовали ли вы использовать Evaluate () для преобразования FExpression
в его фактический адрес памяти? Если вы можете это сделать, приведите этот адрес памяти к указателю TDataSet
и используйте его свойства в обычном режиме без дополнительных вызовов Evaluate (). Например:
procedure TDataSetViewerFrame.mFillData;
var
DS: TDataSet;
I: Integer;
// sw: TStopwatch;
begin
// sw := TStopwatch.StartNew;
DS := TDataSet(StrToInt(Evaluate(FExpression)); // this line may need tweaking
for I := 0 to DS.Fields.Count - 1 do
begin
with DS.Fields[I] do begin
FFields.Add(FieldName);
FValues.Add(VarToStr(Value));
end;
end;
{
sw.Stop;
s := sw.Elapsed;
Application.MessageBox(Pchar(s), '');
}
end;
Этому Evaluate необходимо проделать удивительно много работы.Компилятору необходимо скомпилировать его, преобразовав символы в адреса памяти, в то время как оценка свойств может вызвать вызов функций, для чего отладчик должен скопировать аргументы в отладчик, настроить фрейм стека, вызвать функцию, которая будет вызвана, собрать результаты - а это включает в себя приостановку и возобновление отладки.
Я могу только предложить попытаться вложить больше работы в вызов Evaluate
. Я не уверен на 100%, как взаимодействие между отладчиком и оценщиком (который является частью компилятора) работает для этих визуализаторов, но пакетирование как можно большего объема работы может помочь. Попробуйте создать более сложное выражение перед вызовом Evaluate
после цикла. Возможно, вам придется использовать какое-то соглашение об экранировании или ограничении для распаковки результатов. Например, представьте, как будет выглядеть выражение, которое построило список значений полей и вернуло их в виде строки, разделенной запятыми, но вам нужно будет экранировать запятые в самих значениях.
Поскольку Delphi - это другой процесс, чем ваш отлаживаемый exe, вы не можете напрямую использовать указатели памяти вашего exe, поэтому вам нужно использовать ".Evaluate" для всего.
Вы можете использовать 2 различных подхода:
У меня работает вариант 1, 2 тоже должно быть возможно, но немного сложнее и "уродливее" из-за тактики хакинга... С кодом ниже (просто добавьте в dpr) вы можете использовать:
Result := 'Dump=' + Evaluate('TObjectDumper.SpecialDump(' + FExpression + ')');
Демонстрационный код варианта 1, измените его для вашего TDataset (возможно, сделайте CSV строку всех значений?):
unit Unit1;
interface
type
TObjectDumper = class
public
class function SpecialDump(aObj: TObject): string;
end;
implementation
class function TObjectDumper.SpecialDump(aObj: TObject): string;
begin
Result := '';
if aObj <> nil then
Result := 'Special dump: ' + aObj.Classname;
end;
initialization
//dummy call, just to ensure it is linked c.q. used by compiler
TObjectDumper.SpecialDump(nil);
end.
Edit: in case someone interest: I got option 2 working too (bpl injection)