Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NodesetCompiler use Byte array instead of string #1621

Merged
merged 4 commits into from Jul 31, 2018

Conversation

nizamogluyekta
Copy link
Contributor

@nizamogluyekta nizamogluyekta commented Feb 28, 2018

Due to issue #1254 : The huge strings are changed as arrays of ASCII values of each element inside for ByteArrays. And also some implementations are done due to task.

Fixes #1254

Copy link
Member

@Pro Pro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! As discussed in person earlier, here some comments:

In general I think it would be better to try to avoid the additional malloc call since the data is already on the stack we can just pass it as a reference

@@ -40,7 +46,15 @@ def generateXmlElementCode(value, alloc=False, max_string_length=0):
return "UA_XMLELEMENT{}({})".format("_ALLOC" if alloc else "", splitStringLiterals(value, max_string_length=max_string_length))

def generateByteStringCode(value, alloc=False, max_string_length=0):
return "UA_BYTESTRING{}({})".format("_ALLOC" if alloc else "", splitStringLiterals(value, max_string_length=max_string_length))
return "char stringArr?{}! = {};\n\
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you use this cryptic ? and ! notation and replace it later? Can't you directly use []? Adding the byte array then has to use the correct brackets.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use 4 spaces indentation

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The alloc parameter is not used anymore, remove it from the parameters

variable.data = (UA_Byte *)UA_malloc(variable.length);\n\
memcpy(&stringArr, &variable.data, variable.length)".format(len(stringtoByteArray(value, max_string_length=max_string_length)) \
,stringtoByteArray(value, max_string_length=max_string_length),\
len(stringtoByteArray(value, max_string_length=max_string_length)) )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to avoid calling stringtoByteArray three times. Just store the result before and then use the resulting value here.

return "UA_BYTESTRING_NULL" if not node.value else generateByteStringCode(re.sub(r">\s*<", "><", re.sub(r"[\r\n]+", "", node.value)), alloc=asIndirect, max_string_length=max_string_length)
return prepend + "UA_BYTESTRING_NULL" if not node.value else generateByteStringCode(re.sub(r">\s*<", "><", re.sub(r"[\r\n]+", "", node.value)),
alloc=asIndirect, max_string_length=max_string_length).replace("[","{").replace("]","}")\
.replace("?","[").replace("!","]")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See previous comment, then remove the last two replace operations.

