Visual Basic Foro
Programación => Visual Basic 6 => Mensaje iniciado por: seba123neo en Septiembre 17, 2011, 04:02:23 pm
-
Hola, este post es simplemente para mostrarles algo que seguro muchos ya saben, pero para los que no pues aca lo tienen.
El tema de la concatenacion de Strings saben que al concatenar los strings en un bucle haciendo algo como:
For i = 0 To 100000
vSQL = vSQL & "a"
Next i
la performance en bucles largos se pierde ya que este metodo crea una nueva string en cada iteracion y el tiempo que tarda en terminar es mucho.
esto como sabran en .NET se soluciona usando la clase StringBuilder() que es rapidisima.
y bueno si estan buscando algo como el StringBuilder() de .NET en visual basic 6, tenemos la clase cStringBuilder de vbAccelerator
Link descarga cStringBuilder: StringBuilder Class for VB (http://www.vbaccelerator.com/home/VB/Code/Techniques/StringBuilder/String_Builder_Class_and_Demonstration.asp)
tiene los mismo metodos clasicos de .NET, como Append() para agregar el string, .Clear y ToString para mostrar el string final, mas algunos metodos mas. usa la api CopyMemory para realizar el trabajo.
y el resultado es abismalmente mejor que la con la concatenacion con &.
aca un ejemplo simple usando un bucle de 100 mil iteraciones. y con la clase cTiming se toma el tiempo.
Dim oSQL As cStringBuilder
Dim oTiming As CTiming
Dim i As Long
Dim vSQL As String
Private Sub Command1_Click()
oTiming.Reset
For i = 0 To 100000
oSQL.Append "a"
Next i
Text1.Text = Text1.Text & oTiming.Elapsed & vbNewLine
oSQL.Clear
End Sub
Private Sub Command2_Click()
oTiming.Reset
For i = 0 To 100000
vSQL = vSQL & "a"
Next i
Text1.Text = Text1.Text & oTiming.Elapsed & vbNewLine
vSQL = ""
End Sub
Private Sub Form_Load()
Set oSQL = New cStringBuilder
Set oTiming = New CTiming
End Sub
el resultado compilado es el siguiente:
String: entre 4.4 y 4.5 segundos
StringBuilder: 0.054 segundos y a veces 0.020
o sea entre 20 y 50 milisegundos, mientras que el otro 4500 milisegundos, lo que seria entre 90 y 100 veces mas rapido, y mientras mas grande se haga el bucle mas rapido sera y el string casi no termina mas.
saludos.
-
No he podido hacerlo arrancar.
Supongo que la clase es el codigo ubicado aqui (http://www.vbaccelerator.com/home/vb/code/techniques/StringBuilder/String_Builder_Class_and_Demonstration_zip_cStringBuilder_cls.asp) , pero creo que algo me falta porque se me cae en
Dim oSQL As cStringBuilder y de hecho se cae en las siguientes lineas. El mensaje de error es:
"No se ha definido el tipo definido por el usuario"
Trate de definirlo pero no la chunto. :-[
-
LE pusiste el nombre "cStringBuilder" a la clase ?
-
No le habia puesto, sorry :-[
Ya se lo puse, lo arranco ahora me salta en la siguiente linea:
Dim oTiming As CTiming
Y me sale el mismo error. Sorry no logro cazar esa parte... también.
-
Hola Yvan, la calse CTiming es solo para testear la velocidad pero no es necesaria, la verdad esta piola la clase cStringBuilder, de todas formas si se sabe el tamaño de lo que se quiere construir o es la misma cadena que se repite es mas rapido utilizando la función Mid$ del vb, la clase cStringBuilder es apropiada para construcciones sql, html y cosas asi.
Saludos.
-
la clase cTiming se baja de aca:
cTiming (http://www.xbeat.net/vbspeed/download/CTiming.zip)
-
Muchas gracias!!! Ahora si lo testee.
Gracias Lea por ubicarme.
-
Muy interesante la verdad, pero creo que quedaría mejor si se pusieran más test para ver como se comporta añadiendo cadenas largas.
DoEvents! :P
-
El rendimiento es superior al de concatenar de manera "nativa" sin importar el tamaño de las cadenas simplemente por la mecanica que utiliza.
-
el rendimiento es impresionante, prueben hacer esto....agregar cadenas largas, por ejemplo yo lo uso para hacer INSERTS en mysql de la forma que se llama "BATCH INSERT" (algo parecido al bulk insert de sql server) que son los inserts seguidos de los valores separados por comas.
por ejemplo un insert comun:
INSERT INTO Tabla (campo1,campo2) VALUES ('1', 'prueba')
si necesito insertar miles de registros, debo hacer miles de inserts, pero con batch es asi.
INSERT INTO Tabla (campo1,campo2) VALUES ('1', 'prueba'), ('2', 'prueba'),('3', 'prueba'),('4', 'prueba'),('5', 'prueba')
entonces esa concatenación dinamica si lo hacen con & , es super lenta, si el sistema tiene una tabla con 40 o mas campos, imaginense el string que se va generando, el & llega a un punto en el que va cada ves mas lento y llega a tardar MINUTOS, en cambio con este metodo no tarda ni un segundo, UNA BESTIA.
saludos.
-
Lo acabo de usar para un migrador de Access a MySQL que pase 1millon (aprox) de registros de 50 en 50... y la verdad que vuela :3
-
si yo tambien lo hago cada tantos registros, en mi caso mil, porque si son cientos de miles (por ejemplo 300 mil), hacer un insert de 300 mil a la vez puede ser mas lento que ir insertando de a pequeñas cantidades, yo de a mil no tengo problema, una importacion que antes tardaba mas de un minuto, ahora con este metodo tarda 10 segundos.
-
Esta muy interesante pero estoy perdido en como usarlo :-[ alguien podría poner un ejemplo ya con una base de datos y que se pueda hacer insert ? ;D
-
Echad un vistazo a esto a ver que os parece: http://leandroascierto.com/foro/index.php?topic=1286.0
DoEvents! :P
-
no avise antes, pero habia encontrado una clase mucho mas veloz, la de vbacelerator es lenta. haber si pongo un ejemplo para que vean.
saludos.
-
aca les paso la clase que yo decia, que es mucho mas rapida que la de vbaccelerator y la que estoy usando actualmente.
Option Explicit
Option Compare Binary
Private Declare Sub RtlMoveMemory Lib "kernel32" (dst As Long, src As Long, ByVal nBytes&)
Private Declare Function SysAllocStringByteLen& Lib "oleaut32" (ByVal oleStr&, ByVal BLen&)
Private plngStringLen As Long
Private plngBufferLen As Long
Private pstrBuffer As String
Public Sub Append(Text As String)
Dim lngText As Long
Dim strTemp As String
Dim lngVPointr As Long
lngText = LenB(Text) \ 2&
If lngText > 0& Then
If (plngStringLen + lngText) > plngBufferLen Then
plngBufferLen = (plngStringLen + lngText) * 2&
'*** alternative strTemp = Space$(plngBufferLen)
RtlMoveMemory ByVal VarPtr(strTemp), SysAllocStringByteLen(0&, plngBufferLen + plngBufferLen), 4&
'strTemp = AllocString04(plngBufferLen)
'*** alternative Mid$(strTemp, 1&) = pstrBuffer
RtlMoveMemory ByVal (StrPtr(strTemp)), ByVal (StrPtr(pstrBuffer)), LenB(pstrBuffer)
'*** alternative pstrBuffer = strTemp
'*** (switch pointers)
lngVPointr = StrPtr(pstrBuffer)
RtlMoveMemory ByVal VarPtr(pstrBuffer), ByVal VarPtr(strTemp), 4&
RtlMoveMemory ByVal VarPtr(strTemp), lngVPointr, 4&
Debug.Print "plngBufferLen: " & plngBufferLen
End If
'*** Alternative Mid$(pstrBuffer, plngStringLen + 1&) = Text
RtlMoveMemory ByVal (StrPtr(pstrBuffer) + plngStringLen + plngStringLen), _
ByVal (StrPtr(Text)), lngText + lngText
plngStringLen = plngStringLen + lngText
End If
End Sub
Public Function Value() As String
'*** alternative Value = Left$(pstrBuffer, plngStringLen)
RtlMoveMemory ByVal VarPtr(Value), SysAllocStringByteLen(0&, plngStringLen + plngStringLen), 4&
RtlMoveMemory ByVal (StrPtr(Value)), _
ByVal (StrPtr(pstrBuffer)), plngStringLen + plngStringLen
End Function
Private Function AllocString04(ByVal lSize As Long) As String
' http://www.xbeat.net/vbspeed/
' by Jory, jory@joryanick.com, 20011023
RtlMoveMemory ByVal VarPtr(AllocString04), SysAllocStringByteLen(0&, lSize + lSize), 4&
End Function
Public Sub Clear()
'*** do not clear the buffer to save allocation time
'*** if you use the function multiple times
'*** and you have plenty of RAM.
plngStringLen = 0&
End Sub
Public Sub ClearBuffer()
'*** free memory
plngStringLen = 0&
plngBufferLen = 0& 'clear the buffer
pstrBuffer = vbNullString 'clear the buffer
End Sub
Private Sub Class_Initialize()
'*** paranoid
ClearBuffer
End Sub
la he comparado compilado con la tuya Psyke1, y es hasta 20 veces mas rapida, en un bucle de 100 mil iteraciones (con una cadena bastante larga) la tuya tarda mas de 2 segundos, mientras esta tarda 0.1 segundos
saludos.
-
naaaaaaa....
Lo prouebo y edito
-
@seba123neo
Aún se me ocurre como optimizarla más...
Se agradece que pongas todo esto, ayuda mucho. :)
EDIT:
Por cierto, creo que no hiciste las pruebas con mi nueva clase, mira, te dejo un test que hice con 100.000 vueltas y una cadena larga.
Un test preciso con CTiming.cls:
Option Explicit
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Sub Form_Load()
Const LOOPS As Long = 100000
Dim sTest As String
Dim cConc As New cConcatenator
Dim cSBldr As New cStringBuilder '// Es la nueva clase que pusiste, a pesar de que no cambié el nombre.
Dim t As New CTiming
Dim x As Long
Dim s As String
If App.LogMode = 0 Then End
sTest = String$(1000, "p")
Me.AutoRedraw = True
Me.Print "Test adding string"; LOOPS; "times:"
t.Reset
For x = 1 To LOOPS
cSBldr.Append sTest
Next
Me.Print "cStringBuilder - ", t.sElapsed
Sleep 100
t.Reset
For x = 1 To LOOPS
cConc.AddStr sTest
Next
Me.Print "cConcatenator - ", t.sElapsed
Set cSBldr = Nothing
Set cConc = Nothing
Set t = Nothing
End Sub
Compilado, me devuelve esto:
(http://img534.imageshack.us/img534/4638/carreteraeneldesierto10c.jpg)
DoEvents! :P
-
me fijo ahora..........
@Psyke1: Usas Win 98?
-
mira he probado ese eejmplo y saltan errores en ambas clases, y tengo todas las clases puestas, salta error en memoria insuficiente, es mejor subir los proyectos en estos casos, yo voy a subir el que probe hoy.
-
aca subi el ejemplo, fijate la diferencia que hay.
http://www.mediafire.com/download.php?ydrkfay0xj67j8e (http://www.mediafire.com/download.php?ydrkfay0xj67j8e)
saludos.
-
Dios mio, esto lo añadiré a mis Expedientes X. ;D
-Descargo el proyecto
-Compilo
-Ejecuto
-Doy primero a el botón cStringBuilder y después al cConcatenator.
Me devuelve esto:
(http://img256.imageshack.us/img256/5811/carreteraeneldesierto10.jpg)
(Resultados similares en pruebas posteriores)
Sería bueno que la gente suba sus pruebas para salir de dudas.
Por cierto, se me acaba de ocurrir una manera un poco más compleja, pero segurísimo mucho más rápida que las expuestas hasta ahora. :D
DoEvents! :P
-
es raro, en la otra pc que era un windows xp, dual core, nada raro, la tuyas tarda mas de 2 segundos compilado..pero en serio...
ahora acabo de probar en mi PC que es una I7, con windows 7 64 bits, arroja lo que vos mostras, pero es aleatorio, o sea si apretas varias veces, a veces tarda mas una que la otra, pero basicamente tardan lo mismo.
me gustaria saber si a alguno les pasa que le tarda "segundos" como me paso a mi.
saludos.
-
mi resultado:
(http://i.minus.com/jC1h1HeEWT69F.png) (http://min.us/lC1h1HeEWT69F)
probe aproximadamente 20 veces, sin cuelgues...
-
(http://i53.tinypic.com/152yhqe.jpg)
en el orden de los botones :)
:) dual core (de las primeras a 1.6ghz) con 2gb ram :P
-
raul me podes decir el resultado con el bucle en 1 millon ?
-
De acuerdo al orden del botón
(http://enterpy.net/upload06/archivossubidos/trh5a_string.jpg)
el cConcatenador siempre un promedio de 80.
Win7 64Bits Intel i7 3.06 4 GB de RAM DDR3-2000 Corsair
-
hice otras pruebas, variando la comprobacion de limites del array (en propiedades de compilacion), pero los resultados son muy similares.
tambien probe poner en prioridad tiempo real, y es interesante lo que pasa:
(http://i.minus.com/jbxBc7TPn1n7iR.png) (http://min.us/lbxBc7TPn1n7iR)
los primeros 2 items, son del primer test, en prioridad normal.
los demases, en prioridad Tiempo Real (el sistema se colgaba hasta que terminase la funcion)
PC: AMD Athlon 5600 x2 2.9GHz, 4GB DDR2 800MHz dual channel, W7 x64
-
coco me podes decir cuanto tarda si pones 1 millon en el bucle ? o sea bajas el ejemplo que subi, le cambias a 1 millon y compilas.
-
@seba:
con el codigo que propones vos (reemplazando el de la clase de psyke), tenemos lo siguiente:
1millon de copias
1861,7903296 <- string builder
4341,2279696 <- concatenator
-
Al compilar y ponerle 1 millon me da estos, de acuerdo al orden del boton.
(http://enterpy.net/upload06/archivossubidos/wftcb_string-2.jpg)
-
1904,9929777759
5368,62144983104
1944,91860443481
5343,09827969945
:D con 10.000.000 de vueltas, el cStringBuilder falla al segundo, y cConcatenator falla despues de un tieeeempo :P
-
@raul338: como haces??? aca ni con OC y nitrogeno liquido logro eso
me pasa que con el string builder y 10millones de pasadas, explota el soft (error groso con el cartel de windos)... pero con el concatenator, tarda sus buenos segundos, y aparece el error de vb Memoria insuficiente...
auqnue puede ser random, como dijo seba
-
el FAIL, cambie un solo bucle, ahora arreglo x'D
-
@raul338: como haces??? aca ni con OC y nitrogeno liquido logro eso
me pasa que con el string builder y 10millones de pasadas, explota el soft (error groso con el cartel de windos)... pero con el concatenator, tarda sus buenos segundos, y aparece el error de vb Memoria insuficiente...
auqnue puede ser random, como dijo seba
si a mi tambien me crashea y compilado sale el cartel de windows. pero queria estar seguro de eso, de que a mayor cadenas es mejor el cStringBuilder, y a menos son casi iguales, a mi te juro y pudo poner un video que a veces tardan lo mismo y otras veces (muy raras) gana el cStringBuilder.
saludos.
-
Bueno, creo que ha quedado claro que por ahora para cadenas largas es mejor el StringBuilder. :'(
Tengo una nueva idea a ver que tal... ;)
Por cierto seba: ¿Dónde encontraste esa clase?
Me interesa mucho. :D
DoEvents! :P
-
me tope con esta clase cuando estaba buscando un parseador de JSON para vb, y el proyecto que baje tenia esta clase, pero si buscas la api que usa, la encontras en varios lados. yo la tengo usando ya en un proyecto que importa miles de registros y anda impresionante.
pero esperen, para los que les gusta mas todavia, encontre otra que tarda LA MITAD de lo que tarda esta clase...igualmente yo ya lo tengo usando con esta calse y no la voy a cambiar por esta que es mas rapida, no usa api, usa MidB$.
despues la publico con resultados.
-
No quiero ser pesado... pero encontré la manera de superar a StringBuilder incluso con bucles laaargos.
Descarga del proyecto con mi clase actualizada:
http://www.mediafire.com/?8y83xtdg29p3en3
Resultado (1.000.000 vueltas), le saco casi la mitad de tiempo.
(http://img171.imageshack.us/img171/5811/carreteraeneldesierto10.jpg)
DoEvents! :P
-
Super rapido es todo es a la mitad del tiempo, De acuerdo al orden del boton.
668,798750618205
360,106002695255
632,348110103271
366,55267136063
629,386064110521
364,428015312464
-
barbaro, aca publico la que dije que tambien le saca la mitad a la cStringuilder sin usar api, usando MidB$ (se llama cFastCat), tambien hice test con las 3 clases, 5 repeticiones cada una.
en verdad la nueva cConcatenator de Psyke1 le saca casi la mitad a cStringBuilder, pero esta nueva cFastCat le saca casi la mitad a cConcatenator.
(http://img5.imageshack.us/img5/1407/sinttuloif.png)
el link de la clase cFastCat, esta en un proyecto de PSC que compara las diferentes formas de concatenacion:
Fastest String Concatenation Yet In Visual Basic (http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=37141&lngWId=1)
saludos.
-
Que interesante seba!
Pero no me doy por vencido, mañana pruebo otra cosa... hmmmm
DoEvents! :P
-
HOLA!!!
Perdon por revivir...
Aca arme un codigo con esa idea de MidB, despues me dicen que tan rapido le fue?
Private Memoria As String
Private Capacidad As Long
Private TAM As Long
Private Sub Class_Initialize()
Capacidad = 65536
Memoria = Space$(65536)
TAM = 0
End Sub
Public Sub Agregar(ByRef STR As String)
Dim actTAM As Long
actTAM = LenB(STR) / 2
Do While actTAM > Capacidad - TAM
Memoria = Memoria & Space$(Capacidad)
Capacidad = Capacidad * 2
Loop
MidB$(Memoria, TAM + TAM + 2) = STR
TAM = TAM + actTAM
End Sub
Public Property Get CerrarSTR() As String
CerrarSTR = LeftB$(Memoria, TAM + TAM)
Call Class_Initialize
Debug.Print "Se ha borrado el string viejo y creado uno en blanco"
End Property
Public Property Get TamañoACT() As Long
TamanoACT = TAM
End Property
GRACIAS POR LEER!!!
-
Perdon por revivir este post, pero ya paso mucho tiempo y los links no funcionan :(
Alguien me puede pasar el codigo de las clases que se hablaba en el hilo?
Muchas gracias
-
Perdon por revivir este post, pero ya paso mucho tiempo y los links no funcionan :(
Alguien me puede pasar el codigo de las clases que se hablaba en el hilo?
Muchas gracias
ya corregi el link. gracias por avisar. 8)
-
Hola Seba, hice pruebas y llegué a la conclusion que las diferentes clases, o formas de concatenar, gana una u otra segun el largo.
string a concatenar: 'http://vbaccelerator.com'
valores en mSeg
Veces Std de VB VbAcc otra (movememory)
100 0/1 0 8
1000 11 1 12
10000 380 22 25
100000 -- 1045 60
Conclusion mia, como creo que nunca voy a concatenar mas de 1000 veces para mi gana la de VbAccelerator