Aller au contenu

Photo

Technical Question - GetObjectByTag or...


  • Veuillez vous connecter pour répondre
59 réponses à ce sujet

#1
Hardcore UFO

Hardcore UFO
  • Members
  • 86 messages
This is a question for the more advanced scripters who know about the systems that keep the scripts running. I'm trying to decide between two script versions for an OnEnter event on most areas. This will light placeable torches and lamps while circumventing the need for heartbeat scripts.

The first script uses GetObjectByTag, where all the lamps (probably across the module) would have the same tag; the second uses GetObjectByType followed by GetLocalInt.

The dealbreaker here is which one would be most efficient/less strenuous on the resources? I've been told GetObjectByTag is pretty reliable but it depends on how the engine itself keeps track of what it's scanning for. If an area is large with many doors and placeables, and GetObjectByTag cycles all things considered an object (doors, placeables, creatures, etc) to compile a list of valid tags, it seems like it might end up slower.

Now this isn't a huge deal, but it's something that piqued my curiosity - both scripts run fine, but every bit of streamlining counts. Replies and/or debate is appreciated!

#2
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
Hmm, First Question:

Where does the GetObjectByType function come from. NWNx? or an Include?

If it comes from NWNx it may be faster.
If it is from an include, it is a wrapper?? well I dont know. GetNeareast Object maybe?

