class P4Distinct(P4Operator): def __init__(self, qid, operator_id, meta_init_name, drop_action, nop_action, keys, p4_raw_fields): super(P4Distinct, self).__init__('Distinct', qid, operator_id, keys, p4_raw_fields) self.threshold = 0 self.comp_func = '<=' # bitwise and self.update_func = '&' # bitwise and # create METADATA to store index and value fields = [('value', REGISTER_WIDTH), ('index', REGISTER_NUM_INDEX_BITS)] self.metadata = MetaData(self.operator_name, fields) # create REGISTER to keep track of counts self.register = Register(self.operator_name, REGISTER_WIDTH, REGISTER_INSTANCE_COUNT) # Add map init hash_init_fields = list() for fld in self.keys: if fld == 'qid': hash_init_fields.append( P4Field(layer=None, target_name="qid", sonata_name="qid", size=QID_SIZE)) elif fld == 'count': hash_init_fields.append( P4Field(layer=None, target_name="count", sonata_name="count", size=COUNT_SIZE)) else: hash_init_fields.append( self.p4_raw_fields.get_target_field(fld)) # create HASH for access to register hash_fields = list() for field in hash_init_fields: if '/' in field.sonata_name: self.logger.error('found a / in the key') raise NotImplementedError else: hash_fields.append( '%s.%s' % (meta_init_name, field.target_name.replace(".", "_"))) self.hash = HashFields(self.operator_name, hash_fields, 'crc16', REGISTER_NUM_INDEX_BITS) # name of metadata field where the index of the count within the register is stored self.index_field_name = '%s.index' % self.metadata.get_name() # name of metadata field where the count is kept temporarily self.value_field_name = '%s.value' % self.metadata.get_name() # create ACTION and TABLE to compute hash and get value primitives1 = list() primitives1.append( ModifyFieldWithHashBasedOffset(self.index_field_name, 0, self.hash.get_name(), REGISTER_INSTANCE_COUNT)) primitives1.append( RegisterRead(self.value_field_name, self.register.get_name(), self.index_field_name)) self.action1 = Action('do_init_%s' % self.operator_name, primitives1) # create ACTION and TABLE to bit_or value & write back primitives2 = list() primitives2.append( BitOr(self.value_field_name, self.value_field_name, 1)) primitives2.append( RegisterWrite(self.register.get_name(), self.index_field_name, self.value_field_name)) self.action2 = Action('do_update_%s' % self.operator_name, primitives2) table_name = 'init_%s' % self.operator_name self.init_table = Table(table_name, self.action1.get_name(), [], None, 1) table_name = 'update_%s' % self.operator_name self.update_table = Table(table_name, self.action2.get_name(), [], None, 1) # create two TABLEs that implement reduce operation: if count <= THRESHOLD, update count and drop, else let it # pass through table_name = 'pass_%s' % self.operator_name self.pass_table = Table(table_name, nop_action, [], None, 1) table_name = 'drop_%s' % self.operator_name self.drop_table = Table(table_name, drop_action, [], None, 1) def __repr__(self): return '.Distinct(keys=' + ', '.join([x for x in self.keys]) + ')' def get_code(self): out = '' out += '// %s %i of query %i\n' % (self.name, self.operator_id, self.query_id) out += self.metadata.get_code() out += self.hash.get_code() out += self.register.get_code() out += self.action1.get_code() out += self.action2.get_code() out += self.update_table.get_code() out += self.init_table.get_code() out += self.pass_table.get_code() out += self.drop_table.get_code() out += '\n' return out def get_commands(self): commands = list() commands.append(self.init_table.get_default_command()) commands.append(self.update_table.get_default_command()) commands.append(self.pass_table.get_default_command()) commands.append(self.drop_table.get_default_command()) return commands def get_control_flow(self, indent_level): indent = '\t' * indent_level out = '' out += '%sapply(%s);\n' % (indent, self.init_table.get_name()) out += '%sif (%s %s %i) {\n' % (indent, self.value_field_name, self.comp_func, self.threshold) out += '%s\tapply(%s);\n' % (indent, self.pass_table.get_name()) out += '%s\tapply(%s);\n' % (indent, self.update_table.get_name()) out += '%s}\n' % (indent, ) out += '%selse {\n' % (indent, ) out += '%s\tapply(%s);\n' % (indent, self.drop_table.get_name()) out += '%s}\n' % (indent, ) return out def get_init_keys(self): return self.keys
class P4Reduce(P4Operator): def __init__(self, qid, operator_id, meta_init_name, drop_action, keys, values, threshold, read_register, p4_raw_fields): super(P4Reduce, self).__init__('Reduce', qid, operator_id, keys, p4_raw_fields) if threshold == '-1': self.threshold = int(THRESHOLD) else: self.threshold = int(threshold) self.read_register = read_register if self.read_register: self.out_headers += ['index'] else: self.out_headers += ['count'] # create METADATA to store index and value fields = [('value', REGISTER_WIDTH), ('index', REGISTER_NUM_INDEX_BITS)] self.metadata = MetaData(self.operator_name, fields) # create REGISTER to keep track of counts self.register = Register(self.operator_name, REGISTER_WIDTH, REGISTER_INSTANCE_COUNT) self.values = values # Add map init hash_init_fields = list() for fld in self.keys: if fld == 'qid': hash_init_fields.append( P4Field(layer=None, target_name="qid", sonata_name="qid", size=QID_SIZE)) elif fld == 'count': hash_init_fields.append( P4Field(layer=None, target_name="count", sonata_name="count", size=COUNT_SIZE)) elif fld == 'index': hash_init_fields.append( P4Field(layer=None, target_name="index", sonata_name="index", size=INDEX_SIZE)) else: hash_init_fields.append( self.p4_raw_fields.get_target_field(fld)) # create HASH for access to register hash_fields = list() for field in hash_init_fields: if '/' in field.sonata_name: self.logger.error('found a / in the key') raise NotImplementedError else: hash_fields.append( '%s.%s' % (meta_init_name, field.target_name.replace(".", "_"))) self.hash = HashFields(self.operator_name, hash_fields, 'crc16', REGISTER_NUM_INDEX_BITS) # name of metadata field where the index of the count within the register is stored self.index_field_name = '%s.index' % self.metadata.get_name() # name of metadata field where the count is kept temporarily self.value_field_name = '%s.value' % self.metadata.get_name() # create ACTION and TABLE to compute hash and get value primitives = list() primitives.append( ModifyFieldWithHashBasedOffset(self.index_field_name, 0, self.hash.get_name(), REGISTER_INSTANCE_COUNT)) primitives.append( RegisterRead(self.value_field_name, self.register.get_name(), self.index_field_name)) if self.values[0] == 'count': if self.threshold <= 1: self.threshold = '1' primitives.append( ModifyField(self.value_field_name, '%s + %i' % (self.value_field_name, 1))) else: target_fld = self.p4_raw_fields.get_target_field(self.values[0]) if self.threshold <= 1: self.threshold = '%s.%s' % ( meta_init_name, target_fld.target_name.replace(".", "_")) primitives.append( ModifyField( self.value_field_name, '%s + %s' % (self.value_field_name, '%s.%s' % (meta_init_name, target_fld.target_name.replace(".", "_"))))) primitives.append( RegisterWrite(self.register.get_name(), self.index_field_name, self.value_field_name)) self.init_action = Action('do_init_%s' % self.operator_name, primitives) table_name = 'init_%s' % self.operator_name self.init_table = Table(table_name, self.init_action.get_name(), [], None, 1) # create three TABLEs that implement reduce operation # if count <= THRESHOLD, update count and drop, table_name = 'drop_%s' % self.operator_name self.drop_table = Table(table_name, drop_action, [], None, 1) # if count == THRESHOLD, pass through with current count field_to_modified = None if not self.read_register: field_to_modified = ModifyField('%s.count' % meta_init_name, self.value_field_name) else: field_to_modified = ModifyField('%s.index' % meta_init_name, self.index_field_name) self.set_count_action = Action('set_count_%s' % self.operator_name, field_to_modified) table_name = 'first_pass_%s' % self.operator_name self.first_pass_table = Table(table_name, self.set_count_action.get_name(), [], None, 1) if not self.read_register: # if count > THRESHOLD, let it pass through with count set to 1 self.reset_count_action = Action( 'reset_count_%s' % self.operator_name, ModifyField('%s.count' % meta_init_name, 1)) table_name = 'pass_%s' % self.operator_name self.pass_table = Table(table_name, self.reset_count_action.get_name(), [], None, 1) def __repr__(self): return '.Reduce(keys=' + ','.join( [x for x in self.keys]) + ', threshold=' + str(self.threshold) + ')' def get_code(self): out = '' out += '// %s %i of query %i\n' % (self.name, self.operator_id, self.query_id) out += self.metadata.get_code() out += self.hash.get_code() out += self.register.get_code() out += self.init_action.get_code() out += self.set_count_action.get_code() if not self.read_register: out += self.reset_count_action.get_code() out += self.pass_table.get_code() out += self.init_table.get_code() out += self.first_pass_table.get_code() out += self.drop_table.get_code() out += '\n' return out def get_commands(self): commands = list() commands.append(self.init_table.get_default_command()) commands.append(self.first_pass_table.get_default_command()) if not self.read_register: commands.append(self.pass_table.get_default_command()) commands.append(self.drop_table.get_default_command()) return commands def get_control_flow(self, indent_level): indent = '\t' * indent_level out = '' out += '%sapply(%s);\n' % (indent, self.init_table.get_name()) out += '%sif (%s == %s) {\n' % (indent, self.value_field_name, self.threshold) out += '%s\tapply(%s);\n' % (indent, self.first_pass_table.get_name()) out += '%s}\n' % (indent, ) if not self.read_register: out += '%selse if (%s > %s) {\n' % (indent, self.value_field_name, self.threshold) out += '%s\tapply(%s);\n' % (indent, self.pass_table.get_name()) out += '%s}\n' % (indent, ) out += '%selse {\n' % (indent, ) out += '%s\tapply(%s);\n' % (indent, self.drop_table.get_name()) out += '%s}\n' % (indent, ) return out def get_init_keys(self): return self.keys + self.values