Lightweight interfaces solely for type constraining purposes of generic parameters allowing inlining of member functions #6898
Replies: 6 comments 94 replies
-
I don't see why this proposed construct would be any different from an interface. I don't think the optimizations you suggest would be realized because you'd still require a form of indirection for the runtime to call through in order to call the actual method. The C# compiler doesn't get involved in optimizations such as inlining. And generics aren't expanded by the compiler like they are in C++. Have you tried making |
Beta Was this translation helpful? Give feedback.
-
I'm not even sure where to answer because you've opened so many discussions all covering different topics but using this totally unconcrete example (which resembles C# but it's not one) as
The main problem I see is that you fail to provide a clean C# example. Maybe open your repo, provide some code there first in C# then in "ideal" syntax. I mean first try to solve the problem using means C# already has so people can actually see where the problem lies. It seems to me that people do not really get what you really want to achieve. You've already did some tests/benchmarks so it would be cool to add some proper Benchmark.Net tests there (in that repo) too. At least this will give people (and hopefully you too) more problem context and clear numbers to try work around. |
Beta Was this translation helpful? Give feedback.
-
Virtual methods in struct and sealed class can be inlined by JIT optimization, but is that not enough? |
Beta Was this translation helpful? Give feedback.
-
If you want to rely on generic constraints, you have to use them all the way, you can't just use them locally, and then box the result as the interface. That could look for example something like this (full benchmark code): public interface IArray<T> where T : IArray<T>
{
double this[int i] { get; }
}
static class Extensions
{
public static Plus<TL, TR> Plus<TL, TR>(this TL left, TR right) where TL : IArray<TL> where TR : IArray<TR>
=> new Plus<TL, TR>(left, right);
}
public struct Vector : IArray<Vector>
{
double[] data;
public double this[int i] => data[i];
public Vector(int n)
{ data = new double[n]; }
}
public struct Plus<Left, Right> : IArray<Plus<Left, Right>>
where Left : IArray<Left>
where Right : IArray<Right>
{
public readonly Left L;
public readonly Right R;
public Plus(Left left, Right right)
{
L = left;
R = right;
}
public double this[int i] => L[i] + R[i];
}
public class Benchmark
{
[Benchmark]
public double Modified()
{
Vector a = new Vector(n);
Vector b = new Vector(n);
Vector c = new Vector(n);
return ModifiedImpl(a, b, c);
}
public double ModifiedImpl<T>(T a, T b, T c) where T : IArray<T>
{
var x = a.Plus(b).Plus(c).Plus(c);
double sum = 0.0;
for (int i = 0; i < n; i++)
sum += x[i];
return sum;
}
} Using this code, the performance was close to the hand-coded version:
Note that I had to use |
Beta Was this translation helpful? Give feedback.
-
@Dhmosthenis There is no problem using F# - it is a .Net language perfectly interoperable with C# it does even have units of measure to simplify those computations you're talking about. (And it has many more cool features). There is also a feature that might solve this: shapes. Or type classes if you wish. It's aim is to allow to extend arbitrary types with interfaces or members. So this might even allow extending arrays with operators directly too. I don't see a problem designing a generator which will generate whatever code you want from a given expression either. [SomeAttributeForYouGenerator]
private void SomeComputationTemplate()
{
... write down your program using marker types
}
public partial void SomeComputation
{
... generate optimized code using arrays or matrices or whatever
} And even if we assume language team is very interested in your idea then they obviously will design a very versatile math syntax/features that will be applicable to different problems within math scope. This might take years to research and design properly. I don't know if you are just seeking approval or something but people here are seriously trying to help you solve your problem right here and now. Goodnight. |
Beta Was this translation helpful? Give feedback.
-
@Dhmosthenis I want to avoid the impression that you're getting piled on here. First, thank you for bringing these areas of interest to be discussed here, and for suggesting ideas on how you think things could be better in the language for your needs. That said, please recognize that this is a repo that does extensive design and discussion. It's a huge process to go from idea to acceptance to implementation. That's normal. The bigger the idea, the more extensive this will be. Do not take the comments you are getting to mean that your ideas are not welcome. Rather, it's the exact opposite. People are considering your ideas, but rightfully and appropriately bringing up very important feedback that is all part of yours process. Just keep that in mind as you engage here. People can even want what you want. But it's still going to be a huge discussion and process to get there. |
Beta Was this translation helpful? Give feedback.
-
Dear all,
Currently an
interface
in C# encapsulates 2 purposes, 2 concepts. The first is to describe functionality and properties. The 2nd is that it can be used as constraint for C#generic
s. What I am suggesting is newgeneric interface
s that serve only the role to be used as constraints for generics.Interface based implementation of linear algebra expressions such as:
aiming to avoid temporary arrays such as
temp1=x1+x2
,temp2=temp1*10.0
,temp3=y*temp2
and the performance and memory overhead associated with them, introduces another penalty associated to indirect local access of Expression member functions. This penalty translates to almost 8-10x slower code when the size of array is large compared to a hand-coded loop, see #6893.C++ complains if a template parameter does not implement a requested member function. However, it does not use constraints for templates, and thus it can inline functions efficiently resulting in much faster code and syntax extremely easy to follow. C# uses Interfaces as generic constraints but then when a
class
orstruct
orrecord
inherit from an interface, calling its member functions is not as efficient because it is done from something like a C++ vtable, which prevents inlining and the performance associated with inlining.To this end, it seems beneficial to allow Lightweight interfaces maybe not even called interfaces but TypeDeclarations or something similar currying just the signature of the methods and to be used solely as generic constraints for the only purpose of enforcing the compiler to ask the exact interface member functions signature for each Generic parameter. In other words just for compiling reasons and not for runtime reasons.
This way, we keep the best of both worlds, and the implementation will not involve inheritance of the interface member functions as it is done now resulting in the performance penalty I described above, but these lightweight interfaces will help the compiler enforce constraints at the classes to be used as Generic parameters only. Example
These member functions cannot be inlined if the standard interface is used.
As a result, the member functions of Expressions like Add<Vector, Vector> or Mul<Vector, Vector> etc, will be inlined without trouble allowing for much faster execution.
What do you think? Can you think of applications that would benefit from something like that other than Fortran like syntax of array/matrix operations described #6893?
Beta Was this translation helpful? Give feedback.
All reactions