Подтвердить что ты не робот

Указатель ^ против s [1]

В функции, которая читает данные с данными (данные, означающие исключительно) с диска, что я должен предпочесть? Что лучше?

A) DiskStream.Read(Pointer(s)^, Count)
or
B) DiskStream.Read(s[1], Count)

Примечание:
Я знаю, что оба имеют тот же результат.
Я знаю, что перед вызовом Read я должен установить SetLength of S.


ОБНОВЛЕНИЕ

S - AnsiString.

Вот полная функция:

{Считывает кучу символов из файла. Почему "ReadChars", а не "ReadString"? Эта функция читает строки С++ (длина строки также не записывалась на диск). Итак, я должен указать количество символов для чтения в качестве параметра. }

function TMyStream.ReadChars(out s: AnsiString; CONST Count: Longint): Boolean; 
begin
 SetLength(s, Count);
 Result:= Read(s[1], Count)= Count;
end;

Тест скорости

В моем тесте скорости первый подход был немного быстрее, чем второй. Я использовал файл 400 Мбайт, из которого я читал строки около 200000 раз. Процесс был установлен на высокий приоритет.

Лучшее время для чтения: 1,35 для варианта В и 1,37 для варианта А.
Средний балл:
В среднем, B забил также 20 мс лучше, чем A.

Тест повторялся 15 раз для каждого варианта.

Разница действительно маленькая. Он может попасть в диапазон ошибок измерения. Вероятно, это будет важно, если я буду читать строки чаще и из большего файла. Но на данный момент позвольте сказать, что обе строки кода выполняют одно и то же.

ANSWER
Вариант A - может быть крошечный бит быстрее Вариант B - (очевидно) гораздо легче читать, и он больше Delphi-ish. Мои предпочтения.

Примечание:
Я видел Embarcadero, используя вариант A в примере TStreamReadBuffer, но с TBytes вместо String.

4b9b3361

Ответ 1

Помните, что при запуске

1. DiskStream.Read(Pointer(s)^, Count)
2. DiskStream.Read(s[1], Count)

Версия 1. будет быстрее.

Но вы должны быть уверены, что переменная s явно локальная, или вы вызывали себя UniqueString(s) перед циклом.

Так как pointer(s)^ не вызовет скрытый RTL-вызов UniqueString?() с низким уровнем, он будет быстрее, чем s[1], , но вы можете переопределить некоторые существующие данные, если строка s переменная распределяется между текущим контекстом и другим контекстом (например, если последний контент s был получен из функции из значения свойства, или s отправляется как параметр другому методу).

На самом деле наиболее правильным способом кодирования этого чтения AnsiString из содержимого является:

  s := '';
  SetLength(s,Count);
  DiskStream.Read(pointer(s)^,Count);

или

  SetString(s,nil,Count);
  DiskStream.Read(pointer(s)^,Count);

Вторая версия равна 1-й, но с одной строкой меньше.

Настройка s to '' вызовет FreeMem()+AllocMem() вместо ReallocMem() в SetLength(), поэтому будет избегать вызова на move() и, следовательно, будет немного быстрее.

Фактически, вызов UniqueString?() RTL, сгенерированный s[1], будет очень быстрым, так как вы уже вызвали SetLength() перед его вызовом: поэтому s уже уникален, а UniqueString?() вызов RTL будет вернуться почти сразу. После профилирования разница между двумя версиями невелика: почти все время тратится на распределение строк и перемещение контента с диска. Возможно, s[1] оказывается более "паскальным".

Ответ 2

Определенно обозначение массива. Часть стиля Delphi заключается в том, чтобы сделать ваш код легко читаемым, и легче рассказать, что происходит, когда вы говорите, что именно вы делаете. Приведение строки в указатель, а затем разыменование его выглядит запутанным; зачем ты это делаешь? Это не имеет смысла, если читатель не знает много о внутренних строках.

Ответ 3

Если вы заботитесь об оптимизации, вам следует предпочесть первый вариант. Просто посмотрите на код, сгенерированный компилятором:

