Author Topic: Technical Question - GetObjectByTag or...  (Read 2201 times)

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Technical Question - GetObjectByTag or...
« Reply #30 on: August 21, 2011, 09:32:10 pm »


               

Mavrixio wrote...

The test was done in an area with 70 objects, The module has about 14k objects in its tag list.


Thanks. '<img'> Could you post the full script you used, so I can create a close analogue for my runs? I understand the Timer functions are Win NWNX, but there's also the question of how you got the pc, etc etc - the more similarity, the better. I appreciate your patience - if you're right about this, it'll be very handy to know.

Since we're on the subject, the other main reason we stopped using GObT was nonunique tags, which created some curious bugs in the module we inherited. So long as you have a good naming convention pre-established, though, that shouldn't be a huge hurdle. There were also some idiotic uses of GObT to find the caller of the script (instead of simply using OBJECT_SELF), but that's neither here nor there. '<img'>

Thanks,
Funky
               
               

               
            

Legacy_Mavrixio

  • Full Member
  • ***
  • Posts: 142
  • Karma: +0/-0
Technical Question - GetObjectByTag or...
« Reply #31 on: August 21, 2011, 10:50:41 pm »


               

FunkySwerve wrote...

Mavrixio wrote...

The test was done in an area with 70 objects, The module has about 14k objects in its tag list.


Thanks. '<img'> Could you post the full script you used, so I can create a close analogue for my runs? I understand the Timer functions are Win NWNX, but there's also the question of how you got the pc, etc etc - the more similarity, the better. I appreciate your patience - if you're right about this, it'll be very handy to know.


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);
}
void main()
{
object oPC = OBJECT_SELF;
   //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");  
}
               
               

               
            

Legacy_ffbj

  • Hero Member
  • *****
  • Posts: 1097
  • Karma: +0/-0
Technical Question - GetObjectByTag or...
« Reply #32 on: August 22, 2011, 01:11:01 am »


               So the tag is theres no object with this tag.  So is that so it will just keep running until it has found/checked all the objects in the module, or the actual tag is such?  Just wondering, since I'm not all the up on this sort of stuff.
               
               

               
            

Legacy_Mavrixio

  • Full Member
  • ***
  • Posts: 142
  • Karma: +0/-0
Technical Question - GetObjectByTag or...
« Reply #33 on: August 22, 2011, 01:53:23 am »


               

ffbj wrote...

So the tag is theres no object with this tag.  So is that so it will just keep running until it has found/checked all the objects in the module, or the actual tag is such?  Just wondering, since I'm not all the up on this sort of stuff.


It will do the longest search, but it wont compare it with all tags, since the tags are sorted, it will do a binary search which is very quick no matter the number of tags that theres in your module.
               
               

               
            

Legacy_ffbj

  • Hero Member
  • *****
  • Posts: 1097
  • Karma: +0/-0
Technical Question - GetObjectByTag or...
« Reply #34 on: August 22, 2011, 01:58:45 am »


               I see, so you then compare the various methods since they are all doing the longest search, and arrive at the conclusion that those other two methods are slower, since they take longer to complete the longest search.  I think.  Thanks. I suppose that's a standard methodology for testing search algorithms, but since I don't know about such things, that's just a guess.
               
               

               
            

Legacy_Hardcore UFO

  • Full Member
  • ***
  • Posts: 157
  • Karma: +0/-0
Technical Question - GetObjectByTag or...
« Reply #35 on: August 22, 2011, 07:27:06 am »


               Some of this is going over my head, but I'm glad I'm not the only one wondering about this.
               
               

               
            

Legacy_GhostOfGod

  • Hero Member
  • *****
  • Posts: 1490
  • Karma: +0/-0
Technical Question - GetObjectByTag or...
« Reply #36 on: August 22, 2011, 08:27:08 am »


               So is unique tag and "GetObjectByTag" now the way to go? I've been doing the opposite this whole time since I was always told that GOBT was inefficient. Just let me know which way the crowd is going so I can join.
               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Technical Question - GetObjectByTag or...
