2009-10-18 42 views
15

Convertí mi programa de Delphi 4 a Delphi 2009 hace un año, principalmente para dar el salto a Unicode, pero también para obtener los beneficios de todos esos años de mejoras de Delphi.¿Cuándo y por qué debería usar TStringBuilder?

Mi código, por supuesto, es por lo tanto todo el código heredado. Utiliza cadenas cortas que ahora se han convertido convenientemente en largas cadenas Unicode, y he cambiado todas las antiguas funciones ANSI al nuevo equivalente.

Pero con Delphi 2009, introdujeron la clase TStringBuilder, presumiblemente modelada según la clase StringBuilder de .NET.

Mi programa realiza una gran cantidad de manejo y manipulación de cadenas, y puede cargar cientos de megabytes de cadenas grandes en la memoria a la vez para trabajar.

No sé mucho sobre la implementación de Delphi de TStringBuilder, pero escuché que algunas de sus operaciones son más rápidas que las operaciones de cadena por defecto.

Mi pregunta es si vale la pena o no hacer el esfuerzo y convertir mis cadenas estándar para usar la clase TStringBuilder. ¿Qué ganaría y perdería al hacer eso?


Gracias por sus respuestas y que me lleva a mi conclusión, que es no molestar a menos que se requiera la compatibilidad .NET.

en su blog el Delphi 2009 String Performance, Jolyon Smith states:

Pero parece a mí como si TStringBuilder existe principalmente como un accesorio de compatibilidad .NET, en lugar de proporcionar un beneficio real para los desarrolladores de aplicaciones Win32, con la posible excepción de los desarrolladores que desean o necesitan una fuente única de una base de código Win32/.NET donde el rendimiento de manejo de cadenas no es una preocupación.

+0

Simplemente curioso si se dio cuenta de un cambio en el rendimiento cadena cuando se pasó de shortstrings D4 a cadenas Unicode D2009? – LachlanG

+2

No hice sincronizaciones directamente en cadenas, pero mi actualización resultó en una mejora del 25% en el rendimiento del código, probablemente debido a FastMM y otras optimizaciones integradas en la nueva versión. Los archivos ANSI externos tenían que estar codificados en Unicode, lo que requiere el doble de espacio y esto agrega una sobrecarga importante al programa para archivos muy grandes, lo que revierte la mejora del rendimiento que se observa en los archivos pequeños. El bloqueo en buffers muy grandes redujo el burdon. En general, creo que mi programa probablemente sea tan rápido como antes, pero con el gran beneficio de Unicode. – lkessler

+0

Gracias eso es muy interesante. – LachlanG

Respuesta

13

Según mi leal saber y entender, TStringBuilder se introdujo solo por cierta paridad con .NET y Java, parece ser más un que la función de tipo que cualquier avance importante.

El consenso parece ser que TStringBuilder es más rápido en algunas operaciones pero más lento en otras.

Su programa parece ser interesante para hacer una comparación antes/después de TStringBuilder pero no lo haría más que como ejercicio académico.

7

Según Marco Cantu no es por la velocidad, pero es posible que obtenga un código más limpio y una mejor compatibilidad del código con .Net. Here (y algunas correcciones here) otra prueba de velocidad con TStringBuilder no es más rápido.

+5

Gracias por los enlaces. Ellos ayudaron. Pero no puedo estar de acuerdo con usted en que StringBuilder le da un código más limpio. Definitivamente me gusta: s: = s + s2; mejor que: SB.Append (s2); – lkessler

+0

Del artículo de Marco Cantu, creo que quiso decir al agregar varios tipos de datos. Todavía no es una gran diferencia, por supuesto. – stg

+1

Esta es la prueba de concat de cadena de Cantu: "para i: = 1 a 15 do s: = s + 'xxx';" Eso no es una gran prueba. El bucle for debe ser más grande. Apuesto a que TStringBuilder ganaría en ese caso. No puedo probarlo desafortunadamente porque no tengo D2009. En términos generales, ese patrón es lento en la mayoría de los lenguajes porque las cadenas tienen que reasignarse y copiarse continuamente. –

