viff

changeset 1281:bced13257ba4

Added support for nullary operation.
author Janus Dam Nielsen <janus.nielsen@alexandra.dk>
date Fri, 16 Oct 2009 15:54:29 +0200
parents 79c351c812f3
children 7265daadaa63
files apps/benchmark.py apps/benchmark_classes.py
diffstat 2 files changed, 101 insertions(+), 41 deletions(-) [+]
line diff
     1.1 --- a/apps/benchmark.py	Fri Oct 16 13:59:19 2009 +0200
     1.2 +++ b/apps/benchmark.py	Fri Oct 16 15:54:29 2009 +0200
     1.3 @@ -76,7 +76,7 @@
     1.4  from viff.util import find_prime
     1.5  
     1.6  from benchmark_classes import SelfcontainedBenchmarkStrategy, \
     1.7 -    NeededDataBenchmarkStrategy, ParallelBenchmark, SequentialBenchmark
     1.8 +    NeededDataBenchmarkStrategy, ParallelBenchmark, SequentialBenchmark, BinaryOperation, NullaryOperation
     1.9  
    1.10  # Hack in order to avoid Maximum recursion depth exceeded
    1.11  # exception;
    1.12 @@ -85,10 +85,10 @@
    1.13  
    1.14  last_timestamp = time.time()
    1.15  
    1.16 -operations = {"mul": (operator.mul,[]),
    1.17 -              "compToft05": (operator.ge, [ComparisonToft05Mixin]),
    1.18 -              "compToft07": (operator.ge, [ComparisonToft07Mixin]),
    1.19 -              "eq": (operator.eq, [ProbabilisticEqualityMixin])}
    1.20 +operations = {"mul": (operator.mul, [], BinaryOperation),
    1.21 +              "compToft05": (operator.ge, [ComparisonToft05Mixin], BinaryOperation),
    1.22 +              "compToft07": (operator.ge, [ComparisonToft07Mixin], BinaryOperation),
    1.23 +              "eq": (operator.eq, [ProbabilisticEqualityMixin], BinaryOperation)}
    1.24  
    1.25  runtimes = {"PassiveRuntime": PassiveRuntime,
    1.26              "PaillierRuntime": PaillierRuntime, 
    1.27 @@ -180,6 +180,7 @@
    1.28  # Identify the operation and it mixin dependencies.
    1.29  operation = operations[options.operation][0]
    1.30  actual_mixins += operations[options.operation][1]
    1.31 +operation_arity = operations[options.operation][2]
    1.32  
    1.33  print "Using the base runtime: %s." % base_runtime_class
    1.34  if actual_mixins:
    1.35 @@ -202,10 +203,12 @@
    1.36      needed_data = eval(needed_data)
    1.37  
    1.38  if options.needed_data != "" and options.pc != "":
    1.39 -    bases = (benchmark,) + (NeededDataBenchmarkStrategy,) + (object,)
    1.40 +    bases = (benchmark,) + (NeededDataBenchmarkStrategy, operation_arity, ) + (object,)
    1.41      options.pc = eval(options.pc)
    1.42  else:
    1.43 -    bases = (benchmark,) + (SelfcontainedBenchmarkStrategy,) + (object,)
    1.44 +    bases = (benchmark,) + (SelfcontainedBenchmarkStrategy, operation_arity, ) + (object,)
    1.45 +
    1.46 +print "Using the Benchmark bases: ", bases
    1.47  benchmark = type("ExtendedBenchmark", bases, {})
    1.48  
    1.49  pre_runtime = create_runtime(id, players, options.threshold,
     2.1 --- a/apps/benchmark_classes.py	Fri Oct 16 13:59:19 2009 +0200
     2.2 +++ b/apps/benchmark_classes.py	Fri Oct 16 15:54:29 2009 +0200
     2.3 @@ -42,10 +42,12 @@
     2.4      return x
     2.5  
     2.6  
     2.7 -# Defining the protocol as a class makes it easier to write the
     2.8 -# callbacks in the order they are called. This class is a base class
     2.9 -# that executes the protocol by calling the run_test method.
    2.10 -class Benchmark:
    2.11 +class Benchmark(object):
    2.12 +    """Abstract base class for all Benchmarks.
    2.13 +    
    2.14 +    For concrete classes see the `ParallelBenchmark` and `SequentialBenchmark` classes.
    2.15 +    A concrete class must be mixed with a `BenchmarkStrategy` and an `Operator`.
    2.16 +    """ 
    2.17  
    2.18      def __init__(self, rt, operation, field, count):
    2.19          self.rt = rt
    2.20 @@ -67,30 +69,13 @@
    2.21              return None
    2.22  
    2.23      def doTest(self, d, termination_function):
    2.24 -        self.rt.schedule_callback(d, self.begin)
    2.25 +        self.rt.schedule_callback(d, self.generate_operation_arguments)
    2.26          self.rt.schedule_callback(d, self.sync_test)
    2.27          self.rt.schedule_callback(d, self.run_test)
    2.28          self.rt.schedule_callback(d, self.sync_test)
    2.29          self.rt.schedule_callback(d, self.finished, termination_function)
    2.30          return d
    2.31  
    2.32 -    def begin(self, _):
    2.33 -        print "begin", self.rt.program_counter
    2.34 -        print "Runtime ready, generating shares"
    2.35 -        self.a_shares = []
    2.36 -        self.b_shares = []
    2.37 -        for i in range(self.count):
    2.38 -            inputter = (i % len(self.rt.players)) + 1
    2.39 -            if inputter == self.rt.id:
    2.40 -                a = rand.randint(0, self.field.modulus)
    2.41 -                b = rand.randint(0, self.field.modulus)
    2.42 -            else:
    2.43 -                a, b = None, None
    2.44 -            self.a_shares.append(self.rt.input([inputter], self.field, a))
    2.45 -            self.b_shares.append(self.rt.input([inputter], self.field, b))
    2.46 -        shares_ready = gather_shares(self.a_shares + self.b_shares)
    2.47 -        return shares_ready
    2.48 -
    2.49      def sync_test(self, x):
    2.50          print "Synchronizing test start."
    2.51          sys.stdout.flush()
    2.52 @@ -113,9 +98,9 @@
    2.53          return termination_function(needed_data)
    2.54  
    2.55  
    2.56 -# This class implements a benchmark where run_test executes all
    2.57 -# operations in parallel.
    2.58  class ParallelBenchmark(Benchmark):
    2.59 +    """This class implements a benchmark where run_test executes all
    2.60 +    operations in parallel."""
    2.61  
    2.62      def run_test(self, shares):
    2.63          print "rt", self.rt.program_counter, self.pc
    2.64 @@ -125,10 +110,8 @@
    2.65              self.pc = list(self.rt.program_counter)
    2.66          c_shares = []
    2.67          record_start("parallel test")
    2.68 -        while self.a_shares and self.b_shares:
    2.69 -            a = self.a_shares.pop()
    2.70 -            b = self.b_shares.pop()
    2.71 -            c_shares.append(self.operation(a, b))
    2.72 +        while not self.is_operation_done():
    2.73 +            c_shares.append(self.do_operation())
    2.74              print "."
    2.75  
    2.76          done = gather_shares(c_shares)
    2.77 @@ -141,31 +124,103 @@
    2.78          return done
    2.79  
    2.80  
    2.81 -# A benchmark where the operations are executed one after each other.
    2.82  class SequentialBenchmark(Benchmark):
    2.83 +    """A benchmark where the operations are executed one after each other."""
    2.84  
    2.85      def run_test(self, _, termination_function, d):
    2.86          record_start("sequential test")
    2.87          self.single_operation(None, termination_function)
    2.88  
    2.89      def single_operation(self, _, termination_function):
    2.90 -        if self.a_shares and self.b_shares:
    2.91 -            a = self.a_shares.pop()
    2.92 -            b = self.b_shares.pop()
    2.93 -            c = self.operation(a, b)
    2.94 +        if not self.is_operation_done():
    2.95 +            c = self.do_operation()
    2.96              self.rt.schedule_callback(c, self.single_operation, termination_function)
    2.97          else:
    2.98              record_stop(None, "sequential test", self.count)
    2.99              self.finished(None, termination_function)
   2.100  
   2.101  
   2.102 -class BenchmarkStrategy:
   2.103 +class Operation(object):
   2.104 +    """An abstract mixin which encapsulate the behaviour of an operation.
   2.105 +
   2.106 +    An operation can be nullary, unary, binary, etc.
   2.107 +    """
   2.108 +    
   2.109 +    def generate_operation_arguments(self, _):
   2.110 +        """Generate the input need for performing the operation.
   2.111 +
   2.112 +        Returns: None.
   2.113 +        """
   2.114 +        raise NotImplemented("Override this abstract method in subclasses")
   2.115 +
   2.116 +    def is_operation_done(self):
   2.117 +        """Returns true if there are no more operations to perform. 
   2.118 +        Used in sequential tests.
   2.119 +
   2.120 +        Returns: Boolean.
   2.121 +        """
   2.122 +        raise NotImplemented("Override this abstract method in subclasses")
   2.123 +
   2.124 +    def do_operation(self):
   2.125 +        """Perform the operation.
   2.126 +
   2.127 +        Returns: A share containing the result of the operation.
   2.128 +        """
   2.129 +        raise NotImplemented("Override this abstract method in subclasses")
   2.130 +
   2.131 +class BinaryOperation(Operation):
   2.132 +    """A binary operation."""
   2.133 +
   2.134 +    def generate_operation_arguments(self, _):
   2.135 +        print "Generate operation arguments", self.rt.program_counter
   2.136 +        print "Runtime ready, generating shares"
   2.137 +        self.a_shares = []
   2.138 +        self.b_shares = []
   2.139 +        for i in range(self.count):
   2.140 +            inputter = (i % len(self.rt.players)) + 1
   2.141 +            if inputter == self.rt.id:
   2.142 +                a = rand.randint(0, self.field.modulus)
   2.143 +                b = rand.randint(0, self.field.modulus)
   2.144 +            else:
   2.145 +                a, b = None, None
   2.146 +            self.a_shares.append(self.rt.input([inputter], self.field, a))
   2.147 +            self.b_shares.append(self.rt.input([inputter], self.field, b))
   2.148 +        shares_ready = gather_shares(self.a_shares + self.b_shares)
   2.149 +        return shares_ready
   2.150 +
   2.151 +    def is_operation_done(self):
   2.152 +        return not (self.a_shares and self.b_shares)
   2.153 +
   2.154 +    def do_operation(self):
   2.155 +        a = self.a_shares.pop()
   2.156 +        b = self.b_shares.pop()
   2.157 +        return self.operation(a, b)
   2.158 +
   2.159 +
   2.160 +class NullaryOperation(Operation):
   2.161 +    """A nullary operation."""
   2.162 +    
   2.163 +    def generate_operation_arguments(self, _):
   2.164 +        self.nullary_tests = self.count
   2.165 +        return None
   2.166 +
   2.167 +    def is_operation_done(self):
   2.168 +        return self.nullary_tests == 0
   2.169 +
   2.170 +    def do_operation(self):
   2.171 +        self.nullary_tests -= 1
   2.172 +        return self.operation(self.field)
   2.173 +
   2.174 +
   2.175 +class BenchmarkStrategy(object):
   2.176 +    """A benchmark strategy defines how the benchmark is done."""
   2.177  
   2.178      def benchmark(self, *args):
   2.179          raise NotImplemented("Override this abstract method in subclasses")
   2.180  
   2.181  
   2.182  class SelfcontainedBenchmarkStrategy(BenchmarkStrategy):
   2.183 +    """In a self contained benchmark strategy, all the needed data is generated on the fly."""
   2.184  
   2.185      def benchmark(self, *args):
   2.186          sys.stdout.flush()
   2.187 @@ -176,6 +231,8 @@
   2.188  
   2.189  
   2.190  class NeededDataBenchmarkStrategy(BenchmarkStrategy):
   2.191 +    """In a needed data benchmark strategy, all the needed data has to have been generated 
   2.192 +    before the test is run."""
   2.193  
   2.194      def benchmark(self, needed_data, pc, *args):
   2.195          self.pc = pc