@@ -521,7 +521,7 @@ def generateNodeCode_begin(node, nodeset, max_string_length, generate_ns0, paren
code.append("UA_NODEID_NULL,")
code.append("(const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_{}ATTRIBUTES],NULL, NULL);".format(node.__class__.__name__.upper().replace("NODE" ,"")))
code.extend(codeCleanup)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to avoid changes like this

return "char stringArr?{}! = {};\n\
UA_ByteString variable;\n\
variable.length = {};\n\
variable.data = (UA_Byte *)UA_malloc(variable.length);\n\
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a check if variable.data is NULL and return UA_STATUSCODE_BADOUTOFMEMORY.

@Pro Pro changed the title solution for https://github.com/open62541/open62541/issues/1254 NodesetCompiler use Byte array instead of string Mar 2, 2018
@Pro Pro added this to the 0.4 milestone Mar 2, 2018
@coveralls
Copy link

coveralls commented Mar 7, 2018

Coverage Status

Coverage decreased (-0.08%) to 77.076% when pulling 81d76ca on nizamogluyekta:feature/nodeset_bytearray into 961f0db on open62541:master.

@Pro Pro force-pushed the feature/nodeset_bytearray branch from d2d20fd to 81d76ca Compare March 21, 2018 14:24
@Pro Pro force-pushed the feature/nodeset_bytearray branch from 81d76ca to eb48fdc Compare July 11, 2018 11:19
@Pro
Copy link
Member

Pro commented Jul 11, 2018

@jpfr the Opc.Ua node is a huge bytestring. With this PR we improve the handling of huge bytestrings, but currently there is the following issue:

The generated code looks like this:

/* Opc.Ua - ns=0;i=7617 */

static UA_StatusCode function_ua_namespace0_146_begin(UA_Server *server, UA_UInt16* ns) {
UA_StatusCode retVal = UA_STATUSCODE_GOOD;
UA_VariableAttributes attr = UA_VariableAttributes_default;
attr.minimumSamplingInterval = 0.000000;
attr.userAccessLevel = 1;
attr.accessLevel = 1;
attr.valueRank = -1;
attr.dataType = UA_NODEID_NUMERIC(ns[0], 15);
UA_ByteString *variablenode_ns_0_i_7617_variant_DataContents =  UA_ByteString_new();
UA_Byte stringArr[12777] = {60, 111, 112, 99, /* ... */ , 114, 121, 62};
variablenode_ns_0_i_7617_variant_DataContents->length = 12777;
variablenode_ns_0_i_7617_variant_DataContents->data = stringArr;
UA_Variant_setScalar(&attr.value, variablenode_ns_0_i_7617_variant_DataContents, &UA_TYPES[UA_TYPES_BYTESTRING]);
attr.displayName = UA_LOCALIZEDTEXT("", "Opc.Ua");
attr.description = UA_LOCALIZEDTEXT("", "");
attr.writeMask = 0;
attr.userWriteMask = 0;
retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE,
UA_NODEID_NUMERIC(ns[0], 7617),
UA_NODEID_NUMERIC(ns[0], 93),
UA_NODEID_NUMERIC(ns[0], 47),
UA_QUALIFIEDNAME(ns[0], "Opc.Ua"),
UA_NODEID_NUMERIC(ns[0], 72),
(const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],NULL, NULL);
UA_ByteString_delete(variablenode_ns_0_i_7617_variant_DataContents);
return retVal;
}

As you can see stringArr contains the binary data for Opc.Ua which is stored on the stack. UA_Server_addNode_begin will add the node and copy the variable attributes, i.e. it will alloc a new heap variable and copy over the 12kB. On embedded platforms we then have the same huge amount of data two times (stack+heap).

Do you have a good idea, how we can avoid the allocation and just take a reference to the data which is already there maybe in a global variable?
I thought about adding an additional UA_VariantStorageType like UA_VARIANT_DATA_NOCOPY where UA_Variant_copy takes the pointer instead of copying it. But to do that we need to change the UA_Variant_copy method, which is generated by generate_datatypes.py. Is there a better way to handle this?

@jpfr
Copy link
Member

jpfr commented Jul 12, 2018

I thought about adding an additional UA_VariantStorageType like UA_VARIANT_DATA_NOCOPY where UA_Variant_copy takes the pointer instead of copying it.

Is the additional complexity worth it?

If we need absolute minimum resource consumption, we could implement the binary nodeset file format. We could have a special nodestore that uses zero initialization and generates node only when they are used.

http://documentation.unified-automation.com/uasdkhp/1.1.1/html/md_opcua_binary_fileformat.html

Rumor has the binary nodeset format will play an important role in the standard.

@Pro
Copy link
Member

Pro commented Jul 16, 2018

Ok, then I will try to get CI green again with the current status, and skip the issue of double memory usage for the (huge) nodes.

@Pro Pro self-assigned this Jul 16, 2018
fixes open62541#1254

The huge strings are changed as arrays of ASCII values of each element inside for ByteArrays. And also some implementations are done due to task.
@Pro Pro force-pushed the feature/nodeset_bytearray branch 2 times, most recently from 16d8225 to 5f3b475 Compare July 16, 2018 08:28
@Pro Pro force-pushed the feature/nodeset_bytearray branch from ab9e06e to 30c2c5b Compare July 24, 2018 07:31
@Pro Pro force-pushed the feature/nodeset_bytearray branch from d9e4c13 to 6bd48c8 Compare July 31, 2018 12:14
@Pro Pro merged commit 8158f37 into open62541:master Jul 31, 2018
@lock
Copy link

lock bot commented Jul 31, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Jul 31, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Use statically initialized char array instead of huge string for nodeset generation
4 participants