« Reply #37 on: August 22, 2011, 04:34:12 pm »


               I wouldn't jump to that conclusion just yet. I'm going to do a series of tests to see if practice confirms theory.

Funky
               
               

               
            

Legacy_GhostOfGod

  • Hero Member
  • *****
  • Posts: 1490
  • Karma: +0/-0
Technical Question - GetObjectByTag or...
« Reply #38 on: August 22, 2011, 05:18:53 pm »


               

FunkySwerve wrote...

I wouldn't jump to that conclusion just yet. I'm going to do a series of tests to see if practice confirms theory.

Funky


Awesome.
               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Technical Question - GetObjectByTag or...
« Reply #39 on: August 22, 2011, 05:44:45 pm »


               First test is complete. I may not have to do any others.

This used the bioware profiler - first time I've done profiling with it, rather than NWNX (also the first time since 1.69).

This was done on HG, with the normal placeable counts, etc, in an actual-practice situation. We retrieve an object to use as an effect creator for diseases that inflict vulnerability. Nowadays, we set effect ids using nwnx_structs, but we didn't convert everything to that. Anyway, here's the actual code in the module for it. As you can see, we use the waypoint/tag technique:

void DoDiseaseVuln (object oDiseased) {
    object oArea = GetArea(oDiseased);

    if (!GetIsObjectValid(oArea)) {
        DelayCommand(6.0, DoDiseaseVuln(oDiseased));
        return;
    }

    int nAmount = GetLocalInt(oDiseased, "DiseaseVuln");

    if (!nAmount)
        return;

    if (GetIsDead(oDiseased)) {
        DeleteLocalInt(oDiseased, "DiseaseVuln");
        return;
    }

    object oEffectWay = GetWaypointByTag("effectsareawaypo");
    object oDiseaseControl = GetNearestObjectByTag("disease_vuln", oEffectWay);
    effect eTest = GetFirstEffect(oDiseased);
    int nApplied = FALSE;

    while (GetIsEffectValid(eTest)) {
        if (GetEffectCreator(eTest) == oDiseaseControl)
            nApplied = TRUE;
        eTest = GetNextEffect(oDiseased);
    }


    if (!nApplied) {
        AssignCommand(oDiseaseControl, ApplyDiseaseVuln(oDiseased, nAmount));
    }

    DelayCommand(6.0, DoDiseaseVuln(oDiseased));
}

That waypoint, effectsareawaypo, is in a small, 4x4 area, with very few objects - just a waypoint and 7 places - it was designed specifically to keep GNObT streamlined.

I placed 3 objects in our test area in the mod, each with a different heartbeat script:

test_gnobt

void main()
{
    object oDiseaseControl;
    int nX;
    for (nX = 0; nX < 1000; nX++)
        oDiseaseControl = GetNearestObjectByTag("disease_vuln", GetWaypointByTag("effectsareawaypo"));
}

test_gobt

void main()
{
    object oDiseaseControl;
    int nX;
    for (nX = 0; nX < 1000; nX++)
        oDiseaseControl = GetObjectByTag("disease_vuln");
}

test_glo

void main()
{
    object oDiseaseControl;
    int nX;
    for (nX = 0; nX < 1000; nX++)
        oDiseaseControl = GetLocalObject(GetModule(),"disease_vuln");//set on modload
}

Here are the results of the profiling run:

test_gnobt     runs 348     total time 4900
test_gobt       runs 348     total time 685
test_glo         runs 348     total time 1395

So, according to this test, at least, GetObjectByTag outperforms even GetLocalObject, even on a huge mod like HG which probably has upwards of 70,000 objects in play (wild-ass guess based on 50k place count and 566 areas). We don't have a lot of vars on the mod, either - under 20, if I had to guess. Note that I used GetModule() each time, rather than assigning a var - again, because that's likely what you would be doing in practice, unless you happened to be working on a script where OBJECT_SELF was the mod.

I'm a little concerned at the number of runs - I think load may have played a role in the numbers, perhaps distorting them, since I let this run quite a while, and there should've been more runs. Still, the numbers speak for themselves. Looks like the common wisdom was wrong again - props to Mavrixio for pointing this out. '<img'>

