Ya hay muchos buenos enfoques en las respuestas proporcionadas ( aquí , aquí y aquí ). Si la velocidad es lo que busca principalmente , definitivamente debería considerar hacer el trabajo a través de la API C de Lua, que es muchas veces más rápida que el código Lua sin formato. Cuando se trabaja con fragmentos precargados (por ejemplo , función de carga ), la diferencia no es tan grande, pero sigue siendo considerable.
En cuanto a las soluciones Lua puras , permítanme compartirles este pequeño punto de referencia que hice. Cubre todas las respuestas proporcionadas hasta esta fecha y agrega algunas optimizaciones. Aún así, lo básico a considerar es:
¿Cuántas veces necesitará iterar sobre caracteres en una cadena?
- Si la respuesta es "una vez", entonces debería buscar la primera parte del banchmark ("velocidad bruta").
- De lo contrario, la segunda parte proporcionará una estimación más precisa, porque analiza la cadena en la tabla, que es mucho más rápida de iterar. También debería considerar escribir una función simple para esto, como sugirió @Jarriz.
Aquí está el código completo:
local str = "Hello World!"
local attempts = 5000000
local reuses = 10
local x, c, elapsed, tbl
local stringbyte, stringchar, stringsub, stringgsub, stringgmatch = string.byte, string.char, string.sub, string.gsub, string.gmatch
print("-----------------------")
print("Raw speed:")
print("-----------------------")
x = os.clock()
for j = 1, attempts do
for i = 1, #str do
c = stringsub(str, i)
end
end
elapsed = os.clock() - x
print(string.format("V1: elapsed time: %.3f", elapsed))
x = os.clock()
for j = 1, attempts do
for c in stringgmatch(str, ".") do end
end
elapsed = os.clock() - x
print(string.format("V2: elapsed time: %.3f", elapsed))
x = os.clock()
for j = 1, attempts do
stringgsub(str, ".", function(c) end)
end
elapsed = os.clock() - x
print(string.format("V3: elapsed time: %.3f", elapsed))
local str2table = function(str)
local ret = {}
for i = 1, #str do
ret[i] = stringsub(str, i)
end
return ret
end
x = os.clock()
for j = 1, attempts do
tbl = str2table(str)
for i = 1, #tbl do
c = tbl[i]
end
end
elapsed = os.clock() - x
print(string.format("V4: elapsed time: %.3f", elapsed))
x = os.clock()
for j = 1, attempts do
tbl = {stringbyte(str, 1, #str)}
for i = 1, #tbl do
c = tbl[i]
end
end
elapsed = os.clock() - x
print(string.format("V5: elapsed time: %.3f", elapsed))
x = os.clock()
for j = 1, attempts do
tbl = {stringbyte(str, 1, #str)}
for i = 1, #tbl do
c = stringchar(tbl[i])
end
end
elapsed = os.clock() - x
print(string.format("V5b: elapsed time: %.3f", elapsed))
print("-----------------------")
print("Creating cache table ("..reuses.." reuses):")
print("-----------------------")
x = os.clock()
for k = 1, attempts do
tbl = {}
for i = 1, #str do
tbl[i] = stringsub(str, i)
end
for j = 1, reuses do
for i = 1, #tbl do
c = tbl[i]
end
end
end
elapsed = os.clock() - x
print(string.format("V1: elapsed time: %.3f", elapsed))
x = os.clock()
for k = 1, attempts do
tbl = {}
local tblc = 1
for c in stringgmatch(str, ".") do
tbl[tblc] = c
tblc = tblc + 1
end
for j = 1, reuses do
for i = 1, #tbl do
c = tbl[i]
end
end
end
elapsed = os.clock() - x
print(string.format("V2: elapsed time: %.3f", elapsed))
x = os.clock()
for k = 1, attempts do
tbl = {}
local tblc = 1
stringgsub(str, ".", function(c)
tbl[tblc] = c
tblc = tblc + 1
end)
for j = 1, reuses do
for i = 1, #tbl do
c = tbl[i]
end
end
end
elapsed = os.clock() - x
print(string.format("V3: elapsed time: %.3f", elapsed))
x = os.clock()
for k = 1, attempts do
tbl = str2table(str)
for j = 1, reuses do
for i = 1, #tbl do
c = tbl[i]
end
end
end
elapsed = os.clock() - x
print(string.format("V4: elapsed time: %.3f", elapsed))
x = os.clock()
for k = 1, attempts do
tbl = {stringbyte(str,1,#str)}
for j = 1, reuses do
for i = 1, #tbl do
c = tbl[i]
end
end
end
elapsed = os.clock() - x
print(string.format("V5: elapsed time: %.3f", elapsed))
x = os.clock()
for k = 1, attempts do
tbl = {stringbyte(str, 1, #str)}
for i = 1, #tbl do
tbl[i] = stringchar(tbl[i])
end
for j = 1, reuses do
for i = 1, #tbl do
c = tbl[i]
end
end
end
elapsed = os.clock() - x
print(string.format("V5b: elapsed time: %.3f", elapsed))
Salida de ejemplo (Lua 5.3.4, Windows) :
Raw speed:
V1: elapsed time: 3.713
V2: elapsed time: 5.089
V3: elapsed time: 5.222
V4: elapsed time: 4.066
V5: elapsed time: 2.627
V5b: elapsed time: 3.627
Creating cache table (10 reuses):
V1: elapsed time: 20.381
V2: elapsed time: 23.913
V3: elapsed time: 25.221
V4: elapsed time: 20.551
V5: elapsed time: 13.473
V5b: elapsed time: 18.046
Resultado:
En mi caso, los string.byte
y string.sub
fueron los más rápidos en términos de velocidad bruta. Al usar la tabla de caché y reutilizarla 10 veces por ciclo, la string.byte
versión fue la más rápida incluso al convertir los códigos de caracteres de nuevo a caracteres (lo que no siempre es necesario y depende del uso).
Como probablemente haya notado, hice algunas suposiciones basadas en mis puntos de referencia anteriores y las apliqué al código:
- Las funciones de la biblioteca siempre deben estar localizadas si se usan dentro de bucles, porque es mucho más rápido.
- Insertar un nuevo elemento en la tabla lua es mucho más rápido usando
tbl[idx] = value
que table.insert(tbl, value)
.
- Recorrer la tabla usando
for i = 1, #tbl
es un poco más rápido que for k, v in pairs(tbl)
.
- Prefiera siempre la versión con menos llamadas a funciones, porque la llamada en sí aumenta un poco el tiempo de ejecución.
Espero eso ayude.