I really thing nither one is the answer to your question. I would design a system where the objects(torches only had to be searcherd for once. That way it really would not matter how hard they where to find the first time, since it would only be done once.

you could either do what bioware did with waypoints, Serarching for them the first time and storing them in a list of local objects for future calls.

or
daisy chain them after finding them the first time. You could do that by storing a local object on the first one found that points to the second one. on the second one you would store a local object that points to the second one. ect.

#3
Failed.Bard

Failed.Bard
  • Members
  • 774 messages
I have an odd question. Since placeables can be set to factions, would it be possible to set them all into a special placeable faction and search them by faction?

While the Lexicon suggests it's PC/NPC, it doesn't state that it won't also retrieve objects. I suppose likely I can slap a script together to test it, but that might be a way to do it if it includes all objects, not just creatures.

Edit:  Nope, looks like the faction setting is likely just for determining who'll respond to shouts from the placeables.

Modifié par Failed.Bard, 19 août 2011 - 09:35 .


#4
Hardcore UFO

Hardcore UFO
  • Members
  • 86 messages
It's not from NWNx, it's just a fuction I found in the sidebar. It's not from another included script, nor a wrapper, the way I have it done; just plain old... OnEnter: Scan for this stuff here and do this to it. The two solutions you mentioned are unknown things to me that I'll look up, because they sound like they'd simplify this (at least once I figure out how to apply these things) greatly. These leads are much appreciated!

Too bad about the faction script, though.

Modifié par Hardcore UFO, 19 août 2011 - 09:12 .


#5
GhostOfGod

GhostOfGod
  • Members
  • 863 messages
There is no standard NWN function "GetObjectByType". There is a "GetObjectType" which just returns what type of object oObject is. If it is actually "GetObjectByType" then it is included from some other script or that function is written directly in the OnEnter script. You'd have to post the function from there for anyone here to see if it is efficient or what not.

#6
Hardcore UFO

Hardcore UFO
  • Members
  • 86 messages
You're right. I got confused with GetNearestObject, which can filter object types. Derp.

#7
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
You pretty much never want to use GetObjectByTag, if you're concerned about efficiency - it checks every object in the module. Instead, use GetNearestObjectByTag in the area, using GetFirstObjectInArea to get an object for GetNearest to compare to, if you need one. You can also loop objects in the area with the latter function and its GetNext counterpart.

Funky

#8
Mavrixio

Mavrixio
  • Members
  • 88 messages
It is the opposite... you never want to you GetNearestObjectByTag, if you are concerned by efficiency.

The module keep a sorted array of all tags, which is used by GetObjectByTag. So no matter the number of objects you have in your module, GetObjectByTag will be very fast.

GetNearestObjectByTag doesn't use this sorted array and prefer looping through all objects in the area and it also need to check the distance of each object found... if you have many objects in your area and fewer objects with the tag that you are looking for, you may even want to use this function to get the nearest object with a specific tag:
object MyGetNearestObjectByTag(string sTag, object oTarget=OBJECT_SELF)
{
    int nNth = 0;
    float fNearestDistance = 100000.0;
    object oNearestObject;
    object oTargetArea = GetArea(oTarget);
    object oObject = GetObjectByTag(sTag, nNth);
    while (oObject != OBJECT_INVALID)
    {
        if (GetArea(oObject) == oTargetArea)
        {
            float fDistance = GetDistanceBetween(oObject, oTargetArea);
            if (fDistance < fNearestDistance)
            {
                fNearestDistance = fDistance;
                oNearestObject = oObject;
            }
        }
        oObject = GetObjectByTag(sTag, ++nNth);
    }
    return oNearestObject; 
}

Modifié par Mavrixio, 20 août 2011 - 01:00 .


#9
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages

Mavrixio wrote...

It is the opposite... you never want to you GetNearestObjectByTag, if you are concerned by efficiency.

The module keep a sorted array of all tags, which is used by GetObjectByTag. So no matter the number of objects you have in your module, GetObjectByTag will be very fast.


Interesting theory. In practice, however, completely wrong. I can't speak to the array, having not peeped that part of the engine, but GObT was incredibly slow in usage - one of several reasons we stopped using it. Granted, the distance-measuring in GNObT is wasteful in many cases, but it still FAR outperforms GObT in practice - probably because it's dealing with a few hundred objects instead of every object in the module. Have you done any actual profiling, or are you speaking solely from a theoretical perspective?

Funky

#10
henesua

henesua
  • Members
  • 3 858 messages
Funky, did you find at what point GNObT outperforms GObT? How many objects (roughly speaking) do you need to have for this to matter?

#11
Mavrixio

Mavrixio
  • Members
  • 88 messages

FunkySwerve wrote...

Mavrixio wrote...

It is the opposite... you never want to you GetNearestObjectByTag, if you are concerned by efficiency.

The module keep a sorted array of all tags, which is used by GetObjectByTag. So no matter the number of objects you have in your module, GetObjectByTag will be very fast.


Interesting theory. In practice, however, completely wrong. I can't speak to the array, having not peeped that part of the engine, but GObT was incredibly slow in usage - one of several reasons we stopped using it. Granted, the distance-measuring in GNObT is wasteful in many cases, but it still FAR outperforms GObT in practice - probably because it's dealing with a few hundred objects instead of every object in the module. Have you done any actual profiling, or are you speaking solely from a theoretical perspective?

Funky


Yes, i have tested it.
It certainly depend on the number of object the number of object in the area, i dont have stat about that, but for if there's 100+ object int the area, then GetObjectByTag is 2-3 times faster than GetNearestObjectByTag, I dont know you, but personally we have areas with 1000+ placeables, so GetNearestObjectByTag is a no-no.

#12
Hardcore UFO

Hardcore UFO
  • Members
  • 86 messages
Interesting indeed. I might end up with 2 seperate OnEnter scripts for more/less decorated areas. That is, since the listing option mentioned by Lightfoot8 seems to already be embedded in the GetObjectByTag function.

#13
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages

henesua wrote...

Funky, did you find at what point GNObT outperforms GObT? How many objects (roughly speaking) do you need to have for this to matter?

We found that GNoBT outperformed GObT radically even when our mod was much smaller - there was no evidence of any kind of optimization on GObT. It could take a second or two, even, which is outrageous as compared to GNObT.

Mavrixio wrote...

Yes, i have tested it.
It certainly depend on the number of object the number of object in the area, i dont have stat about that, but for if there's 100+ object int the area, then GetObjectByTag is 2-3 times faster than GetNearestObjectByTag, I dont know you, but personally we have areas with 1000+ placeables, so GetNearestObjectByTag is a no-no.


You say you've tested it, but you don't have any statistics on performance by object count? How exactly did you test it? I'd be very interested to see your profiling on it.

Also, if you have areas with 1k+ placeables, I'm a lilttle less inclined to take your claims about performance seriously. That's an unbelievable number, even if you're really decking an area out. Have you actually counted, or are you guestimating? The most obscene placeable count on HG is in the 500s, where I went seriously overboard decking out the Forest of Poisoned Dreams in Shedaklah with extra mushrooms in order to get the right effect.

For both of you, here's our mod set out in placeable and tile count, for purposes of comparison:
HG placeable/tile counts

FWIW, we stopped using GObT when the mod was less than half its current size.

Funky

#14
Mavrixio

Mavrixio
  • Members
  • 88 messages
I dont need to calculate any stats, the first pofiling result that i got was clear, and I have seen it while debugging nwserver that it use a sorted array for the tags.
I dont have the formula on the top of my head, but the time that it takes to access an element in a sorted array can be considered as constant... so no matter the size of your module, GetObjectByTag will be relatively fast, it can easely be faster than GetLocalObject if you have many variables on the source.

Modifié par Mavrixio, 21 août 2011 - 01:37 .


#15
henesua

henesua
  • Members
  • 3 858 messages
Funky, I have not seen GObT take a second or two. But my mod is very small right now. It is for that reason that I suspect size has something to do with the performance of these functions. I figure as off base as Mavrixio is coming across, there might be something to what he is saying.

That said... I certainly wasn't doubting you and pressing for your data. I just wanted to know what you thought on the matter.

Mavrixio, all I have to say is: show the data or it didn't happen.

#16
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages

henesua wrote...

That said... I certainly wasn't doubting you and pressing for your data. I just wanted to know what you thought on the matter.

Lol, I didn't think you were - I just like to put all the data I have on a topic out there. Knowing how many places are in your mod, or the average per area, or per tile, can come in handy. :P

One reason that I'm highly skeptical of the 1k placeable per area claim is that even a couple hundred places in a small enough space can cause huge amounts of lag, as the server updates the description for the client every time they come into visual range. We actually had to move our Pillar of Skulls in Avernus for this reason - it's original position was too close to the Tiamat fight in the area, and players moving in and out of sight of it was generating immense amounts of lag.

Anyway, what he's saying runs counter to all my experience, but who knows, maybe they optimized GObT in one of the later updates. If he's not going to show any profiling, though, I have no reason to suspect that that's the case. As you say..show it or it didn't happen. :P

Funky

#17
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages

Mavrixio wrote...
I dont have the formula on the top of my head, but the time that it takes to access an element in a sorted array can be considered as constant... so no matter the size of your module, GetObjectByTag will be relatively fast, it can easely be faster than GetLocalObject if you have many variables on the source.

Faster than GetLocalObject? That's even harder to believe. By the way, someone did some profiling a while back that shows that variable read times don't noticeably increase until you hit around 5k variables on an object, so the claim that this is only the case 'if you have many variables on the source' smells a bit too - I've yet to see an application outside the PRC Character Creator that slammed even close to that many vars on an object - even counting things like dynamic convo pseudoarrays, which only hit a couple hundred vars.

Do you have ANY profiling on GObT? You're making me very curious, if nothing else... :P

Funky

#18
Mavrixio

Mavrixio
  • Members
  • 88 messages
Script:
void TestLocalVars(int nCount)
{
object oTest = CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", GetStartingLocation());
int i;
for (i=0; i<nCount; i++) SetLocalObject(oTest, IntToString(i), oTest);
StartTimer();
for (i=0; i<10000; i++) GetLocalObject(oTest, "test");
SendMessageToPC(OBJECT_SELF, IntToString(nCount)+"vars: " + IntToString(EndTimer())+"ms");
DestroyObject(oTest);
}

//test of GetLocalObject
TestLocalVars(10);
TestLocalVars(20);
TestLocalVars(100);
TestLocalVars(200);
TestLocalVars(500);
TestLocalVars(1000);
TestLocalVars(2000);
//test of GetObjectByTag
StartTimer();
for (i=0; i<10000; i++) GetObjectByTag("theres no objects with this tag");
SendMessageToPC(oPC, "GetObjectByTag: " + IntToString(EndTimer())+"ms");
//test of GetNearestObjectByTag
StartTimer();
for (i=0; i<10000; i++) GetNearestObjectByTag("theres no objects with this tag");
SendMessageToPC(oPC, "GetNearestObjectByTag: " + IntToString(EndTimer())+"ms");

Output:
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:46] 10vars: 80ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:46] 20vars: 80ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:46] 100vars: 100ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:46] 200vars: 120ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:46] 500vars: 200ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:46] 1000vars: 300ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:46] 2000vars: 500ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:46] GetObjectByTag: 30ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:46] GetNearestObjectByTag: 590ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:49] 10vars: 80ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:49] 20vars: 80ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:49] 100vars: 100ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:49] 200vars: 110ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:49] 500vars: 200ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:49] 1000vars: 300ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:49] 2000vars: 490ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:49] GetObjectByTag: 30ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:49] GetNearestObjectByTag: 580ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:49] 10vars: 80ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:49] 20vars: 80ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:49] 100vars: 90ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:49] 200vars: 120ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:52] 500vars: 200ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:52] 1000vars: 290ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:52] 2000vars: 480ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:52] GetObjectByTag: 30ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:52] GetNearestObjectByTag: 640ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:52] 10vars: 80ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:52] 20vars: 80ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:52] 100vars: 100ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:52] 200vars: 120ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:52] 500vars: 200ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:52] 1000vars: 300ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:52] 2000vars: 500ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:52] GetObjectByTag: 30ms
[CHAT WINDOW TEXT] [Sat Aug 20 23:23:53] GetNearestObjectByTag: 690ms

#19
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
Interesting, but where are the Start and EndTimer functions? I've always used the NWNX profiler, which doesn't rely on in-script functions. And what size is the module? Object count? Objects in area? The amount of information you're leaving out isn't helping. Also, if you provide the full function and includes, I can run it on HG for comparison.

Funky

Modifié par FunkySwerve, 21 août 2011 - 05:26 .


#20
GhostOfGod

GhostOfGod
  • Members
  • 863 messages
Curiouser and curiouser...

Modifié par GhostOfGod, 21 août 2011 - 10:18 .


#21
Mavrixio

Mavrixio
  • Members
  • 88 messages
StartTimer and EndTimer are custom nwnx functions like nwnx_timer on Windows.

The number of object in our module / the custom changes done to our nwserver, it wont reassure you :P

What there's to know is that:
GetObjectByTag > GetLocalObject
GetNearestObjectByTag is not in the competition, you it only when you have no choice.
GetLocalObject get slightly slower with the number of variable on the object, but at a proportional speed... the performace of it could be calculated by (x + y * z) where x is the constant speed (slower that GetObjectByTag by itself, if you have a large module), y is the number of variables and z is performance decrease per variable (very small but after 300-500 variables you already doubled the default speed)

Modifié par Mavrixio, 21 août 2011 - 01:48 .