I'll probably do some additional runs this evening to see whether all objects in an area get checked by GNObT before it picks one, but that's sort of irrelevant, now. All you need is a good prefixing system to ensure unique tags - with that, GObT is definitely the way to go. I may also compre GObT to GetWaypointByTag, to see if GWbT has similar optimization.

Funky
               
               

               


                     Modifié par FunkySwerve, 22 août 2011 - 04:47 .
                     
                  


            

Legacy_Kato -

  • Hero Member
  • *****
  • Posts: 747
  • Karma: +0/-0
Technical Question - GetObjectByTag or...
« Reply #40 on: August 22, 2011, 06:14:52 pm »


               Very interesting and surprising indeed, I'm making a good note of this...
               
               

               
            

Legacy_Hardcore UFO

  • Full Member
  • ***
  • Posts: 157
  • Karma: +0/-0
Technical Question - GetObjectByTag or...
« Reply #41 on: August 22, 2011, 10:35:15 pm »


               I started a revolution! I'm so proud... Now to take on society.

But seriously, thanks for taking the time to look into this. I know it wasn't just for my benefit but I'm sure it'll help a lot of scripters to know this.
               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Technical Question - GetObjectByTag or...
« Reply #42 on: August 22, 2011, 10:45:57 pm »


               +1 Very useful information.
               
               

               
            

Legacy_ffbj

  • Hero Member
  • *****
  • Posts: 1097
  • Karma: +0/-0
Technical Question - GetObjectByTag or...
« Reply #43 on: August 23, 2011, 01:35:55 am »


               Wow that's quite a difference, like 9x faster to use gobt, props Mavrixio . Thanks FS for construction of the test, results and explanation. So the only real use for gnobt should only be non-unique tags, to differentiate between the same tag and get the nearest one. I suppose that indirectly also answers the question about proximity, which does'nt seems to matter.
               
               

               


                     Modifié par ffbj, 23 août 2011 - 12:45 .
                     
                  


            

Legacy_Failed.Bard

  • Hero Member
  • *****
  • Posts: 1409
  • Karma: +0/-0
Technical Question - GetObjectByTag or...
« Reply #44 on: August 23, 2011, 02:00:06 am »


                 Actually, if you're just getting the absolute nearest object with that tag in the area, I wouldn't use the standard function either.
  I expect, based on the ability to find nNth nearest, that it has to specify quite a large number of floats and objects, then search every object in the mod, compare distances, find the nearest one, and then start over excluding the ones found already until they reach the number needed.

  I wrote this as a "true" GetNearestObjectByTag, and for finding the nearest in the same test I did the others in (500 loops of the function), it averaged 4.5ms compared to GNOBT's 24ms+ average.

I haven't tested it much yet, but I expect part of the difference in speed may be in the for loop itself, which when I switched all my scripts over when I switched to the better compiler seemed to run about 40% faster than while loops.

  Likely it could be optimized even further still, I wrote this pretty quickly yesterday just as a test.
 
// (8/21/2011) Failed Bard
// GNOBT replacer.
object FB_GetNearestObjectByTag (string sTag, object oSource = OBJECT_SELF)
{
 object oNearest, oTest, oArea;
 float  fNearest, fTest;
 int i;
 oArea = GetArea (oSource);
 fNearest = 1000.0;
 oTest = GetObjectByTag (sTag, i);
// the 500 here can be set higher if a module has a huge number of identically named tags. 
 for (i = 1; i < 500 && GetIsObjectValid (oTest); i ++)
    {
     if (GetArea (oTest) == oArea)
        {
         fTest = GetDistanceBetween (oTest, oSource);
         if (fTest < fNearest)
            {
             oNearest = oTest;
             fNearest = fTest;
            }
        }
     oTest = GetObjectByTag (sTag, i);
    }
 return oNearest;
}
               
               

               


                     Modifié par Failed.Bard, 23 août 2011 - 01:07 .