Convert a Partial to Method/Block for Speed

Convert a partial to method/block for speed

Here is an example from one of my previous answers. It's extracted from PartialRenderer sources.

- local_names = [:i]
- partials = {}
- 1000.times do |i|
- name = 'name_%s' % (i % 10)
- partials[name] ||= lookup_context.find_template(name, lookup_context.prefixes, true, local_names)
= partials[name].render(self, i: i)

I'd recommend you to wrap it with a helper method. Keep in mind that locals' names appear here twice: first in local_names as an array and second in hash's keys passed as the second argument of #render method.

How to speed up rendering of a rails_partial with form elements?

You should generally not iterate in your views. Instead, you should use collection rendering which will perform the iteration for you, and is known to be faster in addition to allowing you to implement fragment caching:

Your view:

= render @items, parent: @parent

Your partial (ie _item.html.haml):

- item_no = 'item' + item.item_no.to_s
.item
= form_for item, remote: true, html: { multipart: true, autocomplete: 'off' } do |f|
= f.hidden_field :parent_id, :value => @parent.id
= f.hidden_field :image, value: item.image
...
...
// 5-6 attributes

- if parent.has_item_numbers?
.serial-number{ class: ('right' if item.item_no.odd?) }
= item.item_no

Inverse of PartialFunction's lift method

Not in the library, but it's easy to build. However, isDefinedAt will have to fully evaluate the function making it more expensive than is typical for partial functions built from pattern matching and also possibly result in unwanted side effects.

scala> def unlift[A, B](f : (A => Option[B])) = new PartialFunction[A,B] {
| def isDefinedAt(x : A) = f(x).isDefined
| def apply(x : A) = f(x).get
| }
unlift: [A,B](f: (A) => Option[B])java.lang.Object with PartialFunction[A,B]
scala> def f(x : Int) = if (x == 1) Some(1) else None
f: (x: Int)Option[Int]
scala> val g = unlift(f)
g: java.lang.Object with PartialFunction[Int,Int] = <function1>
scala> g.isDefinedAt(1)
res0: Boolean = true
scala> g.isDefinedAt(2)
res1: Boolean = false
scala> g(1)
res2: Int = 1
scala> g(2)
java.util.NoSuchElementException: None.get
at scala.None$.get(Option.scala:262)
at scala.None$.get(Option.scala:260)
at $anon$1.apply(<console>:7)
at scala.Function1$class.apply$mcII$sp(Function1.scala:39)
at $anon$1.apply$mcII$sp(<console>:5)
at .<init>(<console>:9)
at .<clinit>(<console>)
at RequestResult$.<init>(<console>:9)
at RequestResult$.<clinit>(<console>)
at RequestResult$scala_repl_result(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at scala.tools.nsc.Interpreter$Request$$anonfun$loadAndRun$1$$anonfun$apply$17.apply(Interpreter.scala:988)
at scala.tools....

A purist might also wrap isDefinedAt with a try/catch block to return false on exceptions.

How to properly use the `block` function

You pass a matrix to the function, and this matrix is not copied, it's a reference to the same matrix. As Maxima documentation says,

Matrices are handled with speed and memory-efficiency in mind. This means that assigning a matrix to a variable will create a reference to, not a copy of the matrix. If the matrix is modified all references to the matrix point to the modified object (See copymatrix for a way of avoiding this)

So you need to copy a matrix to handle it independently:

f(x,y):=block([x:x, y:y, m:m, n:n],
m:copymatrix(x),
n:copymatrix(y),
m[1]:-m[1],
m.n);

C - fastest method to swap two memory blocks of equal size?

Your best bet is to maximize registers usage so that when you read a temporary you don't end up with extra (likely cached) memory accesses. Number of registers will depend on a system and registers allocation (the logic that maps your variables onto actual registers) will depend on a compiler. So your best bet is I guess to expect only one register and expect its size to be the same as the pointer. Which boils down to a simple for-loop dealing with blocks interpreted as arrays of size_t.

Are helpers really faster than partials? What about string building?

I ended up reading an excellent article at http://www.infoq.com/articles/Rails-Performance ("A Look At Common Performance Problems In Rails"). Then I followed the author's suggestion to cache computations during request processing:

def estimated_costs
@estimated_costs ||=
begin
# tedious vector math
end
end

Because my worksheet does stuff like the above over and over, and then builds on those results to calculate some more rows, this resulted in a 90% speedup right off the bat. Should have been plain as day, but it started with just a few totals, then I showed the prototype to the customer, and it snowballed from there :)

