2016-02-29 3 views
2

У меня есть загадочный результат, который я пытаюсь понять.Замена назначения строки с помощью операции PChar

Я пытается улучшить скорость этого рутинного

function TStringRecord.GetWord: String; 
begin 
    // return the next word in Input 
    Result := ''; 

    while (PC^ <> #$00) and not PC^.IsLetter do begin 
    inc(FPC); 
    end; 

    while (PC^ <> #$00) and PC^.IsLetter do begin 
    Result := Result + PC^; 
    inc(FPC); 
    end; 
end; 

, заменив Result := Result + PC^ операцией указателей. Это моя попытка:

function TStringRecord.GetWord2: String; 
var 
    Len : Integer; 
    StartPC, 
    DestPC : PChar; 
begin 
    // return the next word in Input 
    Result := ''; 

    while (PC^ <> #$00) and not PC^.IsLetter do begin 
    inc(FPC); 
    end; 

    Len := Length(Input); 
    SetLength(Result, Len); 
    StartPC := PChar(Result); 
    DestPC := PChar(Result); 
    while (PC^ <> #$00) and PC^.IsLetter do begin 
    WStrPLCopy(DestPC, PC, 1); 
    inc(FPC); 
    inc(DestPC); 
    end; 
    SetLength(Result, DestPC - StartPC); 
end; 

По моей линии профилировщике WStrPLCopy(DestPC, PC, 1) занимает в 50 раз больше, чем Result := Result + PC^. Насколько я могу судить, это потому, что при входе в WStrPLCopy есть вызов _WStrFromPWChar, который, похоже, скопирует еще много символов , чем тот, который вам нужен. Как я могу избежать этого, или кто-то может предложить альтернативный метод на основе PChar?

Остальная часть моего кода ниже:

TStringRecord = record 
private 
    FPC: PChar; 
    FInput: String; 
    procedure SetInput(const Value: String); 
public 
    function NextWord : String; 
    function NextWord2 : String; 
    property Input : String read FInput write SetInput; 
    property PC : PChar read FPC; 
end; 

procedure TStringRecord.SetInput(const Value: String); 
begin 
    FInput := Value; 
    FPC := PChar(Input); 
end; 
+1

Без вас, показывая нам значимый код, это трудно поделать. Вы не указали ссылку на входную строку. Вы выполняете два распределения кучи, когда этого достаточно. Вы копируете по частям. Конечно, вам нужно найти индекс начала и конца и использовать «Копировать». Я заверяю вас, что вы получите более качественные ответы, если вы покажете свой код. –

+0

Я не собирался ничего скрывать, просто хочу опустить ненужные детали. Обычно ввод может состоять из нескольких символов. Что я копирую «по частям», как вы выразились, и что вы имеете в виду двумя распределениями кучи? –

+0

Вы выполняете два распределения кучи. Они такие же простые, как и в коде. Вам нужно только сделать это. Вместо копирования символа по символу я копировал один раз в конце. Я бы не использовал здесь 'PChar' и вместо этого использовал бы индексы. Я бы очень опасался профилей линий. Используйте секундомер, чтобы это сделать. –

ответ

4

Это, как я хотел бы написать это:

function TStringRecord.GetWord: String; 
var beg: PChar; 
begin 
    // return the next word in Input 
    while (FPC^ <> #0) and not FPC^.IsLetter do 
    inc(FPC); 
    beg := FPC; 
    while (FPC^ <> #0) and FPC^.IsLetter do 
    inc(FPC); 
    SetString(result, beg, FPC-beg); 
end; 

При этом, код очень читаемый, и у вас есть одно распределение памяти, и я думаю, вы не могли бы написать что-либо быстрее (но встраивая PC^.IsLetter, что является единственным вызовом внешней части кода).

+0

Предположим, что первый вызов возвращает только конечное пространство (ы) в начале 'Input' (** not ** PC^.IsLetter). Если вы исправите это, вам все равно нужно будет продвигать прошлые пробелы между словами перед установкой 'beg' –

+0

@TomBrunberg Вы правы: я не следовал точно используемому алгоритму поиска - просто хотел показать, как закодировать некоторое извлечение строки используя переменные PChar, с одним распределением памяти. –

+0

Не индексируется с использованием '[]' быстрее, чем арифметические подходы с указателем? –

Смежные вопросы