#22
Failed.Bard

Failed.Bard
  • Members
  • 774 messages
  I was curious about this myself, so I set up a test for it in my little mod using OnCick on placeables to trigger the scripts.  133 placeables in the area, exactly 100 barrell placeables placed between script trigger placeable and the placeable it was looking for, to simulate a somewhat crowded are.
  I'm not sure my complete mod placeable count, likely 1500-2000 though, since it's 50 areas.

 500 iterations of each search, in an attempt to get the run time high enough to compare.


  Script one, labeled unimaginatively "test_1"

 void main()
{
 object oTest;
 int i;
 for (i = 1; i <= 500; i ++)
    {
     oTest = GetObjectByTag ("TEST_FIND_ME");
    }
 SpeakString ("Done");
}


Script Two, "test_2"

void main()
{
 object oTest;
 int i;
 for (i = 1; i <= 500; i ++)
    {
     oTest = GetNearestObjectByTag ("TEST_FIND_ME");
    }
 SpeakString ("Done");
}


Run times:

test_1          0 msec        1 calls
test_2          38 msec      1 calls


I'll try it again with only 50 placeables between, to see how much of a speed difference it makes per placeable it has to check, but I expect it has to check every single placeable in the area, then compare tags and distances, so it'll still be substantially slower.


Edit:  50 between, 83 placeables total in area:

test_1           0 msec      1 calls
test_2          25 msec      1 calls

test_1          20 msec     10 calls
test_2         241 msec     10 calls

Modifié par Failed.Bard, 21 août 2011 - 12:01 .


#23
OldTimeRadio

OldTimeRadio
  • Members
  • 1 400 messages
I have nothing to contribute to this discussion but I just wanted to cheer you folks on.  Knowing the more efficient of these methods, or which is more efficient in what situation, would be a huge help to me.  I'm not that great a sccripter to begin with so I need all the help I can get when it comes to efficiency of certain functions.

#24
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages

Mavrixio wrote...

StartTimer and EndTimer are custom nwnx functions like nwnx_timer on Windows.

The number of object in our module / the custom changes done to our nwserver, it wont reassure you :P

What there's to know is that:
GetObjectByTag > GetLocalObject
GetNearestObjectByTag is not in the competition, you it only when you have no choice.
GetLocalObject get slightly slower with the number of variable on the object, but at a proportional speed... the performace of it could be calculated by (x + y * z) where x is the constant speed (slower that GetObjectByTag by itself, if you have a large module), y is the number of variables and z is performance decrease per variable (very small but after 300-500 variables you already doubled the default speed)


I already know what you're claiming. I'm asking for data to help in verifying those claims. Again:

And what size is the module? Object count? Objects in area? The amount
of information you're leaving out isn't helping. Also, if you provide
the full function and includes, I can run it on HG for comparison.


Funky

#25
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages

Failed.Bard wrote...

  I was curious about this myself, so I set up a test for it in my little mod using OnCick on placeables to trigger the scripts.  133 placeables in the area, exactly 100 barrell placeables placed between script trigger placeable and the placeable it was looking for, to simulate a somewhat crowded are.
  I'm not sure my complete mod placeable count, likely 1500-2000 though, since it's 50 areas.

It's not just placeables, remember, but all objects. Places are typically the most numerous types, but there's still other heavy hitters, like items, and triggers, along with creatures, doors, waypoints, etc.

Funky