I also wondered whether my array-based math might be inefficient, so I replaced the Ruby Arrays of numbers with NArray (http://narray.rubyforge.org/). The speedup was negligible but the code's cleaner, so it's staying that way.

Finally, I put some object caching in place. The "magic numbers" in the database only change a few times a year at most, and some of them are encrypted, but they need to be used in most of the calculations. That's low-hanging fruit ripe for caching, and it shaved off another 1.25 seconds.

I'll look at eager loading of associations next, as there's probably some time to save there, and I'll do a quick comparison of sending "just the data" vs sending the HTML, as @tadman suggested. About the only partial I can cache is the navigation sidebar. All of the other content depends on the request parameters.

Thanks for your suggestions!

How to process array data in chunks where last chunk may be a partial size

The code needs to send up to only the available data. To do that the general approach would be to send full sub-blocks until the last sub-block which may be a partial one. That can be determined by simple maths logic to work out how much the current iteration should send based on how much data is left.

The code changes would be:

  1. Change siz to be the real number of entries in the array: siz = sizeof(data)/sizeof(data[0]).
  2. Change rang in the function call to `(ind + rang <= size ? rang : size - ind)``. That is, the size passed to the function call depends on how much data is left.

MVC 6 Change where a view block renders

You can implement this behavior using tag helpers.

Let's say you create a tag helper InlineScriptConcatenatorTagHelper targetting the <script> tag, where you basically remove its contents from the output but keep them in memory for later use:

[HtmlTargetElement("script", Attributes = "inline-bundle-add")]
public class InlineScriptConcatenatorTagHelper: TagHelper
{
private IHttpContextAccessor httpContextAccessor;

public InlineScriptConcatenatorTagHelper(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}

[HtmlAttributeName("inline-bundle-add")]
public string BundleName { get; set; }

public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
//Get the script contents
var contents = await context.GetChildContentAsync();
var scriptContent = contents.GetContent();

//Save them into the http Context
if (httpContextAccessor.HttpContext.Items.ContainsKey(BundleName))
{
var scripts = httpContextAccessor.HttpContext.Items[BundleName] as ICollection<string>;
scripts.Add(scriptContent);
}
else
{
httpContextAccessor.HttpContext.Items[BundleName] = new List<string> { scriptContent };
}

//suppress any output
output.SuppressOutput();
}
}

You can then create a similar tag helper InlineScriptTagHelper where you will basically concatenate and render all the contents you collected from the previous helper:

[HtmlTargetElement("script", Attributes = "inline-bundle-render")]
public class InlineScriptTagHelper : TagHelper
{
private IHttpContextAccessor httpContextAccessor;

public InlineScriptTagHelper(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}

[HtmlAttributeName("inline-bundle-render")]
public string BundleName { get; set; }

public override void Process(TagHelperContext context, TagHelperOutput output)
{
//if no scripts were added, suppress the contents
if (!httpContextAccessor.HttpContext.Items.ContainsKey(BundleName))
{
output.SuppressOutput();
return;
}

//Otherwise get all the scripts for the bundle
var scripts = httpContextAccessor.HttpContext.Items[BundleName] as ICollection<string>;

//Concatenate all of them as set them as the contents of this tag
output.Content.SetContentEncoded(String.Join("", scripts));
}
}

With this in place, you could add as many script blocks in your views and assign them an inline bundle name:

<script inline-bundle-add="myInlineBundle">
var title = '@ViewData["Title"]';
var greet = function (message) {
console.log(message);
}
</script>

...

<script inline-bundle-add="myInlineBundle">
greet(title);
</script>

Then add a single script element in your _Layout.cshtml that will render the concatenated output of all the inline scripts with the same bundle name:

    ...

<script inline-bundle-render="myInlineBundle"></script>
</body>

The rendered output will contain a single script element concatenating all the scripts you included in the inline bundle:

    ...
<script>
var title = 'Home Page';
var greet = function (message) {
console.log(message);
}

greet(title);
</script>
</body>

Don´t forget to register the tag helpers in your assembly by adding a @addTagHelper directive to the _ViewImports.cshtml file

EDIT

Check out the github project created by @SvdSinner. It has taken the approach described here and created a tag helper that supports deduplication and dependency ordering. (With the aim of supporting minification and provide a nuget package)



Related Topics



Leave a reply



Submit