6

TStringBuilder es básicamente una característica de me-too, como dijo LachlanG. Es necesario en .NET porque las cadenas CLR son inmutables, pero Delphi no tiene ese problema, por lo que no es necesario un generador de cadenas como solución temporal.

8

TStringBuilder se introdujo exclusivamente para proporcionar un mecanismo compatible con el código fuente de las aplicaciones para llevar a cabo el manejo de cadena en Delphi y Delphi.NET. Usted sacrifica algo de velocidad en Delphi por algunos beneficios potencialmente significativos en Delphi.NET

El StringBuilder concepto en .NET Soluciona problemas de rendimiento con la aplicación de cadena en esa plataforma, las cuestiones que el Delphi (código nativo) plataforma, simplemente no tiene.

Si no está escribiendo código que necesita ser compilado por tanto el código nativo y Delphi.NET entonces simplemente no hay razón para usar TStringBuilder.

+2

No es cierto que se introdujo únicamente para la compatibilidad del código fuente. Eso fue parte de esto, pero otra razón importante es que es una clase poderosa de usar, y porque algunas personas prefieren su capacidad de hacer un patrón de codificación fluido. En pocas palabras: úselo si lo desea, no lo use si no lo desea. –

+1

Genuinamente curioso: "Poderoso" ¿cómo? ¿Por qué? En cuanto a fluidez? Sí, bueno, como dices, úsalo si lo deseas, no lo uses si no quieres (o si te gusta aferrarte a tu cordura al depurar ). – Deltics

+0

@Deltics: puede ser cierto que TStringBuilder se introdujo únicamente para compatibilidad con .NET, pero supongo que eso fue aproximadamente el 95% del motivo ;-) ¿Cuáles son las probabilidades de que TStringBuilder se haya introducido si Delphi.NET requisito nunca había existido? – IanH

11

Básicamente, utilizo estos modismos para construir cadenas. Las diferencias más importantes son:

Para los patrones complejos de construcción, la primera crea el código mucho más limpio, la segunda sólo si añado líneas y con frecuencia incluye muchas de las llamadas Format.

El tercero hace que mi código sea más claro cuando los patrones de formato son importantes.

Uso el último solo cuando la expresión es muy simple.

algunas diferencias más entre las dos primeras expresiones idiomáticas:

  • TStringBuilder tiene muchas sobrecargas de Append, y también tiene AppendLine (con sólo dos sobrecargas) si desea añadir líneas como TStringList.Add puede
  • TStringBuilder reasigna el búfer subyacente con un esquema de sobrecapacidad, lo que significa que con grandes búferes y anexos frecuentes, puede ser mucho más rápido que TStringList
  • Para obtener el TStringBuilder conte nt, tienes que llamar al método ToString que puede ralentizar las cosas.

Entonces: la velocidad no es lo más importante para elegir la fraseología que se agrega. El código legible es

8

Intenté mejorar una rutina anterior que estaba analizando un archivo de texto (1.5GB). La rutina fue bastante tonta y estaba construyendo una cadena como esta: Result:= Result+ buff[i];

Así que pensé que TStringBuilder agregaría mejoras de velocidad significativas. Resultó que el código "tonto" era en realidad un 114% más rápido que la versión "mejorada" con TStringBuilder.

Por lo tanto, crear una cadena de caracteres NO es un lugar donde se puede obtener una mejora de velocidad con TStringBuilder.


Mi StringBuilder (abajo) es 184.82 veces (sí !!!!!! 184) más rápido que el clásico s: = s + CHR. (Experimento en una cadena de 4 MB)

Classic s: = S + c
horarias: 8502 ms

procedure TfrmTester.btnClassicClick(Sender: TObject); 
VAR 
    s: string; 
    FileBody: string; 
    c: Cardinal; 
    i: Integer; 
begin 
FileBody:= ReadFile(File4MB); 
c:= GetTickCount; 

for i:= 1 to Length(FileBody) DO 
    s:= s+ FileBody[i]; 

Log.Lines.Add('Time: '+ IntToStr(GetTickCount-c) + 'ms');  // 8502 ms 
end; 

amortiguada previamente
Tiempo:
BuffSize = 10000; // 10k buffer = 406ms BuffSize = 100000; // 100k buffer = 140ms BuffSize = 1000000; // 1M buffer = 46ms

Código:
procedure TfrmTester.btnBufferedClick (Sender: TObject); VAR s: cadena; FileBody: cadena; c: Cardinal; CurBuffLen, marcador, i: Entero; begin FileBody: = ReadFile (File4MB); c: = GetTickCount;

marker:= 1; 
CurBuffLen:= 0; 
for i:= 1 to Length(FileBody) DO 
    begin 
    if i > CurBuffLen then 
    begin 
    SetLength(s, CurBuffLen+ BuffSize); 
    CurBuffLen:= Length(s) 
    end; 
    s[marker]:= FileBody[i]; 
    Inc(marker); 
    end; 

SetLength(s, marker-1); { Cut down the prealocated buffer that we haven't used } 

Log.Lines.Add('Time: '+ IntToStr(GetTickCount-c) + 'ms'); 
if s <> FileBody 
then Log.Lines.Add('FAILED!'); 
end; 

amortiguada previamente, como clase
Tiempo:
BuffSize = 10000; // 10k buffer = 437ms
BuffSize = 100000; // 100k buffer = 187ms
BuffSize = 1000000; // 1M buffer = 78ms

Código:
procedimiento TfrmTester.btnBuffClassClick (Sender: TObject); VAR StringBuff: TCStringBuff; s: cadena; FileBody: cadena; c: Cardinal; i: Entero; begin FileBody: = ReadFile (File4MB); c: = GetTickCount;

StringBuff:= TCStringBuff.Create(BuffSize); 
TRY 
    for i:= 1 to Length(FileBody) DO 
    StringBuff.AddChar(filebody[i]); 
    s:= StringBuff.GetResult; 
FINALLY 
    FreeAndNil(StringBuff); 
END; 

Log.Lines.Add('Time: '+ IntToStr(GetTickCount-c) + 'ms'); 
if s <> FileBody 
then Log.Lines.Add('FAILED!'); 
end; 

Y esta es la clase:

{ TCStringBuff } 

constructor TCStringBuff.Create(aBuffSize: Integer= 10000); 
begin 
BuffSize:= aBuffSize; 
marker:= 1; 
CurBuffLen:= 0; 
inp:= 1; 
end; 


function TCStringBuff.GetResult: string; 
begin 
SetLength(s, marker-1);     { Cut down the prealocated buffer that we haven't used } 
Result:= s; 
s:= '';   { Free memory } 
end; 


procedure TCStringBuff.AddChar(Ch: Char); 
begin 
if inp > CurBuffLen then 
    begin 
    SetLength(s, CurBuffLen+ BuffSize); 
    CurBuffLen:= Length(s) 
    end; 

s[marker]:= Ch; 
Inc(marker); 
Inc(inp); 
end; 

Conclusión: Deje de usar s: = s + c si tiene grandes (más de 10K) cuerdas. Puede ser cierto incluso si tiene cadenas pequeñas, pero lo hace a menudo (por ejemplo, tiene una función que está procesando cadenas en una cadena pequeña, pero la llama con frecuencia). _

PS: También puede ver esto: https://www.delphitools.info/2013/10/30/efficient-string-building-in-delphi/2/

+1

Gracias por agregar esta respuesta tardía. Es un excelente artículo al que enlazas. – lkessler

Cuestiones relacionadas