Sigo jugando con las tendencias de Twitter. Tengo suficientes datos recopilados que me gustaría acelerar el procesamiento. Quiero asignar un índice entero a cada palabra de tendencia mediante una matriz asociativa indexada con la palabra de tendencia. A continuación, hay tres técnicas para agregar una palabra nueva a la tabla u obtener el índice de una palabra antigua.
El primero usa TRY para capturar la excepción cuando una palabra no está en la tabla. Es el más rápido si la palabra está presente y el más lento (con diferencia) si ocurre la excepción.
El segundo usa CONTAINS para probar si la palabra está presente, luego la agrega o la busca. Es el más rápido para sumar y el más lento para buscar (porque busca dos veces).
El último utiliza un valor predeterminado de matriz asociativa para ver si el valor recuperado ya existe. Proporciona una buena velocidad de compromiso.
Todos los ejemplos tienen un bucle "t" que se ejecuta dos veces. La primera vez que la matriz xN está vacía y debe completarse. La segunda vez todos los valores ya estarán presentes.
// Three ways to build a lookup table. No clear winner!
// Goal: assign an integer, starting at 1, to each new
// entry. For timing purposes, use the integer as the
// key and the value. The key will be a string in real
// code. The value will be used as an index elsewhere.
explain = {"create new entry","use existing entry"};
N = 1e5; // several seconds at 1e5
/*
Use TRY to determine when an entry needs to be created.
On the "catch" create the entry. If there is no catch,
the value is already fetched. The "t" loop runs 20X
slower the first time because the "catch" is expensive.
But the second time the "t" loop is the fastest solution.
*/
x1 = [=> ];
For( t = 1, t <= 2, t += 1,
start = HP Time();
For( ix = 1, ix < N, ix += 1,
Try( y = x1[ix], y = (x1[ix] = ix) )
);
stop = HP Time();
Write( Eval Insert( "\!nTry method: ^char((stop - start)/1e6,5,3)^ seconds ^explain[t]^" ) );
);
/*
Use CONTAINS to determine if the entry needs to be created.
Use the if's "then" clause to fetch the existing index or
"else" clause to create a new entry. The "t" loop has the
fastest speed on the first run, by far, and the slowest
speed on the second run.
*/
x2 = [=> ];
For( t = 1, t <= 2, t += 1,
start = HP Time();
For( ix = 1, ix < N, ix += 1,
If( Contains( x2, ix ),
y = x2[ix],
y = (x2[ix] = ix)
)
);
stop = HP Time();
Write( Eval Insert( "\!nContains method: ^char((stop - start)/1e6,5,3)^ seconds ^explain[t]^" ) );
);
/*
Use a DEFAULT value to determine if the entry needs to be
created. Fetch the value for the entry, check for the default
and create if needed. This gives an between value for both
"t" loops. It might be a good compromise choice if no extra
testing will be done and 0 can be used as a default. !y,
below, is faster than y==0.
*/
x3 = [=> 0];
For( t = 1, t <= 2, t += 1,
start = HP Time();
For( ix = 1, ix < N, ix += 1,
y = x3[ix];
If( !y, y = (x3[ix] = ix) );
);
stop = HP Time();
Write( Eval Insert( "\!ndefault 0 method: ^char((stop - start)/1e6,5,3)^ seconds ^explain[t]^" ) );
);
// quick test... the lack of a default value in x1 and x2
// makes them compare not equal, so add it before testing.
x1 << setdefaultvalue( 0 );
x2 << setdefaultvalue( 0 );
Show( x1 == x2, x1 == x3 );
Método de prueba: 2.851 segundos crear nueva entrada
Pruebe el método: 0.099 segundos usar entrada existente
Contiene método: 0.209 segundos crear nueva entrada
Contiene método: 0.151 segundos usar entrada existente
método predeterminado 0: 0.259 segundos crear nueva entrada
método predeterminado 0: 0.117 segundos usar entrada existente
x1 == x2 = 1;
x1 == x3 = 1;
En rojo, TRY y CONTAINS proporcionan los tiempos más rápidos para entradas existentes y nuevas. En verde, DEFAULT 0 podría ser un buen compromiso. Realmente depende de cuántas entradas se crearán y cuántas veces se buscarán.