Jump to content
Sign in to follow this  
thiennguyen

uvm_factory performance improvement for object creation

Recommended Posts

Hi,

As I read the code in uvm-1.2 package, the type override lookup (by uvm_default_factory::find_override_by_type()) seems to be done every time a new UVM object is created.

This really hurts performance because object creation happens most frequently in a UVM environment. 

My idea is: The implementation needs to be changed so that the type override info is put inside each object proxy, instead of a factory's queue (m_type_overrides). When set_type_override_by_type() is called, it sets the override info for every affected object proxy. So when a new object is created, we just use the override info already available inside its proxy to do creation and no more type override lookup needed. The call of set_type_override_by_type() should happen much much less frequently than object creation, I believe.

The override info for each object proxy should have 2 element as below:

  • override: the type that overrides this type
  • override_to[$]: a list containing all types that are overriden by this type. 

so that when set_type_override_by_type() is called, we can traverse all the affected types regardless of the calling order of multiple calls of set_type_override_by_type(). For example:

  1. set_type_override_by_type(): B overrides A
  2. set_type_override_by_type(): C overrides B

then finally C must override A regardless of 1. or 2. being executed first.

 

Best regards,

Thien

Share this post


Link to post
Share on other sites

Thien-

 

Agreed, there's a lot of performance lost in the factory, although we usually see that on the name-based side of town (as opposed to the type-based).  Do you have a proposal/code edit that we could inspect and test?  This sounds strikingly similar to an enhancement that we've been looking at on the uvm_callbacks side of town as well.

 

-Justin

PS- UVM 1.2 is technically deprecated at this point.  Any changes would actually be ported forward to the next 1800.2 release.

Share this post


Link to post
Share on other sites

Hi Justin,

Thanks for your reply. I don't have the modified code yet. But I'd like to make my proposal clearer as below.

The problems with the factory are:

  • An associative array lookup step needs to be done to retrieve instance override info of requested_type:
uvm_factory_queue_class qc;
qc = m_inst_override_queues.exists(requested_type) ?
     m_inst_override_queues[requested_type] : null;
  •  Iterate through m_override_info queue to check for override loop.
  •  Iterate through m_type_overrides queue to check for type override.
  •  Recursive calls of find_override_by_type() for the case of "B overrides A, C overrides B, etc":
(in function find_override_by_type)
// inst override; return first match; takes precedence over type overrides
if (full_inst_path != "" && qc != null)
	for (int index = 0; index < qc.queue.size(); ++index) begin
    ...
    return find_override_by_type(qc.queue[index].ovrd_type,full_inst_path);
                                                
// type override - exact match  foreach (m_type_overrides[index]) begin
	if (m_type_overrides[index].orig_type == requested_type ||
    ...
    return find_override_by_type(m_type_overrides[index].ovrd_type,full_inst_path);

All above things happen for every creation of a UVM object which uses the factory.

So I propose to put type override and instance override information in the type proxy itself (i.e, uvm_object_wrapper), so that we can get the override info directly from requested_type and no more lookup needed. It's responsibility of set_type_override_by_type() to do all the awful stuffs, because this method is called only once for each override setting.

The override info inside uvm_object_wrapper should be:

  •  ovrd_insts [$]: a queue of {<instance-path>, <override-type>} pairs for instance override.
  •  ovrd_type: for type override. Set to this type when the type specialization is created or registered to the factory.
  •  affected_types [$]: a queue of types that are overriden by this type. Used like a linked-list pointer for set_type_override_by_type() to traverse in the mentioned case "B overrides A, C overrides B, etc".

Pseudo code of set_type_override_by_type (original_type, override_type) is as below:

function set_type_override_by_type (original_type, override_type)
{
	__set_type_override_by_type (original_type, override_type);
	
	// add original_type into 'affected_types' of override_type
	override_type.affected_types.push_back(original_type);
}

function __set_type_override_by_type (original_type, override_type)
{
	original_type.ovrd_type = override_type.ovrd_type; // note: ovrd_type defaults to the container type
                                                  	   // until it is overriden.

	// set override for each affected_type of original_type.
	// may be recursive for each affected_type child of that affected_type, ...
	foreach original_type.affected_types[i] {
		if original_type.affected_types[i] == override_type.ovrd_type
			error("Override loop detected");
		else
			__set_type_override_by_type(original_type.affected_types[i], override_type.ovrd_type)
	}
}

 

Pseudo code of create_object_by_type (): no more lookup or recursive calls to find the override type.

function uvm_object create_object_by_type (uvm_object_wrapper requested_type,
                                              string parent_inst_path="",
                                              string name);

	// ... code to make full_inst_path from parent_inst_path, etc as in current uvm-1.2 code

	// instance override. takes precedence over type override.
	foreach requested_type.ovrd_insts[i] begin
		if(uvm_is_match(requested_type.ovrd_insts[i].full_inst_path, full_inst_path))
			return requested_type.ovrd_insts[i].ovrd_type.create_object(name);
	end	

	// type override
	return requested_type.ovrd_type.create_object(name);

endfunction

The modification for this point is rather simple, but it may impact other functionalities such as factory.print() and name-based override. So please give me some time to do the modification thoroughly. I'll get back when it's done.

Best regards,

Thien

Share this post


Link to post
Share on other sites

Hi Justin,

As promised, I'd like to release the modified uvm_factory.svh file in attachment for your reference. Please search keyword '//CHANGE' for all the changes made.

I tested it with some simple test cases for type and instance overrides and it works as the old implementation. May need to be verified more.

New data structure of uvm_object_wrapper is as below:

  •   uvm_object_wrapper   ovrd_type  : ultimate overriding type. Initialized to this type. Used to create the object of this type (after no instance override matches).
  •   uvm_factory_queue_class   inst_override  : queue of instance overrides (each override consists of a pair of ovrd_type and full_inst_path). The ovrd_type at which full_inst_path matches with get_full_name() of the object to be created will be used to create that object.
  •   uvm_object_wrapper   direct_ovrd_type  : direct override type, i.e, the overriding type that was set by set_type_override_by_type() or set_type_override_by_name(). Used with affected_types (below) for traversing across type override hierachy (e.g, C overrides B, B overrides A).
  •   bit affected_types [uvm_object_wrapper]  : types that was overriden by this type. Used with direct_ovrd_type (above) for traversing across type override hierachy (e.g, C overrides B, B overrides A).

As explained in previous comment, set_type_override_by_type/name() and set_inst_override_by_type/name() will set the ovrd_type of the requested_type argument. When create_object_by_type/name() is called, the ovrd_type in the object wrapper will be used to create the object handily and least lookup operations needed, as opposed to the old implementation.

 

Since set_type_override_by_name() and set_inst_override_by_name() allow arbitrary (including wildcards) type names (a.k.a, lookup names) which may not have been registered with the factory, a new class is introduced and the 2 new data members below are added into the uvm_default_factory for this purpose.

class uvm_factory_lookup_type extends uvm_object_wrapper;
	function new ();
		inst_override = new;
	endfunction
	virtual function string get_type_name();    
		return "";
	endfunction
endclass
class uvm_default_factory extends uvm_factory;
	...
	protected uvm_factory_lookup_type   m_lookup_type_names[string];
	protected uvm_factory_lookup_type   m_lookup_type_wildcards[string];
	...
endclass

Because uvm_factory_lookup_type inherits all the aforementioned data members of uvm_object_wrapper, type and instance overrides which set to a lookup type name is treated just like a normal override setting to a registered type. This makes the implementation cleaner as opposed to the old one where lookup type names and registered type names are mixed together into m_type_names associative array.

 

Finally, there're some notes with the attached uvm_factory.svh file.

  •  debug_create_by_type/name() methods are not implemented yet.
  •  In old factory implementation, the "override loop" error is checked at run_phase, when the create_object. In new implementation, it is checked when set_type_override_by_type/name() is called, so it may happen in build_phase() and the simulation would be stopped due to "build error" reason.

Best Regards,

Thien

 

uvm_factory.svh

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

×
×
  • Create New...