Gateway Script or Expression in Tag? for better performance

My Ignition (Perspective) project needs to calculate few dozen tags from existing tags every few seconds. Would it be better (in terms of performance) to use Expressions inside memory tags or a Gateway script to calculate those values and then write to the memory tags? These calculated tags use existing tags which in turn get their values from PLC via OPC. The calculated tags will be memory tags. The calculations are simple but uses 8 tags each with simple if-then and arithmetic formula.
The Gateway script option will require multiple system.tag.readBlocking and system.tag.writeAsync statements (even if I put the tags in a list) so worried about performance
Note: the Expression option will also need to read a bunch of neighboring tags from various tag folders but not use the system functions listed above so will it provide any performance benefits or save system resources?

A few dozen tags, every few seconds? Performance isn't going to be a bottleneck - write it whatever you're most comfortable maintaining.

A few thousand tags, or trying to run every few hundred milliseconds? Then you've got a performance concern worth thinking seriously about.

Tens/hundreds of thousands of tags, or trying to run in tens of milliseconds? Then you've got an engineering problem worth giving someone money to write a custom module for. Or an architecture problem :smile:

2 Likes

The script was running slow (took 51s, instead of 3s). So this means I need to look elsewhere for the sluggish behavior. Any suggestions on how to troubleshoot it other than typical Pythonic methods?

How are you using these computed tags? If they are only for UI display purposes, compute them in the UI in expression bindings, on demand. (Only for the ones displayed in that UI.) If you need history of the computed values, use expression tags, not memory tags, unless you cannot compute the result without a script. (And then you might want my Integration Toolkit module.)

1 Like

Thanks. These tags are used for all the purposes you stated. So I will go ahead and spread the computation between GW scripts (running every few seconds), Expression tags, and computation in UI itself with Expressions

How would we make “typical Pythonic” suggestions? You haven’t shared even pseudo code.

There are many gotchas when writing scripts in relation to tag computations and Ignition scripting in general. 51s for a script is a very long time, I have done some very heavy computations in scripts and I don’t remember ever having a script that ran longer than a few seconds and even then I thought something was wrong, and moved to better more optimal methods.

Sorry, didn't meant that. I meant, any timing analysis other than measuring time before and after the script and subtracting them, ex.

from timeit import default_timer as timer
start = timer()
print(1+2)
end = timer()
print(end - start)

PS: the code itself is just tedious and nothing fancy, lots and lots of read tags, simple calculations, few if-then-else, write results back to other tags. Too long and not worth sharing. Just looking for some statements or Gateway tools used to analyze code. Brute force method would be to break it into smaller pieces and measure each ones run time

Are you reading all tags at once or individually?

What might seem like tedious mundane code might be killing performance.

Even with taking Paul and Phil’s advice, you’ll want to make sure you’re reading and writing to tags using the most effective method in any scripts that you do end up using.

Also as an aside. I would shy away from using python imports in ignition scripting when there are suitable Java substitutes.

1 Like

You could use a decorator function?

from java.lang.System import nanoTime
import functools

TIME_EXECUTION_ENABLE_REPORT = True

def time_execution(func):
	"""
	Decorator to measure and print the execution time of a function.
	"""
	@functools.wraps(func)
	def wrapper(*args, **kwargs):
		start_time = nanoTime()
		result = func(*args, **kwargs)
		end_time = nanoTime()
		if TIME_EXECUTION_ENABLE_REPORT:
			print "Execution time for `{}`: {:.3f} ms".format(func.__name__, (end_time - start_time)/1000.0/1000)
		return result
	return wrapper

Then use it like this:

@time_execution
def foo():
	i = 0
	while i < 10000:
   		i += 1
foo()
>> Execution time for `foo`: 2.210 ms
3 Likes