It is currently July 19th, 2019, 3:31 pm

Controlling Dynamic Variables

Tips and Tricks from the Rainmeter Community
User avatar
smurfier
Moderator
Posts: 1937
Joined: January 29th, 2010, 1:43 am
Location: Willmar, MN

Controlling Dynamic Variables

smurfier » May 8th, 2014, 7:58 pm

The use of Dynamic Variables has been part of Rainmeter since the dawn of time. It’s one of those things that is set to either “on” or “off”. Sometimes this can have an impact on the performance of the skin. All of that reading every single option, doing all of that math, parsing each variable can take some effort.

With the addition of the !SetOption bang we were given a third option for Dynamic Variables. When you use !SetOption it sets Dynamic Variables to “on” for a single update cycle for the targeted section. This can allow us to use Dynamic Variables only when we want to.

While we have had the use of !SetOption for quite some time it wasn’t until somewhat recently that we’ve had very many methods for testing when we want to use it in this fashion.

Let’s take a look at what we’re talking about.

Code: Select all

[Rainmeter]
Update=1000

[MeasureHour]
Measure=Time
Format=%I
OnChangeAction=[!SetOption MeterHour DynamicVariables 0]

[MeterHour]
Meter=String
Text=[MeasureHour]
In this instance we know that [MeasureHour] will only change it’s value once every hour. Knowing this there really is no need to read and parse the options on [MeterHour] every update, especially since we know that would be once every second.

When you add IfMatch, IfCondition, and IfAbove/Below/Equal to this method you have a very powerful level of control over when to use Dynamic Variables.

In most cases this method won’t make much of a difference. However there is at least one case where it can be a life saver.

Code: Select all

[MeasureWebParser]
Measure=Plugin
Plugin=WebParser
Url=http://somewebsite.com
RegExp=^(.*)$

[MeterString]
Meter=String
MeasureName=MeasureWebParser
ClipString=2
This creates an issue where [MeterString] starts out with a H and W of 0 and that never changes due to the initial value of [MeasureWebParser] being empty. Luckily this can be fixed with the addition of Dynamic Variables. The easy method is just to set DynamicVariables=1 on the meter, but that’s what we’re trying to avoid here.

FinishAction=[!SetOption MeterString DynamicVariables 0]

Adding the above line to [MeasureWebParser] easily works around our issue without having to evaluate Dynamic Variables on every single update.

While this method is unnecessary for the majority of authors I hope that a few will find it useful in discovering a solution to their resource hungry skin.
GitHub | DeviantArt | Tumblr
This is the song that never ends. It just goes on and on my friends. Some people started singing it not knowing what it was, and they'll continue singing it forever just because . . .
User avatar
exper1mental
Posts: 282
Joined: January 9th, 2013, 7:52 pm
Location: Southern USA

Re: Controlling Dynamic Variables

exper1mental » May 18th, 2014, 5:45 pm

Wouldn't it work just as well to use UpdateDivider to improve performance and when changing something related to a meter with an UpdateDivider adding [!UpdateMeter MeterName] to the action?

Using one of your examples:

Code: Select all

[Rainmeter]
Update=1000

[MeasureHour]
Measure=Time
Format=%I
OnChangeAction=[!UpdateMeter MeterHour]

[MeterHour]
Meter=String
Text=[MeasureHour]
UpdateDivider=-1
DynamicVariables=1
This creates a meter updated only when MeasureHour changes.
Image
User avatar
jsmorley
Developer
Posts: 19294
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Controlling Dynamic Variables

jsmorley » May 18th, 2014, 6:10 pm

Great tips, but in my view is it possible to go to great lengths and not really save any appreciable resources.

As far as updating meters, remember that setting a meter to UpdateDivider=-1 doesn't keep Rainmeter from redrawing the meter, if ANY single pixel anywhere else in the skin is redrawn. It is all or nothing as far as redrawing the skin goes. There are no actual "layers" in a Rainmeter window, and in reality the entire thing is one plane drawn in order on the single window. The entire skin is always redrawn on every update. This is why you generally, if not always, follow !UpdateMeter with !Redraw, and why there isn't a !RedrawMeter.

UpdateDivider=-1 used to make a huge difference with image meters, as the image was retrieved from disk on each update. This has not been true for some time, images are now "cached" and there is no real savings from "freezing" a static image meter anymore.

What you save with UpdateDivider=-1 on a meter is having the meter retrieve the current value of any measures or variables used in it and resolve formulas in options, which at the end of the day is probably the least resource intensive work Rainmeter does. Moving bits from one bucket to another inside a program and doing addition and subtraction are what computers do with the least amount of effort.

Not saying that fine-tuning isn't a good thing, and saving a CPU cycle is saving a CPU cycle. However, I suspect it is the work that measures do (and possibly redrawing a complicated skin with a low update rate) that use the most resources. My focus would be on making sure that as much as possible, measures are updated only and exactly when they need to be. I think you would have to have a boatload of meters indeed before throttling the "updates" of meters would make any visible difference.
User avatar
Mordasius
Posts: 1059
Joined: January 22nd, 2011, 4:23 pm
Location: GMT +8

