Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve HasEntProp performance #1908

Merged
merged 1 commit into from
Mar 30, 2023

Conversation

sirdigbot
Copy link
Contributor

@sirdigbot sirdigbot commented Jan 18, 2023

Implements the HasEntProp optimization suggested by asherkin in the discord (caching missed lookups).
I tried to make any changes as non-disruptive as possible because I barely know what I'm doing, so it could probably be optimized further.

Testing shows it to be roughly 2-3x faster:

100 (* 6) iterations without fix:
[HasEntProp] Average Time:1.230063ms
[HasEntProp] Average Time:1.186231ms
[HasEntProp] Average Time:1.137192ms
Average = (3.553486ms / 3) = 1.18449533ms

100 (* 6) iterations with fix:
[HasEntProp] Average Time:0.383327ms
[HasEntProp] Average Time:0.375259ms
[HasEntProp] Average Time:0.443078ms
Average = (1.201664ms / 3) = 0.4005546ms

Using the following code, specifically sm_hasentprop2 (which is probably not a good test):
The props used are either common, less common, or invalid for each of Prop_Send and Prop_Data.

#include <sourcemod>
#include <profiler>

#pragma semicolon 1
#pragma newdecls required

public Plugin myinfo = {
    name        = "",
    author      = "",
    description = "",
    version     = "0.0.0",
    url         = ""
};

public void OnPluginStart()
{
    PrintToServer("Ready to test HasEntProp!");
    
    RegConsoleCmd("sm_hasentprop", Cmd_HasEntProp);
    RegConsoleCmd("sm_hasentprop2", Cmd_HasEntProp2);
}


Action Cmd_HasEntProp(int client, int args)
{
    int maxEnts = GetMaxEntities();

    Profiler p = new Profiler();
    for (int i = 1; i <= 4; ++i)
    {
        int ents;
        int name;
        int silencer;
        int speed;
        int attack;
        
        p.Start();
    
        for (int j = MaxClients + 1; j <= maxEnts; ++j)
        {
            if (!IsValidEntity(j))
                continue;
                
            ++ents;
 
            if (HasEntProp(j, Prop_Send, "m_iName"))
                ++name;

            if (HasEntProp(j, Prop_Send, "m_bSilencerOn"))
                ++silencer;
            
            if (HasEntProp(j, Prop_Send, "invalid prop"))
                PrintToChatAll("%i INVALID?! (Prop_Send)", j);
                
            if (HasEntProp(j, Prop_Data, "m_flSpeed"))
                ++speed;

            if (HasEntProp(j, Prop_Data, "m_flNextPrimaryAttack"))
                ++attack;

            if (HasEntProp(j, Prop_Data, "invalid prop"))
                PrintToChatAll("%i INVALID?! (Prop_Data)", j);
        }
        
        p.Stop();
        
        PrintToChatAll("[HasEntProp %i] Time:%f | Ents:%i, Name:%i, Silencer:%i, Speed:%i, Attack:%i", i, p.Time, ents, name, silencer, speed, attack);
    }
    
    delete p;

    return Plugin_Handled;
}

Action Cmd_HasEntProp2(int client, int args)
{
    int maxEnts = GetMaxEntities();
    float totalTime;
    Profiler p = new Profiler();
    
    const int ITERS = 100;

    for (int i = 1; i <= ITERS; ++i)
    {
        int dummy;
        
        p.Start();
    
        for (int j = MaxClients + 1; j <= maxEnts; ++j)
        {
            if (!IsValidEntity(j))
                continue;
    
            if (HasEntProp(j, Prop_Send, "m_iName"))
                ++dummy;
            
            if (HasEntProp(j, Prop_Send, "m_bSilencerOn"))
                ++dummy;
            
            if (HasEntProp(j, Prop_Send, "invalid prop"))
                ++dummy;
                
            
            if (HasEntProp(j, Prop_Data, "m_flSpeed"))
                ++dummy;

            if (HasEntProp(j, Prop_Data, "m_flNextPrimaryAttack"))
                ++dummy;
            
            if (HasEntProp(j, Prop_Data, "invalid prop"))
                ++dummy;
        }
        
        p.Stop();
        
        totalTime += p.Time;
    }
    
    PrintToChatAll("[HasEntProp] Average Time:%fms", (totalTime / ITERS) * 1000);
    delete p;

    return Plugin_Handled;
}

@sirdigbot
Copy link
Contributor Author

sirdigbot commented Jan 18, 2023

I left the first command in the testing code because it was used to verify that HasEntProp was also returning the right values, which it seems to.

@asherkin asherkin self-requested a review January 18, 2023 12:05
@asherkin
Copy link
Member

Nice work, thanks for this! I'll try and take a look in the next few days - prod me on Discord if I forget.

@Kenzzer
Copy link
Member

Kenzzer commented Jan 22, 2023

Can a method be exposed for extensions to purge the cache ?
https://canary.discord.com/channels/335290997317697536/335290997317697536/1034852606608166942

Copy link
Member

@Headline Headline left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to do cache invalidation later, I'm cool with this now. I'm afraid this will overcook

@Headline Headline merged commit bc6e920 into alliedmodders:master Mar 30, 2023
@sirdigbot sirdigbot deleted the hasentprop-fix branch April 10, 2023 03:47
StarterX4 pushed a commit to StarterX4/sourcemod that referenced this pull request Aug 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants