For list accesses I tried accessing the first element of a list 500 time, the middle element of the list 500 times, and the last element of a list 500 times (using a 300 element list). I also tried to access the first nad last elements of a list alternately, as well as the first and middle, and middle and last element. I timed them all and got:
stats: Retrieving first element 500 times.
stats: Operation took: 23.197552 seconds
stats: Retrieving middle element 500 times.
stats: Operation took: 23.649410 seconds
stats: Retrieving last element 500 times.
stats: Operation took: 24.349136 seconds
stats: Flip-flop test: first and last: 500 accesses.
stats: Operation took: 19.149178 seconds
stats: Flip-flop test: first and middle: 500 accesses.
stats: Operation took: 18.698975 seconds
stats: Flip-flop test: middle and last: 500 accesses.
stats: Operation took: 19.166626 seconds
It seems to me that even if accessing lists is a linear walk through the list, its fast enough compared to whatever else the script does. Otherwise there does not seem to be enough evidence to say whether accessing an element of a list is O(1) or O(n).
====
Another issue I've seen is passing lists to functions. The conventional wisdom is that passing large lists takes more time than passing small ones, but that does bear out in my tests (again, its a 300-element list).
stats: Passing empty list 500 times
stats: Operation took: 8.759567 seconds
stats: Passing large list 500 times
stats: Operation took: 8.299210 seconds
====
My question is, what do you make of these numbers? Can I assume constant time access to an arbitrary element of a list? Does anyone know how LSL lists really behave?
anyway, below is the code for my tests.
CODE
list zero = [];
list small = [0,1,2,3,4,5,6,7,8,9];
list medium = [];
list large = [];
float get_elem(list l, integer pos, integer iters) {
integer i;
float x = llGetTime();
for(i = 0; i < iters; i++) {
llList2Integer(l, pos);
}
return llGetTime() - x;
}
float flip_flop_test(list l, integer pos1, integer pos2, integer iters) {
integer i;
float x = llGetTime();
iters = iters/2;
for(i = 0; i < iters; i++) {
llList2Integer(l, pos1);
llList2Integer(l, pos2);
}
return llGetTime() - x;
}
float proc_test(list l, integer iters) {
integer i;
float x = llGetTime();
for(i = 0; i < iters; i++) {
pass_list(l);
}
return llGetTime() - x;
}
pass_list(list l) {
return;
}
default
{
state_entry()
{
llSay(0, "Creating medium list...");
integer i;
for(i = 0; i < 100; i++) {
medium += i;
}
llSay(0, "Creating large list...");
for(i = 0; i < 300; i++) {
large += i;
}
llSay(0, "Done.... touch me to run tests.");
}
touch_start(integer total_number)
{
if(llDetectedKey(0) != llGetOwner()) {
return;
}
float tm;
llSay(0, "Retrieving first element 500 times.");
tm = get_elem(large, 0, 500);
llSay(0, "Operation took: " + (string) tm + " seconds");
llSay(0, "Retrieving middle element 500 times.");
tm = get_elem(large, llGetListLength(large)/2, 500);
llSay(0, "Operation took: " + (string) tm + " seconds");
llSay(0, "Retrieving last element 500 times.");
tm = get_elem(large, llGetListLength(large) - 1, 500);
llSay(0, "Operation took: " + (string) tm + " seconds");
llSay(0, "Flip-flop test: first and last: 500 accesses.");
tm = flip_flop_test(large, 0, llGetListLength(large) - 1, 500);
llSay(0, "Operation took: " + (string) tm + " seconds");
llSay(0, "Flip-flop test: first and middle: 500 accesses.");
tm = flip_flop_test(large, 0, llGetListLength(large)/2, 500);
llSay(0, "Operation took: " + (string) tm + " seconds");
llSay(0, "Flip-flop test: middle and last: 500 accesses.");
tm = flip_flop_test(large, llGetListLength(large)/2,
llGetListLength(large) - 1, 500);
llSay(0, "Operation took: " + (string) tm + " seconds");
llSay(0, "Passing empty list 500 times");
tm = proc_test(zero, 500);
llSay(0, "Operation took: " + (string) tm + " seconds");
llSay(0, "Passing large list 500 times");
tm = proc_test(large, 500);
llSay(0, "Operation took: " + (string) tm + " seconds");
}
}
