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 performance of external (UDR) functions #7989

Merged
merged 2 commits into from Feb 28, 2024
Merged

Conversation

asfernandes
Copy link
Member

@asfernandes asfernandes commented Jan 31, 2024

Test preparation:

recreate function div_udr (
    n1 integer,
    n2 integer
) returns double precision
    external name 'udf_compat!UC_div'
    engine udr;

create domain d_int_check integer check (value > 0);
create domain d_double_check double precision check (value >= 0);

recreate function div_udr2 (
    n1 d_int_check,
    n2 d_int_check
) returns d_double_check
    external name 'udf_compat!UC_div'
    engine udr;

Comparison:

set stats on;

-- 4.4s -> 2.7s
execute block
as
    declare n integer = 5000000;
    declare x integer;
begin
    while (n > 0) do
    begin
        n = n - 1;
        x = div_udr(3, 2);
    end
end;

-- 5.1s -> 3.2s
execute block
as
    declare n integer = 5000000;
    declare x integer;
begin
    while (n > 0) do
    begin
        n = n - 1;
        x = div_udr2(3, 2);
    end
end;

@sim1984
Copy link

sim1984 commented Jan 31, 2024

Does this only improve the performance of external functions only, or is there an effect for procedures/triggers too?

@asfernandes
Copy link
Member Author

Does this only improve the performance of external functions only, or is there an effect for procedures/triggers too?

It's mostly about functions only.

There could be a marginal improvement with procedures with parameters with constraints, but I didn't tested it, as procedure/trigger implementation could also be greater improved, but is much more difficult.

@dyemanov dyemanov self-requested a review January 31, 2024 17:56
@asfernandes asfernandes self-assigned this Feb 27, 2024
@asfernandes
Copy link
Member Author

@dyemanov are you going to review this before merge?

dsc dstDesc = dstDescIt[0];
dstDesc.dsc_address = dstMsg + dstArgOffset;

MOV_move(tdbb, &srcDesc, &dstDesc);
Copy link
Member

Choose a reason for hiding this comment

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

In some places we still have the code like this:

if (DSC_EQUIV(src, dst))
  memcpy
else
  MOV_move

Do you think it could improve something? I know we also have the same trick inside CVT_move_common(), but it's two calls deeper in the stack...

Copy link
Member Author

Choose a reason for hiding this comment

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

It's already being done in CVT_move_common.
It's not inline, but, I think this is not going to be so relevant.

*dstNullPtr = 0;
}
else
*dstNullPtr = -1;
Copy link
Member

Choose a reason for hiding this comment

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

Maybe add a named constant e.g. MSG_NULL_FLAG = -1 instead of using magic numbers directly in code?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think FB_FALSE / FB_TRUE could be used as we used them extensively in similar places.

Parameter* parameter = parameters[argNumber / 2];
defaultValueNode = NULL;
// Iterate over the format items, except the EOF item.
const unsigned paramCount = message->format->fmt_count / 2;
Copy link
Member

Choose a reason for hiding this comment

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

Being paranoid, I'd also add an assertion about fmt_count being even ;-)

Copy link
Member Author

Choose a reason for hiding this comment

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

It should be wrong. It's used to initialize output message, that is going to have the null flag.
In this case, it purposely ignores it.


// Initialize outputs in the internal message.
{ // scope
fb_assert(udf->getOutputFormat()->fmt_desc.getCount() / 2 == udf->getOutputFields().getCount());
Copy link
Member

Choose a reason for hiding this comment

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

I see nothing this scope would protect. Is it really needed?

Copy link
Member Author

Choose a reason for hiding this comment

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

I used it mostly as a block to not create a separate function just for it.
I'm not removing the explicit scope label.

defaultValue = EVL_expr(tdbb, request, fieldInfo.defaultValue);

if (request->req_flags & req_null)
defaultValue = nullptr;
Copy link
Member

Choose a reason for hiding this comment

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

IIRC, this condition/assignment is redundant, EVL_expr() is guaranteed to return nullptr in this case.

@asfernandes asfernandes merged commit 547cb83 into master Feb 28, 2024
45 of 47 checks passed
@asfernandes asfernandes deleted the work/udrperf branch February 28, 2024 21:43
asfernandes added a commit that referenced this pull request Apr 1, 2024
…8046)

* Postfix for #7989 - Improve performance of external (UDR) functions.

* Extract common functions.

* Fix profiler request time for external functions.

* Change cancellation detection for external functions.

* Move new private code to anonymous namesapce and rename functions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants