Inline MSIL?

C

Chris LaJoie

Is it possible to use 'inline' MSIL (Intermediate Language) similar to how
you can use inline assembly in C/C++? If not, how can I include some code
written in IL in my project? Thanks,

Chris LaJoie
 
A

Austin Ehlers

You should be able to just use ilasm.exe and make a DLL from the MSIL,
and then just add a reference to it from your project.

Austin Ehlers
 
C

Chris LaJoie

Thats what I'm trying to avoid. I'd like to keep classes written in C# and
classes written in IL in the same assembly.

Chris LaJoie
 
M

Michael Mayer

Chris LaJoie said:
Is it possible to use 'inline' MSIL (Intermediate Language) similar to how
you can use inline assembly in C/C++? If not, how can I include some code
written in IL in my project? Thanks,

I'm curious why you're writing in IL? Is it for some sort of performance
boost (is there even one? - I'd be kindof surprised) or is it to do things
that the framework supports but maybe C# doesn't allow you to do?

Don't read my post as anything negative - just curious what motivates a
person to write in IL.
 
J

Jon Skeet

Michael Mayer said:
I'm curious why you're writing in IL? Is it for some sort of performance
boost (is there even one? - I'd be kindof surprised) or is it to do things
that the framework supports but maybe C# doesn't allow you to do?

I can think of one thing offhand where the latter might be useful. IL
allows you to unbox a reference without copying the value - you can
use/modify the value directly. Suppose you were counting word
occurences, and you were using a hashtable mapping from the word to the
number of occurences so far. In IL, you could unbox the value and
increment it directly, without occurring the copy and the subsequent
boxing cost. In C# you'd have to unbox and copy, increment, then rebox
and then change the value in the hashtable to the new reference, which
is much more expensive.

Of course, in C# you could define a MutableIntegerReference class
instead, but that defeats the point of the example :)
 
C

Chris LaJoie

I'm curious why you're writing in IL? Is it for some sort of performance
boost (is there even one? - I'd be kindof surprised) or is it to do things
that the framework supports but maybe C# doesn't allow you to do?

Don't read my post as anything negative - just curious what motivates a
person to write in IL.

Thats a good question. I have several methods that must loop through
thousands of objects in an array, and manipulate each of them. When I
disassemble it, I find that C# simply isn't doing it in the most efficient
way, so I'd like to write 3 methods of my class in IL, and the rest in C#.
This apparently isn't possible.

Chris LaJoie
 
T

Thomas Scheidegger [MVP]

I find that C# simply isn't doing it in the most efficient way,

by the current design/implementation, it is for 99%
the job of the JITer to optimize code.
[and BTW, the JITer does it extremely well, mostly]

The C# compiler does only minimal 'cleanup'.

It has many advantages to do optimization
at JIT/runtime:

- JITer can produce code for the actual CPU
(instruction set, or even 64-Bit architecture in future)
- adapt to other contexts like OS, RAM.
- method inlining even for libraries
- ...
- the optimizer works for ALL .NET languages!
 
C

Chris LaJoie

by the current design/implementation, it is for 99%
the job of the JITer to optimize code.

That doesn't make sense at all. Why would Microsoft choose to have their
code optimized at runtime rather than at compile time?


In my case, the C# compiler is making IL for a single function that spans
~70 lines. I have rewritten that same function myself in IL in 30 lines.
My problem is more complex, but to give you a basic idea of what the C#
compiler makes vs. what I can code myself I'll give you an example:

here is the C# code for an 'Add' function:

private static int Add(int op1, int op2) {
return (op1 + op2);
}

when disassembled, that function looks like this:

..method private hidebysig static int32 Add(int32 op1,int32 op2)
{
.locals init (int32 retval)
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: add
IL_0003: stloc.0
IL_0004: br.s IL_0006

IL_0006: ldloc.0
IL_0007: ret
}

now here is some code I wrote, which does exactly the same thing:

..method private hidebysig static int32 Add(int32 op1,int32 op2)
{
ldarg.0 // push op1 on stack
ldarg.1 // push op2 on stack
add // add the 2 ops
ret // return
}

that's a ratio of 8 lines vs. my 4 lines. Which one will be faster?

Chris LaJoie
 
J

Jon Skeet

Chris LaJoie said:
That doesn't make sense at all. Why would Microsoft choose to have their
code optimized at runtime rather than at compile time?

For one thing, it means that the JIT can act differently depending on
the *exact* system it's running on.

For another, it means the code can be portable across different
platform architectures.

For a third, it means that potentially the runtime can recompile parts
depending on actual usage - the compiler isn't going to know at
compile-time which branch is mostly likely to be taken for any given
"if" statement, but a runtime could gather that information and
recompile appropriately.
In my case, the C# compiler is making IL for a single function that spans
~70 lines. I have rewritten that same function myself in IL in 30 lines.
My problem is more complex, but to give you a basic idea of what the C#
compiler makes vs. what I can code myself I'll give you an example:

here is the C# code for an 'Add' function:

that's a ratio of 8 lines vs. my 4 lines. Which one will be faster?

I would expect there to be no difference at runtime, because of the JIT
being smart. Have you benchmarked it to show that your version actually
*is* faster, as you're implying?
 
J

Jon Skeet

Chris LaJoie said:
Thats a good question. I have several methods that must loop through
thousands of objects in an array, and manipulate each of them. When I
disassemble it, I find that C# simply isn't doing it in the most efficient
way, so I'd like to write 3 methods of my class in IL, and the rest in C#.
This apparently isn't possible.

Have you tried putting those other methods in a different assembly just
to make sure that your version actually *is* more efficient at runtime?

Is this definitely your performance bottleneck in the first place?
 
M

Mattias Sjögren

Chris,
.method private hidebysig static int32 Add(int32 op1,int32 op2)
{
.locals init (int32 retval)
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: add
IL_0003: stloc.0
IL_0004: br.s IL_0006

IL_0006: ldloc.0
IL_0007: ret
}

Surely you're looking at a non-optimized debug build? Compile with /o
and look again, the C# compiler can definitely do better than this.



Mattias
 
L

Lloyd Dupont

BTW it doesn't compile at runtime everytime.
there is different policy on when a given method or assembly or method
should be compiled (you could choose but I don't remember how) however each
method is compiled only once and cached.
 
J

Jon Skeet

Lloyd Dupont said:
BTW it doesn't compile at runtime everytime.
there is different policy on when a given method or assembly or method
should be compiled (you could choose but I don't remember how) however each
method is compiled only once and cached.

I don't believe that's true. I believe that unless you specifically
ngen the assembly, the JITted code won't be stored on disk anywhere.
(It's cached within the lifetime of the app, of course, but that's a
different matter.)

In a future version it *could* cache the code, of course, but I don't
believe it does at the moment.
 
J

Jon Skeet

that's a ratio of 8 lines vs. my 4 lines. Which one will be faster?

Just for fun, I *did* actually benchmark this - and to my surprise,
your version *was* faster (over 1000000000 iterations, it took about
3.5s as opposed to 6.3 for the original). I'm very surprised that the
JITter couldn't figure the two out to be identical.

However, I got almost exactly the same benefit by moving the hand-
optimized code into a separate DLL. It means it needs to be public,
which isn't terribly pleasant, but it's unlikely that there will be
many routines which actually *do* benefit significantly from this kind
of approach (and only those which actually *are* bottlenecks in the
app) so this shouldn't be needed very often.
 
W

Willy Denoyette [MVP]

Jon,
See inline ****

Willy.


*** I guess OP meant, cached in memory which is correct.
 
C

Chris LaJoie

Interresting test Jon. A few days ago I benchmarked our program's functions
written in C# (compiled with /o) against the ones written in IL and got
similar results. My IL code was nearly twice as fast. I would post the
code for you to see and test, but I don't want to give out company code and
risk losing my job. ;)

I believe that inline IL would be invaluable in situations where speed is
essential, and the C# compiler isn't compiling it in the most efficient way.
I plan on writing up a formal request/suggestion regarding inline IL in .NET
languages to Microsoft. I value your input and I'd like to ask your opinion
on this. Do you believe this would be a valuable addition, or am I wasting
my time?

Thanks,
Chris LaJoie
 
M

Michael Roper

Chris said:
Interresting test Jon. A few days ago I benchmarked
our program's functions written in C# (compiled with /o)
against the ones written in IL and got similar results.

I'm new, but it certainly seems that compiler/jitter aren't very good. The
example given earlier was particularly striking. Is there an expectation
that they'll get better, soon? Are there competing products that do a
better job?

An inline feature would be nice, but it would be even better to have a
competent compilation in the first place.

Michael Roper
 
C

Chris LaJoie

Michael,
The code you saw earlier was not optimized code. I'm not entirely sure what
the code would look like if it was optimized, but I can tell you that for
the most part, the C# (or VB) compiler does an awesome job.

You have probably heard that assembly is faster than C/C++, but do you know
why? It's because the human mind is much more inventive and can think about
problems in many different ways to come up with the best/fastest solution.
A compiler has limitations. Someone please jump in if I'm wrong, but this
is how I look at it.

The only time I would ever write in a 'low-level' language like IL is if
speed was very important (which isn't very often). It's only these rare
situations that I believe IL would be invaluable.


Chris LaJoie
 
J

Jon Skeet

Chris LaJoie said:
Interresting test Jon. A few days ago I benchmarked our program's functions
written in C# (compiled with /o) against the ones written in IL and got
similar results. My IL code was nearly twice as fast. I would post the
code for you to see and test, but I don't want to give out company code and
risk losing my job. ;)

Understandable :)
I believe that inline IL would be invaluable in situations where speed is
essential, and the C# compiler isn't compiling it in the most efficient way.
I plan on writing up a formal request/suggestion regarding inline IL in .NET
languages to Microsoft. I value your input and I'd like to ask your opinion
on this. Do you believe this would be a valuable addition, or am I wasting
my time?

I would personally rather MS spent the time on improving the
compiler/jitter to come up with the same kind of code to start with -
that would give a bigger bang for the buck, I believe. I'd rather not
*have* to resort to IL for the kind of method you showed before.

On the other hand, they've already got the assembler itself - maybe it
wouldn't be a bad idea (especially as there are things you can do with
IL that you can't do in C#, as mentioned before). I think I'd probably
suggest making it pretty limited though: rather than inline IL within a
method, keep it to a per-method basis. That would mean it could stick
with the current IL without anyone having to worry about which
parameter/local variable corresponded with which IL stack variable,
etc.
 
M

Magnus Lidbom

Chris LaJoie said:
Michael,
The code you saw earlier was not optimized code. I'm not entirely sure what
the code would look like if it was optimized,
..method private hidebysig static int32 Add(int32 op1,
int32 op2) cil managed
{
// Code size 4 (0x4)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: add
IL_0003: ret
} // end of method Test::Add

Looks sort of familiar to me ;)

<SNIP>

/Magnus Lidbom
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top