Unit7.pas.98: Stream.Read(Pointer(S)^, 10);
00470EA9 8B55FC           mov edx,[ebp-$04]
00470EAC B90A000000       mov ecx,$0000000a
00470EB1 8BC6             mov eax,esi
00470EB3 8B18             mov ebx,[eax]
00470EB5 FF530C           call dword ptr [ebx+$0c]

Unit7.pas.99: Stream.Read(s[1], 10);
00470EB8 8B5DFC           mov ebx,[ebp-$04]
00470EBB 85DB             test ebx,ebx
00470EBD 7418             jz $00470ed7
00470EBF 8BC3             mov eax,ebx
00470EC1 83E80A           sub eax,$0a
00470EC4 66833802         cmp word ptr [eax],$02
00470EC8 740D             jz $00470ed7
00470ECA 8D45FC           lea eax,[ebp-$04]
00470ECD 8B55FC           mov edx,[ebp-$04]
00470ED0 E8CB3FF9FF       call @InternalUStrFromLStr
00470ED5 8BD8             mov ebx,eax
00470ED7 8D45FC           lea eax,[ebp-$04]
00470EDA E89950F9FF       call @UniqueStringU
00470EDF 8BD0             mov edx,eax
00470EE1 B90A000000       mov ecx,$0000000a
00470EE6 8BC6             mov eax,esi
00470EE8 8B18             mov ebx,[eax]
00470EEA FF530C           call dword ptr [ebx+$0c]

UPDATE

Вышеприведенный код генерируется компилятором Delphi 2009. Вы можете улучшить код с помощью директивы {$ STRINGCHECKS OFF}, но у вас все еще есть служебные вызовы функции UniqueStringU:

Unit7.pas.100: Stream.Read(s[1], 10);
00470EB8 8D45FC           lea eax,[ebp-$04]
00470EBB E8B850F9FF       call @UniqueStringU
00470EC0 8BD0             mov edx,eax
00470EC2 B90A000000       mov ecx,$0000000a
00470EC7 8BC3             mov eax,ebx
00470EC9 8B18             mov ebx,[eax]
00470ECB FF530C           call dword ptr [ebx+$0c]

Ответ 4

Второй вариант определенно больше "Стиль Delphi" (если вы посмотрите на версии Delphi заголовков Windows API, вы увидите, что большинство параметров указателя были преобразованы в параметры var).

В дополнение к этому, второй вариант не нуждается в трансляции и более читаем IMHO.

Ответ 5

Я бы всегда использовал второй, который поддерживает безопасность типов. Я действительно не покупаю аргумент производительности, так как вы попали на диск в худшем случае, или в кеш-память, или в основную память, все из которых собираются сделать несколько операций с ЦП несколько тривиальными. Правильности следует уделять более высокий приоритет, чем производительность.

Тем не менее, я бы добавил, что это не то, что должно вас беспокоить слишком много, так как вы должны написать этот кусок кода один раз и только один раз. Поместите его в класс помощника и аккуратно заверните. Не стесняйтесь заботиться о оптимизации, переписывайте ее как ассемблер, что бы ни привлекало ваше воображение. Но d on't r epeat y.

Ответ 6

Если есть вероятность, что ваша функция будет вызвана с помощью Count из 0, тогда A) будет работать с Pointer(s)^, просто оценивая nil, а B) сбой при исключении проверки диапазона.

Если вы хотите использовать B) и по-прежнему обрабатывать графы 0 изящно, вы должны использовать:

function TMyStream.ReadChars(out s: AnsiString; const Count: Integer): Boolean; 
begin
 SetLength(s, Count);
 Result := (Count = 0)  or (Read(s[1], Count) = Count);
end;

Ответ 7

Второй (DiskStream.Read(s [1], Count)). Всякий раз, когда вы сталкиваетесь с нетипизированным параметром var, он читается как "берет адрес того, что передается как параметр". Таким образом, в этом случае вы передаете адрес первого символа строки s, который вы намеревались сделать.