...or I could compile the script code and re-execute the compiled code over and over. But if I do that, it doesn't seem that I can pass any parameters to the "computeFoo" method in my script code. Instead, it seems that I have to do something like
this:

I've done some timing tests, and while the compiled approach is faster, it's not as dramatically faster as I had expected (it's more on the order of 20-30% faster). And if I make the "Foo" class a .NET class such as a Dictionary<>, the speed
differential between the compiled and dynamic evaluation approach is narrowed even further.

Considering that parameter passing from C# to the script code is less elegant with the compiled approach vs. using the Script member, I'm almost tempted to ditch compilation. The script code is something that is provided by our customers, and it's much more
intuitive to tell them to create a "computeFoo" function that looks something like the first example versus the second one. Or am I missing something fundamental about how to pass parameters to compiled scripts?

Your first technique above (the one that goes through ScriptEngine.Script) is the right way to invoke an existing script function multiple times. We'd actually recommend caching a reference to the function:

Your second code sample executes the same function statement over and over, needlessly redeclaring the function 50000 times. In addition to making it difficult to pass arguments, this could create 50000 copies of the function for the garbage collector
to clean up. We say could because V8 might be smart enough to optimize the duplicate functions away.

Frankly we're surprised that the second technique is faster. Our suspicion is that this is because (a)
computeFoo() is trivial, and (b) V8 is very good at avoiding duplicate work.

By the way, in your second code sample, the line foo = getFoo(i); only reassigns the .NET variable
foo; it does not affect the script variable hostFoo. If we're reading your code correctly, this means that
computeFoo() will operate on the same Foo instance every time. The code should probably be something like this instead:

But again, the other way is much better. It's simpler and cleaner, and it should be faster and more efficient in real-world situations.

If you're wondering then what the point of compilation is, consider a scenario where your client provides arbitrary script code (not a single script function) that you must execute many times. In this situation, the following:

Wow, I'm glad I asked then, because my test results so far are that re-evaluation of compiled code is faster than dynamic evaluation using the Script object, and coming from a C/C++/C# background I just intuitively started from the position that re-executing
"compiled" code would not mean that I was redeclaring that code on each pass. Thanks...

Actually, on second thought, roughly speaking they should be equally fast, as both amount to re-execution of compiled code. The second technique does redeclare the function; that is, it reassigns a global script property. But it probably doesn't
duplicate the function, so the redeclaration is effectively a no-op.

However, note that the first technique correctly passes an argument, whereas the second one doesn't, due to the bug described above. Exporting a .NET object to the script engine is not cost-free. Also, the first technique redundantly imports the function from
the script engine, and that also is not cost-free. Together these factors might add up to the discrepancy you're seeing. Would it be possible to make the recommended changes (cache the script function in the first case, and fix the argument passing in the
second) and rerun the timing tests?

We'd like to reiterate that in both cases you're compiling code once and re-executing it many times. The two techniques have minor differences, but both should be much faster than recompiling the code many times.

The difference in average runtimes between the two techniques is now so small as to be negligible. That's good, because using the V8ScriptEngine.Script object to invoke the JavaScript method is infinitely preferable. Thanks!