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

Do not support full CLR IL code if it is resonable for perfomance (256 params/variables/assignments in lambda but not 65k) #89

Open
dzmitry-lahoda opened this issue Aug 20, 2018 · 7 comments

Comments

@dzmitry-lahoda
Copy link
Contributor

dzmitry-lahoda commented Aug 20, 2018

FEC may work only with 8 bit parameters. Just to consider. I got into this while working with byref. May be there are other CLR features cool for CLR but no reason in high performance code.

https://stackoverflow.com/questions/12658883/what-is-the-maximum-number-of-parameters-that-a-c-sharp-method-can-be-defined-as



Here is your theoretical answer:

In order to push method arguments onto the stack, compiled code has the following MSIL opcodes to choose from:

ldarg.0

ldarg.1

ldarg.2

ldarg.3

ldarg.S

ldarg

ldarg.0 to ldarg.3 is used to push the first 4 method arguments onto the stack (including this as the first argument for instance methods).

ldarg.S takes an 8 bit argument number, and so it can be used to push up to 256 arguments onto the stack.

That leaves us with plain old ldarg, which can handle the most method arguments: it takes an unsigned 16 bit argument number. So the largest number of arguments that can be successfully compiled into valid MSIL is 2^16 = 65,536.

As others have noted, however, there are various practical stack size limits that apply when actually trying to execute your method depending on the platform/architecture of the system. Based on rmiesen's answer, it looks like the current .NET implementation limits the maximum size of the stack at runtime to 2^14.

....

CIL also stores lengths of parameter arrays in unsigned shorts, so the length can't be more than 65,535. 
@dadhi
Copy link
Owner

dadhi commented Aug 20, 2018

How do you hit it with byref? Had more than 256 params? omg

@dzmitry-lahoda
Copy link
Contributor Author

i needed to pass clone of param array if call was by ref and the index as int in pull #90. if we could limit param to 256 items i could use other pattern with 256 bit vector. also all for loops and params on jitted stack could use byte and probably be faster. as of now whole fec uses int, while it could use ushort. not sure it would be faster for ushort so.

@dzmitry-lahoda
Copy link
Contributor Author

dzmitry-lahoda commented Aug 21, 2018

Bug? (only _S call, but no without suffix)

Not bug, but more check and code.

And what if for loops by short are faster? I.e. it is by spec CLR limit. Why do we use ints everywhere?

And why would you have more than 256 params even if CLR allows? Can we explicitly simplify code, and generate only _S forms? I mean it is always be possible to have more than 256 params later if ever requested.

@dzmitry-lahoda
Copy link
Contributor Author

Stloc - somewhere used short limit, somewhere byte. Possible bugs. I.e. I guess FEC will not compile anything with more that 255 params. And where long form used it used to generate non optimal IL.....
Let drop such possibility at all!

@dzmitry-lahoda
Copy link
Contributor Author

Slow expression compiler:)

            var c = short.MaxValue-(short.MaxValue/4);
            var variables = new ParameterExpression[c];
            var assignments = new BinaryExpression[c];
            for (int i = 0; i < c; i++)
            {
                variables[i] = Variable(typeof(long));
            }

            for (int i = 0; i < c; i++)
            {
                assignments[i] = Assign(variables[i], Constant(3L));
            }

            Expression sum = variables[c - 1];

            var lambda = Lambda<Func<long>>(Block(variables, assignments.Cast<Expression>().Concat(new[] { sum })));


            var sw = new System.Diagnostics.Stopwatch();
            sw.Start();
            var compiledA = lambda.Compile();
            sw.Stop();
            Console.WriteLine(sw.Elapsed.TotalMilliseconds);
            var r = compiledA();

            var sw1 = new System.Diagnostics.Stopwatch();
            sw1.Start();
            var compiledB = lambda.CompileFast<Func<long>>(true);
            sw1.Stop();
            Console.WriteLine(sw1.Elapsed.TotalMilliseconds);
            var r2 = compiledB()
173.7444
3088.6155

@dzmitry-lahoda
Copy link
Contributor Author

SOE from FEC, but not from LINQ (linq is fine)

            var c = short.MaxValue/8;

            var exprected = 0;
            for (int i = 0; i < c; i++)
            {
                exprected += i;
            }

            var variables = new ParameterExpression[c];
            var assignments = new BinaryExpression[c];
            for (int i = 0; i < c; i++)
            {
                variables[i] = Variable(typeof(long));
            }

            for (long i = 0; i < c; i++)
            {
                assignments[i] = Assign(variables[i], Constant(i));
            }

            Expression sum = variables[0];
            for (int i = 1; i < c; i++)
            {
                sum = Add(sum, variables[i]);
            }

            var lambda = Lambda<Func<long>>(Block(variables, assignments.Cast<Expression>().Concat(new[] { sum })));


            var sw = new System.Diagnostics.Stopwatch();
            sw.Start();
            var compiledA = lambda.Compile();
            sw.Stop();
            Console.WriteLine(sw.Elapsed.TotalMilliseconds);
            var r = compiledA();

            var sw1 = new System.Diagnostics.Stopwatch();
            sw1.Start();
            var compiledB = lambda.CompileFast<Func<long>>(true);
            sw1.Stop();
            Console.WriteLine(sw1.Elapsed.TotalMilliseconds);
            var r2 = compiledB();

@dzmitry-lahoda dzmitry-lahoda changed the title Do not support full CLR IL code if it is resonable for perfomance Do not support full CLR IL code if it is resonable for perfomance (256 params/variables/assignments in lambda but not 65k) Aug 21, 2018
@dadhi
Copy link
Owner

dadhi commented Aug 21, 2018

Interesting.. Keep going 👍
I will look into this later... currently in prep for conference.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants