如何从我的程序中调用 _varOp(_varAdd 等)?
how to call _varOp (_varAdd etc) from my program?
我正在编写自定义变体 PhysUnit,它类似于 VarConv,但更高级,不仅允许加法和减法,还允许乘法和除法单位,具有实数或复数,它工作正常,但非常慢。
问题是,这个自定义变体环绕了一些其他变体(简单类型,如整数或双精度,或其他自定义类型,如 VarComplex),因此在执行 DoAdd、DoSubtract 时,它首先检查两个操作数是否具有相同的系列(长度为例如),然后添加数量,如果需要,转换其中之一:
Left:=Left + Right*multiplier;
类似的,这里的 Left 和 Right 是变体。
编译器将此行转换为一系列调用:
_varCopy(tmp,Left);
_varAdd(tmp,Right*multiplier);
_varClear(Left);
_varCopy(Left,tmp);
_varClear(tmp);
而事实上,_varAdd 就足够了,没有 allocating/deallocating 内存用于临时变体和所有这些解决方法。
可悲的是:我不能只写 _varAdd(Left,Right),它没有在 VCL 中链接。
所以问题是:是否可以调用它并使其尽可能成为 "clean",而不会讨厌地调用直接内存地址,直接内存地址在使用不同的选项或添加的其他库编译时可能会发生变化?
如果您想使用简洁的语法,例如:
Left := Left + Right*multiplier;
然后你必须让编译器生成代码。而且由于您使用的是变体,生成的代码将执行得非常糟糕。
如果你想直接调用一个函数来执行操作,那么使用变体是没有意义的。您可以创建一个类型,大概是一条记录,向该类型添加一些方法,然后直接调用这些方法。
但是,如果你想制作一个支持数学运算符的类型,并且你关心性能,那么你应该使用operator overloading on records。
您不能调用带下划线的函数,因为编译器会将下划线转换为 @
,因此无法将其用作标识符。
但是汇编函数可以调用它们。您可以使用原始声明并将 TVarData
更改为 Variant
,这样您就不必一直转换变体。
procedure _VarAdd(var Left: Variant; const Right: Variant);
asm
jmp System.Variants.@VarAdd
end;
procedure _VarSub(var Left: Variant; const Right: Variant);
asm
jmp System.Variants.@VarSub
end;
begin
_VarAdd(Left, Right);
end;
但是如果你想快点,使用变体并不是正确的方法。它们非常慢,并且没有编译器优化器的帮助,例如整数运算,其中 i := i + 1;
无需临时 variable/cpu-register.
即可编译
您可以通过对常见情况使用特殊处理来使变体更快:
if TVarData(Left).VType = TVarData(Right).VType then
begin
case TVarData(Left).VType of
varSingle:
begin
TVarData(Left).VSingle := TVarData(Left).VSingle + TVarData(Right).VSingle * M;
Exit;
end;
varDouble:
begin
TVarData(Left).VDouble := TVarData(Left).VDouble + TVarData(Right).VDouble * M;
Exit;
end;
end;
end;
// use the slow function for all other cases
_VarAdd(Left, Right * M);
我正在编写自定义变体 PhysUnit,它类似于 VarConv,但更高级,不仅允许加法和减法,还允许乘法和除法单位,具有实数或复数,它工作正常,但非常慢。
问题是,这个自定义变体环绕了一些其他变体(简单类型,如整数或双精度,或其他自定义类型,如 VarComplex),因此在执行 DoAdd、DoSubtract 时,它首先检查两个操作数是否具有相同的系列(长度为例如),然后添加数量,如果需要,转换其中之一:
Left:=Left + Right*multiplier;
类似的,这里的 Left 和 Right 是变体。
编译器将此行转换为一系列调用:
_varCopy(tmp,Left);
_varAdd(tmp,Right*multiplier);
_varClear(Left);
_varCopy(Left,tmp);
_varClear(tmp);
而事实上,_varAdd 就足够了,没有 allocating/deallocating 内存用于临时变体和所有这些解决方法。
可悲的是:我不能只写 _varAdd(Left,Right),它没有在 VCL 中链接。
所以问题是:是否可以调用它并使其尽可能成为 "clean",而不会讨厌地调用直接内存地址,直接内存地址在使用不同的选项或添加的其他库编译时可能会发生变化?
如果您想使用简洁的语法,例如:
Left := Left + Right*multiplier;
然后你必须让编译器生成代码。而且由于您使用的是变体,生成的代码将执行得非常糟糕。
如果你想直接调用一个函数来执行操作,那么使用变体是没有意义的。您可以创建一个类型,大概是一条记录,向该类型添加一些方法,然后直接调用这些方法。
但是,如果你想制作一个支持数学运算符的类型,并且你关心性能,那么你应该使用operator overloading on records。
您不能调用带下划线的函数,因为编译器会将下划线转换为 @
,因此无法将其用作标识符。
但是汇编函数可以调用它们。您可以使用原始声明并将 TVarData
更改为 Variant
,这样您就不必一直转换变体。
procedure _VarAdd(var Left: Variant; const Right: Variant);
asm
jmp System.Variants.@VarAdd
end;
procedure _VarSub(var Left: Variant; const Right: Variant);
asm
jmp System.Variants.@VarSub
end;
begin
_VarAdd(Left, Right);
end;
但是如果你想快点,使用变体并不是正确的方法。它们非常慢,并且没有编译器优化器的帮助,例如整数运算,其中 i := i + 1;
无需临时 variable/cpu-register.
您可以通过对常见情况使用特殊处理来使变体更快:
if TVarData(Left).VType = TVarData(Right).VType then
begin
case TVarData(Left).VType of
varSingle:
begin
TVarData(Left).VSingle := TVarData(Left).VSingle + TVarData(Right).VSingle * M;
Exit;
end;
varDouble:
begin
TVarData(Left).VDouble := TVarData(Left).VDouble + TVarData(Right).VDouble * M;
Exit;
end;
end;
end;
// use the slow function for all other cases
_VarAdd(Left, Right * M);