Re: Controlling Dynamic Variables

Mordasius » May 19th, 2014, 5:35 am

jsmorley wrote:I think you would have to have a boatload of meters indeed before throttling the "updates" of meters would make any visible difference.
..or you have one or more large images in the skin.

The S.T.A.L.K.E.R. collection has a blood effect meter that enlarges a 783x517 image to 1564x1034 and has the ImageAlpha value set by a CPU measure (the higher the CPU usage the more opaque the image). The meter has UpdateDivider=-1 and is updated with [!UpdateMeter] and [!Redraw] !Bangs when CPU usage reaches certain levels. There are also some string meters that are similarly 'throttled' with UpdateDivider=-1 and updated with bangs. This keeps CPU usage to acceptable levels for most of the time.

The screenshots show the increase in CPU usage caused by commenting out UpdateDivider=-1 in the string meters whilst leaving UpdateDivider=-1 in the image meter.
CPUusage.jpg
I guess this illustrates JS Morley's point that "that setting a meter to UpdateDivider=-1 doesn't keep Rainmeter from redrawing the meter, if ANY single pixel anywhere else in the skin is redrawn". I had no idea about this effect and it seems that despite UpdateDivider=-1 on the image meter, the 1564x1034 image is being redrawn on every update without UpdateDivider=-1 on the string meters. The odd thing is that the increase in CPU usage occurs even when the string meters are hidden.
:???:
You do not have the required permissions to view the files attached to this post.
User avatar
jsmorley
Developer
Posts: 19294
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Controlling Dynamic Variables

jsmorley » May 19th, 2014, 9:07 am

Interesting Mordasius...

I suspect that the resource usage isn't due to redrawing a large image, as once the image is loaded and cached that is no different than redrawing any other skin, but that the image is being resized from 783 x 517 to 1564 x 1034. It must be doing that resize each time it redraws the skin. An interesting test would be to actually size the image to 1564 x 1034 and leave off W and H to see what if any difference that makes.

Edit:

Yeah, that makes all the difference in the world. I have two images, one 450 x 605, and the other the same image resized in Photoshop to 1000 x 1344. I have a string meter that is just displaying a counter from 1 to 100.

If I use the large image and don't resize it in Rainmeter, then the resource usage is negligible. Putting UpdateDivider=-1 on the image meter makes no noticeable difference at all.

If I use the small image but resize it in Rainmeter to 1000 x 1344 with W and H, then the CPU usage of the skin is significantly higher. Putting UpdateDivider=-1 on the image meter doesn't help. Clearly, resizing an image in memory using W and H is expensive when the skin is redrawn, and where possible should be avoided with large image sizes and / or complicated skins.

However, if I disable the string meter or use UpdateDivider=-1 on it, then either way, resizing the image or not, uses almost no CPU. UpdateDivider=-1 or not on the image meter basically never seems to make any visible difference.

Not sure exactly what is going on under the covers, but it seems that the image meter in and of itself is very efficient. The image is reloaded and resized (if needed) one time when the skin is loaded/refreshed. The normal update and redraw of the skin uses very few resources. However when any other meters in the skin are being updated and the entire skin is redrawn, then the work to resize the image is done on every skin update. UpdateDivider=-1 on the image meter does not avoid this work, it is done in the "redraw" not the "update".

I'm not seeing the dramatic difference you are, the skin uses like double or triple the CPU, not 10 times the CPU, but it might be the dynamic ImageAlpha on top of the resizing that is causing yours to eat more cycles than mine.
User avatar
Mordasius
Posts: 1059
Joined: January 22nd, 2011, 4:23 pm
Location: GMT +8

Re: Controlling Dynamic Variables

Mordasius » May 19th, 2014, 12:29 pm

smurfier wrote:FinishAction=[!SetOption MeterString DynamicVariables 0]
Adding the above line to [MeasureWebParser] easily works around our issue without having to evaluate Dynamic Variables on every single update.
Sorry to have sorta hijacked this thread, but when you use something like FinishAction=[!SetOption MeterString DynamicVariables 0], doesn't that set DynamicVariables to the default value meaning that MeterString will never use dynamic variables? If you use FinishAction=[!SetOption MeterString DynamicVariables 1] won't that mean that MeterString will have DynamicVariables=1 and therefore always use dynamic variables?
User avatar
jsmorley
Developer
Posts: 19294
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Controlling Dynamic Variables

jsmorley » May 19th, 2014, 12:32 pm

Mordasius wrote: Sorry to have sorta hijacked this thread, but when you use something like FinishAction=[!SetOption MeterString DynamicVariables 0], doesn't that set DynamicVariables to the default value meaning that MeterString will never use dynamic variables? If you use FinishAction=[!SetOption MeterString DynamicVariables 1] won't that mean that MeterString will have DynamicVariables=1 and therefore always use dynamic variables?
Correct. Until the skin is refreshed of course. If you use !SetOption in the WebParser measure to set DynamicVariables=1 on a meter, you would need to use !SetOption again at some point to turn it "off", or that meter would remain dynamic.

That is not what Smurfier is suggesting though. He is saying have DynamicVariables be "0", then use !SetOption to set DynamicVariables to "0". While it might be counter-intuitive, what that does is make the meter "dynamic" for one update, while !SetOption does its thing. !SetOption isn't actually doing ANYTHING, but the meter still is dynamic for just one update.

What any !SetOption does is make the target measure or meter "dynamic" for one update, so the option can be set. During that one update the entire measure or meter is dynamic and evaluates all options.
User avatar
Mordasius
Posts: 1059
Joined: January 22nd, 2011, 4:23 pm
Location: GMT +8

Re: Controlling Dynamic Variables

Mordasius » May 19th, 2014, 12:52 pm

jsmorley wrote:Interesting Mordasius...
More playing around with the skin suggests that increasing the size of blood.png rather than scaling it with W and H values does indeed reduce CPU usage when the string meters don't have UpdateDivider=-1. Putting UpdateDivider=-1 back in the string meters reduces CPU usage back to the same levels as with the 783x517 blood image scaled using W=1564 and H=1034.

Somewhat surprisingly, changes to ImageAlpha using !SetOption don't seem to have any noticeable effect on CPU usage whatever the UpdateDivider settings in other String or Image meters.

Some (tentative) conclusions on minimising CPU usage:

1) It doesn't matter what UpdateDivider you put in an image meter if you have a string meter with the default Update=1000 in the same skin.

2) Using W= and H= to scale an image meter increases CPU usage unless all other meters have UpdateDivider=-1.

3) Using UpdateDivider=-1 in string meters reduces CPU usage even if you use W= and H= to scale an image in an ImageMeter in the same skin.

4) Changing ImageAlpha using !SetOption doesn't have any noticeable effect on CPU usage regardless of UpdateDivider settings in other meters in the skin.
User avatar
jsmorley
Developer
Posts: 19294
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Controlling Dynamic Variables

jsmorley » May 19th, 2014, 1:25 pm

Mordasius wrote: Some (tentative) conclusions on minimising CPU usage:

1) It doesn't matter what UpdateDivider you put in an image meter if you have a string meter with the default Update=1000 in the same skin.

2) Using W= and H= to scale an image meter increases CPU usage unless all other meters have UpdateDivider=-1.

3) Using UpdateDivider=-1 in string meters reduces CPU usage even if you use W= and H= to scale an image in an ImageMeter in the same skin.

4) Changing ImageAlpha using !SetOption doesn't have any noticeable effect on CPU usage regardless of UpdateDivider settings in other meters in the skin.
I think the best conclusions we can draw based on what we have tested is:

1) UpdateDivider on meters controls when the current value of any bound measures or dynamic variables / formulas used in options are re-evaluated. UpdateDivider has nothing to do with when or how the meter is "redrawn", only "updated".

2) Resizing an image meter with W and H is expensive, is done as part of the "redraw", and will be done on every skin update if any other meters of any type are updated on every skin update.

Basically what is going on is that each meter in the skin sets a "I've been updated" flag on each skin update. Either "true" or "false". When the skin is redrawn, an image meter will ask each meter in the skin "hey, is your flag "true" or "false?". If any are "true" the image meter will again resize the image based on the W and H. The evolution of this is based on the fact that the "original" image is what is stored in memory, not the "resized" image. This was initially designed this way to save on memory. However, in this case the memory savings are obtained at the expense of CPU. Might be something we look at, but it might not be easy to change, as this is sorta baked into the DNA of how meters work.
Wallboy
Posts: 62
Joined: October 1st, 2012, 4:53 am

Re: Controlling Dynamic Variables

Wallboy » August 15th, 2014, 1:40 am

Hmm I had a post here yesterday and it seems to have disappeared. Perhaps a hiccup with the forum...Nonetheless,

I have run into similar issues with using the Universal Transitions script and fading images. The images my WebParser downloads are 300x300, but I only need 40x40. If I use the W= and H= for the meters, the fade uses approximately 5-7% CPU (on a 7 year old rig). If I don't use W= and H= and let it fade the entire sized image, the CPU usage is exactly the same. When I actually resize those same images in another program down to 40x40, the fade script uses roughly 1.5-2% CPU. Fairly large difference.

So my current pipe is to download the image, resize it using the RunCommand plugin that calls an image resize command line tool, and then finally fade in/out the resized image.

Not sure if there is an easier way to do this. The program I use to resize is convert from ImageMagick which is a bit large because of all the additional features it has that I don't use. If anyone knows of a smaller executable command line program that resizes images, let me know. Might see if an AutoIt script would be better.