1 // Copyright (c) Microsoft Corporation. All rights reserved. 2 // Licensed under the MIT license. 3 4 using System; 5 using System.Collections.Generic; 6 using System.Diagnostics; 7 using System.IO; 8 using System.Linq; 9 using Microsoft.Research.SEAL; 10 11 namespace SEALNetExamples 12 { 13 partial class Examples 14 { BFVPerformanceTest(SEALContext context)15 private static void BFVPerformanceTest(SEALContext context) 16 { 17 Stopwatch timer; 18 Utilities.PrintParameters(context); 19 Console.WriteLine(); 20 21 bool hasZLIB = Serialization.IsSupportedComprMode(ComprModeType.ZLIB); 22 bool hasZSTD = Serialization.IsSupportedComprMode(ComprModeType.ZSTD); 23 24 using EncryptionParameters parms = context.FirstContextData.Parms; 25 using Modulus plainModulus = parms.PlainModulus; 26 ulong polyModulusDegree = parms.PolyModulusDegree; 27 28 Console.Write("Generating secret/public keys: "); 29 using KeyGenerator keygen = new KeyGenerator(context); 30 Console.WriteLine("Done"); 31 32 using SecretKey secretKey = keygen.SecretKey; 33 keygen.CreatePublicKey(out PublicKey publicKey); 34 35 Func<RelinKeys> GetRelinKeys = () => { 36 if (context.UsingKeyswitching) 37 { 38 /* 39 Generate relinearization keys. 40 */ 41 Console.Write("Generating relinearization keys: "); 42 timer = Stopwatch.StartNew(); 43 keygen.CreateRelinKeys(out RelinKeys relinKeys); 44 int micros = (int)(timer.Elapsed.TotalMilliseconds * 1000); 45 Console.WriteLine($"Done [{micros} microseconds]"); 46 return relinKeys; 47 } 48 else 49 { 50 return null; 51 } 52 }; 53 54 Func<GaloisKeys> GetGaloisKeys = () => { 55 if (context.UsingKeyswitching) 56 { 57 if (!context.KeyContextData.Qualifiers.UsingBatching) 58 { 59 Console.WriteLine("Given encryption parameters do not support batching."); 60 return null; 61 } 62 63 /* 64 Generate Galois keys. In larger examples the Galois keys can use a lot of 65 memory, which can be a problem in constrained systems. The user should 66 try some of the larger runs of the test and observe their effect on the 67 memory pool allocation size. The key generation can also take a long time, 68 as can be observed from the print-out. 69 */ 70 Console.Write($"Generating Galois keys: "); 71 timer = Stopwatch.StartNew(); 72 keygen.CreateGaloisKeys(out GaloisKeys galoisKeys); 73 int micros = (int)(timer.Elapsed.TotalMilliseconds * 1000); 74 Console.WriteLine($"Done [{micros} microseconds]"); 75 return galoisKeys; 76 } 77 else 78 { 79 return null; 80 } 81 }; 82 83 using RelinKeys relinKeys = GetRelinKeys(); 84 using GaloisKeys galKeys = GetGaloisKeys(); 85 86 using Encryptor encryptor = new Encryptor(context, publicKey); 87 using Decryptor decryptor = new Decryptor(context, secretKey); 88 using Evaluator evaluator = new Evaluator(context); 89 using BatchEncoder batchEncoder = new BatchEncoder(context); 90 91 /* 92 These will hold the total times used by each operation. 93 */ 94 Stopwatch timeBatchSum = new Stopwatch(); 95 Stopwatch timeUnbatchSum = new Stopwatch(); 96 Stopwatch timeEncryptSum = new Stopwatch(); 97 Stopwatch timeDecryptSum = new Stopwatch(); 98 Stopwatch timeAddSum = new Stopwatch(); 99 Stopwatch timeMultiplySum = new Stopwatch(); 100 Stopwatch timeMultiplyPlainSum = new Stopwatch(); 101 Stopwatch timeSquareSum = new Stopwatch(); 102 Stopwatch timeRelinearizeSum = new Stopwatch(); 103 Stopwatch timeRotateRowsOneStepSum = new Stopwatch(); 104 Stopwatch timeRotateRowsRandomSum = new Stopwatch(); 105 Stopwatch timeRotateColumnsSum = new Stopwatch(); 106 Stopwatch timeSerializeSum = new Stopwatch(); 107 Stopwatch timeSerializeZLIBSum = new Stopwatch(); 108 Stopwatch timeSerializeZSTDSum = new Stopwatch(); 109 110 /* 111 How many times to run the test? 112 */ 113 int count = 10; 114 115 /* 116 Populate a vector of values to batch. 117 */ 118 ulong slotCount = batchEncoder.SlotCount; 119 ulong[] podValues = new ulong[slotCount]; 120 Random rnd = new Random(); 121 for (ulong i = 0; i < batchEncoder.SlotCount; i++) 122 { 123 podValues[i] = plainModulus.Reduce((ulong)rnd.Next()); 124 } 125 126 Console.Write("Running tests "); 127 for (int i = 0; i < count; i++) 128 { 129 /* 130 [Batching] 131 There is nothing unusual here. We batch our random plaintext matrix 132 into the polynomial. Note how the plaintext we create is of the exactly 133 right size so unnecessary reallocations are avoided. 134 */ 135 using Plaintext plain = new Plaintext(parms.PolyModulusDegree, 0); 136 timeBatchSum.Start(); 137 batchEncoder.Encode(podValues, plain); 138 timeBatchSum.Stop(); 139 140 /* 141 [Unbatching] 142 We unbatch what we just batched. 143 */ 144 List<ulong> podList = new List<ulong>((int)slotCount); 145 timeUnbatchSum.Start(); 146 batchEncoder.Decode(plain, podList); 147 timeUnbatchSum.Stop(); 148 if (!podList.SequenceEqual(podValues)) 149 { 150 throw new InvalidOperationException("Batch/unbatch failed. Something is wrong."); 151 } 152 153 /* 154 [Encryption] 155 We make sure our ciphertext is already allocated and large enough 156 to hold the encryption with these encryption parameters. We encrypt 157 our random batched matrix here. 158 */ 159 using Ciphertext encrypted = new Ciphertext(context); 160 timeEncryptSum.Start(); 161 encryptor.Encrypt(plain, encrypted); 162 timeEncryptSum.Stop(); 163 164 /* 165 [Decryption] 166 We decrypt what we just encrypted. 167 */ 168 using Plaintext plain2 = new Plaintext(polyModulusDegree, 0); 169 timeDecryptSum.Start(); 170 decryptor.Decrypt(encrypted, plain2); 171 timeDecryptSum.Stop(); 172 if (!plain2.Equals(plain)) 173 { 174 throw new InvalidOperationException("Encrypt/decrypt failed. Something is wrong."); 175 } 176 177 /* 178 [Add] 179 We create two ciphertexts and perform a few additions with them. 180 */ 181 using Plaintext plain1 = new Plaintext(parms.PolyModulusDegree, 0); 182 for (ulong j = 0; j < batchEncoder.SlotCount; j++) 183 { 184 podValues[j] = j; 185 } 186 batchEncoder.Encode(podValues, plain1); 187 for (ulong j = 0; j < batchEncoder.SlotCount; j++) 188 { 189 podValues[j] = j + 1; 190 } 191 batchEncoder.Encode(podValues, plain2); 192 using Ciphertext encrypted1 = new Ciphertext(context); 193 encryptor.Encrypt(plain1, encrypted1); 194 using Ciphertext encrypted2 = new Ciphertext(context); 195 encryptor.Encrypt(plain2, encrypted2); 196 197 timeAddSum.Start(); 198 evaluator.AddInplace(encrypted1, encrypted1); 199 evaluator.AddInplace(encrypted2, encrypted2); 200 evaluator.AddInplace(encrypted1, encrypted2); 201 timeAddSum.Stop(); 202 203 /* 204 [Multiply] 205 We multiply two ciphertexts. Since the size of the result will be 3, 206 and will overwrite the first argument, we reserve first enough memory 207 to avoid reallocating during multiplication. 208 */ 209 encrypted1.Reserve(3); 210 timeMultiplySum.Start(); 211 evaluator.MultiplyInplace(encrypted1, encrypted2); 212 timeMultiplySum.Stop(); 213 214 /* 215 [Multiply Plain] 216 We multiply a ciphertext with a random plaintext. Recall that 217 MultiplyPlain does not change the size of the ciphertext so we use 218 encrypted2 here. 219 */ 220 timeMultiplyPlainSum.Start(); 221 evaluator.MultiplyPlainInplace(encrypted2, plain); 222 timeMultiplyPlainSum.Stop(); 223 224 /* 225 [Square] 226 We continue to use encrypted2. Now we square it; this should be 227 faster than generic homomorphic multiplication. 228 */ 229 timeSquareSum.Start(); 230 evaluator.SquareInplace(encrypted2); 231 timeSquareSum.Stop(); 232 233 if (context.UsingKeyswitching) 234 { 235 /* 236 [Relinearize] 237 Time to get back to encrypted1. We now relinearize it back 238 to size 2. Since the allocation is currently big enough to 239 contain a ciphertext of size 3, no costly reallocations are 240 needed in the process. 241 */ 242 timeRelinearizeSum.Start(); 243 evaluator.RelinearizeInplace(encrypted1, relinKeys); 244 timeRelinearizeSum.Stop(); 245 246 /* 247 [Rotate Rows One Step] 248 We rotate matrix rows by one step left and measure the time. 249 */ 250 timeRotateRowsOneStepSum.Start(); 251 evaluator.RotateRowsInplace(encrypted, 1, galKeys); 252 evaluator.RotateRowsInplace(encrypted, -1, galKeys); 253 timeRotateRowsOneStepSum.Stop(); 254 255 /* 256 [Rotate Rows Random] 257 We rotate matrix rows by a random number of steps. This is much more 258 expensive than rotating by just one step. 259 */ 260 int rowSize = (int)batchEncoder.SlotCount / 2; 261 // rowSize is always a power of 2. 262 int randomRotation = rnd.Next() & (rowSize - 1); 263 timeRotateRowsRandomSum.Start(); 264 evaluator.RotateRowsInplace(encrypted, randomRotation, galKeys); 265 timeRotateRowsRandomSum.Stop(); 266 267 /* 268 [Rotate Columns] 269 Nothing surprising here. 270 */ 271 timeRotateColumnsSum.Start(); 272 evaluator.RotateColumnsInplace(encrypted, galKeys); 273 timeRotateColumnsSum.Stop(); 274 } 275 276 /* 277 [Serialize Ciphertext] 278 */ 279 using MemoryStream stream = new MemoryStream(); 280 timeSerializeSum.Start(); 281 encrypted.Save(stream, ComprModeType.None); 282 timeSerializeSum.Stop(); 283 284 if (hasZLIB) 285 { 286 /* 287 [Serialize Ciphertext (ZLIB)] 288 */ 289 timeSerializeZLIBSum.Start(); 290 encrypted.Save(stream, ComprModeType.ZLIB); 291 timeSerializeZLIBSum.Stop(); 292 } 293 294 if (hasZSTD) 295 { 296 /* 297 [Serialize Ciphertext (Zstandard)] 298 */ 299 timeSerializeZSTDSum.Start(); 300 encrypted.Save(stream, ComprModeType.ZSTD); 301 timeSerializeZSTDSum.Stop(); 302 } 303 304 /* 305 Print a dot to indicate progress. 306 */ 307 Console.Write("."); 308 Console.Out.Flush(); 309 } 310 311 Console.WriteLine(" Done"); 312 Console.WriteLine(); 313 Console.Out.Flush(); 314 315 int avgBatch = (int)(timeBatchSum.Elapsed.TotalMilliseconds * 1000 / count); 316 int avgUnbatch = (int)(timeUnbatchSum.Elapsed.TotalMilliseconds * 1000 / count); 317 int avgEncrypt = (int)(timeEncryptSum.Elapsed.TotalMilliseconds * 1000 / count); 318 int avgDecrypt = (int)(timeDecryptSum.Elapsed.TotalMilliseconds * 1000 / count); 319 int avgAdd = (int)(timeAddSum.Elapsed.TotalMilliseconds * 1000 / (3 * count)); 320 int avgMultiply = (int)(timeMultiplySum.Elapsed.TotalMilliseconds * 1000 / count); 321 int avgMultiplyPlain = (int)(timeMultiplyPlainSum.Elapsed.TotalMilliseconds * 1000 / count); 322 int avgSquare = (int)(timeSquareSum.Elapsed.TotalMilliseconds * 1000 / count); 323 int avgRelinearize = (int)(timeRelinearizeSum.Elapsed.TotalMilliseconds * 1000 / count); 324 int avgRotateRowsOneStep = (int)(timeRotateRowsOneStepSum.Elapsed.TotalMilliseconds * 1000 / (2 * count)); 325 int avgRotateRowsRandom = (int)(timeRotateRowsRandomSum.Elapsed.TotalMilliseconds * 1000 / count); 326 int avgRotateColumns = (int)(timeRotateColumnsSum.Elapsed.TotalMilliseconds * 1000 / count); 327 int avgSerializeSum = (int)(timeSerializeSum.Elapsed.TotalMilliseconds * 1000 / count); 328 int avgSerializeZLIBSum = (int)(timeSerializeZLIBSum.Elapsed.TotalMilliseconds * 1000 / count); 329 int avgSerializeZSTDSum = (int)(timeSerializeZSTDSum.Elapsed.TotalMilliseconds * 1000 / count); 330 331 Console.WriteLine($"Average batch: {avgBatch} microseconds"); 332 Console.WriteLine($"Average unbatch: {avgUnbatch} microseconds"); 333 Console.WriteLine($"Average encrypt: {avgEncrypt} microseconds"); 334 Console.WriteLine($"Average decrypt: {avgDecrypt} microseconds"); 335 Console.WriteLine($"Average add: {avgAdd} microseconds"); 336 Console.WriteLine($"Average multiply: {avgMultiply} microseconds"); 337 Console.WriteLine($"Average multiply plain: {avgMultiplyPlain} microseconds"); 338 Console.WriteLine($"Average square: {avgSquare} microseconds"); 339 if (context.UsingKeyswitching) 340 { 341 Console.WriteLine($"Average relinearize: {avgRelinearize} microseconds"); 342 Console.WriteLine($"Average rotate rows one step: {avgRotateRowsOneStep} microseconds"); 343 Console.WriteLine($"Average rotate rows random: {avgRotateRowsRandom} microseconds"); 344 Console.WriteLine($"Average rotate columns: {avgRotateColumns} microseconds"); 345 } 346 Console.WriteLine($"Average serialize ciphertext: {avgSerializeSum} microseconds"); 347 if (hasZLIB) 348 { 349 Console.WriteLine( 350 $"Average compressed (ZLIB) serialize ciphertext: {avgSerializeZLIBSum} microseconds"); 351 } 352 if (hasZSTD) 353 { 354 Console.WriteLine( 355 $"Average compressed (Zstandard) serialize ciphertext: {avgSerializeZSTDSum} microseconds"); 356 } 357 358 Console.Out.Flush(); 359 } 360 CKKSPerformanceTest(SEALContext context)361 private static void CKKSPerformanceTest(SEALContext context) 362 { 363 Stopwatch timer; 364 Utilities.PrintParameters(context); 365 Console.WriteLine(); 366 367 bool hasZLIB = Serialization.IsSupportedComprMode(ComprModeType.ZLIB); 368 bool hasZSTD = Serialization.IsSupportedComprMode(ComprModeType.ZSTD); 369 370 using EncryptionParameters parms = context.FirstContextData.Parms; 371 ulong polyModulusDegree = parms.PolyModulusDegree; 372 373 Console.Write("Generating secret/public keys: "); 374 using KeyGenerator keygen = new KeyGenerator(context); 375 Console.WriteLine("Done"); 376 377 using SecretKey secretKey = keygen.SecretKey; 378 keygen.CreatePublicKey(out PublicKey publicKey); 379 380 Func<RelinKeys> GetRelinKeys = () => { 381 if (context.UsingKeyswitching) 382 { 383 /* 384 Generate relinearization keys. 385 */ 386 Console.Write("Generating relinearization keys: "); 387 timer = Stopwatch.StartNew(); 388 keygen.CreateRelinKeys(out RelinKeys relinKeys); 389 int micros = (int)(timer.Elapsed.TotalMilliseconds * 1000); 390 Console.WriteLine($"Done [{micros} microseconds]"); 391 return relinKeys; 392 } 393 else 394 { 395 return null; 396 } 397 }; 398 399 Func<GaloisKeys> GetGaloisKeys = () => { 400 if (context.UsingKeyswitching) 401 { 402 if (!context.KeyContextData.Qualifiers.UsingBatching) 403 { 404 Console.WriteLine("Given encryption parameters do not support batching."); 405 return null; 406 } 407 408 /* 409 Generate Galois keys. In larger examples the Galois keys can use a lot of 410 memory, which can be a problem in constrained systems. The user should 411 try some of the larger runs of the test and observe their effect on the 412 memory pool allocation size. The key generation can also take a long time, 413 as can be observed from the print-out. 414 */ 415 Console.Write($"Generating Galois keys: "); 416 timer = Stopwatch.StartNew(); 417 keygen.CreateGaloisKeys(out GaloisKeys galoisKeys); 418 int micros = (int)(timer.Elapsed.TotalMilliseconds * 1000); 419 Console.WriteLine($"Done [{micros} microseconds]"); 420 return galoisKeys; 421 } 422 else 423 { 424 return null; 425 } 426 }; 427 428 using RelinKeys relinKeys = GetRelinKeys(); 429 using GaloisKeys galKeys = GetGaloisKeys(); 430 431 using Encryptor encryptor = new Encryptor(context, publicKey); 432 using Decryptor decryptor = new Decryptor(context, secretKey); 433 using Evaluator evaluator = new Evaluator(context); 434 using CKKSEncoder ckksEncoder = new CKKSEncoder(context); 435 436 Stopwatch timeEncodeSum = new Stopwatch(); 437 Stopwatch timeDecodeSum = new Stopwatch(); 438 Stopwatch timeEncryptSum = new Stopwatch(); 439 Stopwatch timeDecryptSum = new Stopwatch(); 440 Stopwatch timeAddSum = new Stopwatch(); 441 Stopwatch timeMultiplySum = new Stopwatch(); 442 Stopwatch timeMultiplyPlainSum = new Stopwatch(); 443 Stopwatch timeSquareSum = new Stopwatch(); 444 Stopwatch timeRelinearizeSum = new Stopwatch(); 445 Stopwatch timeRescaleSum = new Stopwatch(); 446 Stopwatch timeRotateOneStepSum = new Stopwatch(); 447 Stopwatch timeRotateRandomSum = new Stopwatch(); 448 Stopwatch timeConjugateSum = new Stopwatch(); 449 Stopwatch timeSerializeSum = new Stopwatch(); 450 Stopwatch timeSerializeZLIBSum = new Stopwatch(); 451 Stopwatch timeSerializeZSTDSum = new Stopwatch(); 452 453 Random rnd = new Random(); 454 455 /* 456 How many times to run the test? 457 */ 458 int count = 10; 459 460 /* 461 Populate a vector of floating-point values to batch. 462 */ 463 ulong slotCount = ckksEncoder.SlotCount; 464 double[] podValues = new double[slotCount]; 465 for (ulong i = 0; i < slotCount; i++) 466 { 467 podValues[i] = 1.001 * i; 468 } 469 470 Console.Write("Running tests "); 471 for (int i = 0; i < count; i++) 472 { 473 /* 474 [Encoding] 475 For scale we use the square root of the last CoeffModulus prime 476 from parms. 477 */ 478 double scale = Math.Sqrt(parms.CoeffModulus.Last().Value); 479 using Plaintext plain = new Plaintext(parms.PolyModulusDegree * 480 (ulong)parms.CoeffModulus.Count(), 0); 481 timeEncodeSum.Start(); 482 ckksEncoder.Encode(podValues, scale, plain); 483 timeEncodeSum.Stop(); 484 485 /* 486 [Decoding] 487 */ 488 List<double> podList = new List<double>((int)slotCount); 489 timeDecodeSum.Start(); 490 ckksEncoder.Decode(plain, podList); 491 timeDecodeSum.Stop(); 492 493 /* 494 [Encryption] 495 */ 496 using Ciphertext encrypted = new Ciphertext(context); 497 timeEncryptSum.Start(); 498 encryptor.Encrypt(plain, encrypted); 499 timeEncryptSum.Stop(); 500 501 /* 502 [Decryption] 503 */ 504 using Plaintext plain2 = new Plaintext(polyModulusDegree, 0); 505 timeDecryptSum.Start(); 506 decryptor.Decrypt(encrypted, plain2); 507 timeDecryptSum.Stop(); 508 509 /* 510 [Add] 511 */ 512 using Ciphertext encrypted1 = new Ciphertext(context); 513 ckksEncoder.Encode(i + 1, plain); 514 encryptor.Encrypt(plain, encrypted1); 515 using Ciphertext encrypted2 = new Ciphertext(context); 516 ckksEncoder.Encode(i + 1, plain2); 517 encryptor.Encrypt(plain2, encrypted2); 518 timeAddSum.Start(); 519 evaluator.AddInplace(encrypted1, encrypted2); 520 evaluator.AddInplace(encrypted2, encrypted2); 521 evaluator.AddInplace(encrypted1, encrypted2); 522 timeAddSum.Stop(); 523 524 /* 525 [Multiply] 526 */ 527 encrypted1.Reserve(3); 528 timeMultiplySum.Start(); 529 evaluator.MultiplyInplace(encrypted1, encrypted2); 530 timeMultiplySum.Stop(); 531 532 /* 533 [Multiply Plain] 534 */ 535 timeMultiplyPlainSum.Start(); 536 evaluator.MultiplyPlainInplace(encrypted2, plain); 537 timeMultiplyPlainSum.Stop(); 538 539 /* 540 [Square] 541 */ 542 timeSquareSum.Start(); 543 evaluator.SquareInplace(encrypted2); 544 timeSquareSum.Stop(); 545 546 if (context.UsingKeyswitching) 547 { 548 /* 549 [Relinearize] 550 */ 551 timeRelinearizeSum.Start(); 552 evaluator.RelinearizeInplace(encrypted1, relinKeys); 553 timeRelinearizeSum.Stop(); 554 555 /* 556 [Rescale] 557 */ 558 timeRescaleSum.Start(); 559 evaluator.RescaleToNextInplace(encrypted1); 560 timeRescaleSum.Stop(); 561 562 /* 563 [Rotate Vector] 564 */ 565 timeRotateOneStepSum.Start(); 566 evaluator.RotateVectorInplace(encrypted, 1, galKeys); 567 evaluator.RotateVectorInplace(encrypted, -1, galKeys); 568 timeRotateOneStepSum.Stop(); 569 570 /* 571 [Rotate Vector Random] 572 */ 573 // ckksEncoder.SlotCount is always a power of 2. 574 int randomRotation = rnd.Next() & ((int)ckksEncoder.SlotCount - 1); 575 timeRotateRandomSum.Start(); 576 evaluator.RotateVectorInplace(encrypted, randomRotation, galKeys); 577 timeRotateRandomSum.Stop(); 578 579 /* 580 [Complex Conjugate] 581 */ 582 timeConjugateSum.Start(); 583 evaluator.ComplexConjugateInplace(encrypted, galKeys); 584 timeConjugateSum.Stop(); 585 } 586 587 /* 588 [Serialize Ciphertext] 589 */ 590 using MemoryStream stream = new MemoryStream(); 591 timeSerializeSum.Start(); 592 encrypted.Save(stream, ComprModeType.None); 593 timeSerializeSum.Stop(); 594 595 if (hasZLIB) 596 { 597 /* 598 [Serialize Ciphertext (ZLIB)] 599 */ 600 timeSerializeZLIBSum.Start(); 601 encrypted.Save(stream, ComprModeType.ZLIB); 602 timeSerializeZLIBSum.Stop(); 603 } 604 605 if (hasZSTD) 606 { 607 /* 608 [Serialize Ciphertext (Zstandard)] 609 */ 610 timeSerializeZSTDSum.Start(); 611 encrypted.Save(stream, ComprModeType.ZSTD); 612 timeSerializeZSTDSum.Stop(); 613 } 614 615 /* 616 Print a dot to indicate progress. 617 */ 618 Console.Write("."); 619 Console.Out.Flush(); 620 } 621 622 Console.WriteLine(" Done"); 623 Console.WriteLine(); 624 Console.Out.Flush(); 625 626 int avgEncode = (int)(timeEncodeSum.Elapsed.TotalMilliseconds * 1000 / count); 627 int avgDecode = (int)(timeDecodeSum.Elapsed.TotalMilliseconds * 1000 / count); 628 int avgEncrypt = (int)(timeEncryptSum.Elapsed.TotalMilliseconds * 1000 / count); 629 int avgDecrypt = (int)(timeDecryptSum.Elapsed.TotalMilliseconds * 1000 / count); 630 int avgAdd = (int)(timeAddSum.Elapsed.TotalMilliseconds * 1000 / (3 * count)); 631 int avgMultiply = (int)(timeMultiplySum.Elapsed.TotalMilliseconds * 1000 / count); 632 int avgMultiplyPlain = (int)(timeMultiplyPlainSum.Elapsed.TotalMilliseconds * 1000 / count); 633 int avgSquare = (int)(timeSquareSum.Elapsed.TotalMilliseconds * 1000 / count); 634 int avgRelinearize = (int)(timeRelinearizeSum.Elapsed.TotalMilliseconds * 1000 / count); 635 int avgRescale = (int)(timeRescaleSum.Elapsed.TotalMilliseconds * 1000 / count); 636 int avgRotateOneStep = (int)(timeRotateOneStepSum.Elapsed.TotalMilliseconds * 1000 / (2 * count)); 637 int avgRotateRandom = (int)(timeRotateRandomSum.Elapsed.TotalMilliseconds * 1000 / count); 638 int avgConjugate = (int)(timeConjugateSum.Elapsed.TotalMilliseconds * 1000 / count); 639 int avgSerializeSum = (int)(timeSerializeSum.Elapsed.TotalMilliseconds * 1000 / count); 640 int avgSerializeZLIBSum = (int)(timeSerializeZLIBSum.Elapsed.TotalMilliseconds * 1000 / count); 641 int avgSerializeZSTDSum = (int)(timeSerializeZSTDSum.Elapsed.TotalMilliseconds * 1000 / count); 642 643 Console.WriteLine($"Average encode: {avgEncode} microseconds"); 644 Console.WriteLine($"Average decode: {avgDecode} microseconds"); 645 Console.WriteLine($"Average encrypt: {avgEncrypt} microseconds"); 646 Console.WriteLine($"Average decrypt: {avgDecrypt} microseconds"); 647 Console.WriteLine($"Average add: {avgAdd} microseconds"); 648 Console.WriteLine($"Average multiply: {avgMultiply} microseconds"); 649 Console.WriteLine($"Average multiply plain: {avgMultiplyPlain} microseconds"); 650 Console.WriteLine($"Average square: {avgSquare} microseconds"); 651 if (context.UsingKeyswitching) 652 { 653 Console.WriteLine($"Average relinearize: {avgRelinearize} microseconds"); 654 Console.WriteLine($"Average rescale: {avgRescale} microseconds"); 655 Console.WriteLine($"Average rotate vector one step: {avgRotateOneStep} microseconds"); 656 Console.WriteLine($"Average rotate vector random: {avgRotateRandom} microseconds"); 657 Console.WriteLine($"Average complex conjugate: {avgConjugate} microseconds"); 658 } 659 Console.WriteLine($"Average serialize ciphertext: {avgSerializeSum} microseconds"); 660 if (hasZLIB) 661 { 662 Console.WriteLine( 663 $"Average compressed (ZLIB) serialize ciphertext: {avgSerializeZLIBSum} microseconds"); 664 } 665 if (hasZSTD) 666 { 667 Console.WriteLine( 668 $"Average compressed (Zstandard) serialize ciphertext: {avgSerializeZSTDSum} microseconds"); 669 } 670 671 Console.Out.Flush(); 672 } 673 ExampleBFVPerformanceDefault()674 private static void ExampleBFVPerformanceDefault() 675 { 676 Utilities.PrintExampleBanner("BFV Performance Test with Degrees: 4096, 8192, and 16384"); 677 678 using EncryptionParameters parms = new EncryptionParameters(SchemeType.BFV); 679 ulong polyModulusDegree = 4096; 680 parms.PolyModulusDegree = polyModulusDegree; 681 parms.CoeffModulus = CoeffModulus.BFVDefault(polyModulusDegree); 682 parms.PlainModulus = new Modulus(786433); 683 using (SEALContext context = new SEALContext(parms)) 684 { 685 BFVPerformanceTest(context); 686 } 687 688 Console.WriteLine(); 689 polyModulusDegree = 8192; 690 parms.PolyModulusDegree = polyModulusDegree; 691 parms.CoeffModulus = CoeffModulus.BFVDefault(polyModulusDegree); 692 parms.PlainModulus = new Modulus(786433); 693 using (SEALContext context = new SEALContext(parms)) 694 { 695 BFVPerformanceTest(context); 696 } 697 698 Console.WriteLine(); 699 polyModulusDegree = 16384; 700 parms.PolyModulusDegree = polyModulusDegree; 701 parms.CoeffModulus = CoeffModulus.BFVDefault(polyModulusDegree); 702 parms.PlainModulus = new Modulus(786433); 703 using (SEALContext context = new SEALContext(parms)) 704 { 705 BFVPerformanceTest(context); 706 } 707 708 /* 709 Comment out the following to run the biggest example. 710 */ 711 //Console.WriteLine(); 712 //polyModulusDegree = 32768; 713 //parms.PolyModulusDegree = polyModulusDegree; 714 //parms.CoeffModulus = CoeffModulus.BFVDefault(polyModulusDegree); 715 //parms.PlainModulus = new Modulus(786433); 716 //using (SEALContext context = new SEALContext(parms)) 717 //{ 718 // BFVPerformanceTest(context); 719 //} 720 } 721 ExampleBFVPerformanceCustom()722 private static void ExampleBFVPerformanceCustom() 723 { 724 Console.Write("> Set PolyModulusDegree (1024, 2048, 4096, 8192, 16384, or 32768): "); 725 string input = Console.ReadLine(); 726 if (!ulong.TryParse(input, out ulong polyModulusDegree)) 727 { 728 Console.WriteLine("Invalid option."); 729 return; 730 } 731 if (polyModulusDegree < 1024 || polyModulusDegree > 32768 || 732 (polyModulusDegree & (polyModulusDegree - 1)) != 0) 733 { 734 Console.WriteLine("Invalid option."); 735 return; 736 } 737 738 string banner = $"BFV Performance Test with Degree: {polyModulusDegree}"; 739 Utilities.PrintExampleBanner(banner); 740 741 using EncryptionParameters parms = new EncryptionParameters(SchemeType.BFV) 742 { 743 PolyModulusDegree = polyModulusDegree, 744 CoeffModulus = CoeffModulus.BFVDefault(polyModulusDegree) 745 }; 746 if (polyModulusDegree == 1024) 747 { 748 parms.PlainModulus = new Modulus(12289); 749 } 750 else 751 { 752 parms.PlainModulus = new Modulus(786433); 753 } 754 755 using (SEALContext context = new SEALContext(parms)) 756 { 757 BFVPerformanceTest(context); 758 } 759 } 760 ExampleCKKSPerformanceDefault()761 private static void ExampleCKKSPerformanceDefault() 762 { 763 Utilities.PrintExampleBanner("CKKS Performance Test with Degrees: 4096, 8192, and 16384"); 764 765 // It is not recommended to use BFVDefault primes in CKKS. However, for performance 766 // test, BFVDefault primes are good enough. 767 using EncryptionParameters parms = new EncryptionParameters(SchemeType.CKKS); 768 ulong polyModulusDegree = 4096; 769 parms.PolyModulusDegree = polyModulusDegree; 770 parms.CoeffModulus = CoeffModulus.BFVDefault(polyModulusDegree); 771 using (SEALContext context = new SEALContext(parms)) 772 { 773 CKKSPerformanceTest(context); 774 } 775 776 Console.WriteLine(); 777 polyModulusDegree = 8192; 778 parms.PolyModulusDegree = polyModulusDegree; 779 parms.CoeffModulus = CoeffModulus.BFVDefault(polyModulusDegree); 780 using (SEALContext context = new SEALContext(parms)) 781 { 782 CKKSPerformanceTest(context); 783 } 784 785 Console.WriteLine(); 786 polyModulusDegree = 16384; 787 parms.PolyModulusDegree = polyModulusDegree; 788 parms.CoeffModulus = CoeffModulus.BFVDefault(polyModulusDegree); 789 using (SEALContext context = new SEALContext(parms)) 790 { 791 CKKSPerformanceTest(context); 792 } 793 794 /* 795 Comment out the following to run the biggest example. 796 */ 797 //Console.WriteLine(); 798 //polyModulusDegree = 32768; 799 //parms.PolyModulusDegree = polyModulusDegree; 800 //parms.CoeffModulus = CoeffModulus.BFVDefault(polyModulusDegree); 801 //using (SEALContext context = new SEALContext(parms)) 802 //{ 803 // CKKSPerformanceTest(context); 804 //} 805 } 806 ExampleCKKSPerformanceCustom()807 private static void ExampleCKKSPerformanceCustom() 808 { 809 Console.Write("> Set PolyModulusDegree (1024, 2048, 4096, 8192, 16384, or 32768): "); 810 string input = Console.ReadLine(); 811 if (!ulong.TryParse(input, out ulong polyModulusDegree)) 812 { 813 Console.WriteLine("Invalid option."); 814 return; 815 } 816 if (polyModulusDegree < 1024 || polyModulusDegree > 32768 || 817 (polyModulusDegree & (polyModulusDegree - 1)) != 0) 818 { 819 Console.WriteLine("Invalid option."); 820 return; 821 } 822 823 string banner = $"CKKS Performance Test with Degree: {polyModulusDegree}"; 824 Utilities.PrintExampleBanner(banner); 825 826 using EncryptionParameters parms = new EncryptionParameters(SchemeType.CKKS) 827 { 828 PolyModulusDegree = polyModulusDegree, 829 CoeffModulus = CoeffModulus.BFVDefault(polyModulusDegree) 830 }; 831 832 using (SEALContext context = new SEALContext(parms)) 833 { 834 CKKSPerformanceTest(context); 835 } 836 } 837 ExamplePerformanceTest()838 private static void ExamplePerformanceTest() 839 { 840 Utilities.PrintExampleBanner("Example: Performance Test"); 841 842 if (!Stopwatch.IsHighResolution) 843 { 844 Console.WriteLine("WARNING: High resolution stopwatch not available in this machine."); 845 Console.WriteLine(" Timings might not be accurate."); 846 } 847 848 while (true) 849 { 850 Console.WriteLine(); 851 Console.WriteLine("Select a scheme (and optionally PolyModulusDegree):"); 852 Console.WriteLine(" 1. BFV with default degrees"); 853 Console.WriteLine(" 2. BFV with a custom degree"); 854 Console.WriteLine(" 3. CKKS with default degrees"); 855 Console.WriteLine(" 4. CKKS with a custom degree"); 856 Console.WriteLine(" 0. Back to main menu"); 857 Console.WriteLine(); 858 859 ConsoleKeyInfo key; 860 do 861 { 862 Console.Write("> Run performance test (1 ~ 4) or go back (0): "); 863 key = Console.ReadKey(); 864 Console.WriteLine(); 865 } while (key.KeyChar < '0' || key.KeyChar > '4'); 866 switch (key.Key) 867 { 868 case ConsoleKey.D1: 869 ExampleBFVPerformanceDefault(); 870 break; 871 872 case ConsoleKey.D2: 873 ExampleBFVPerformanceCustom(); 874 break; 875 876 case ConsoleKey.D3: 877 ExampleCKKSPerformanceDefault(); 878 break; 879 880 case ConsoleKey.D4: 881 ExampleCKKSPerformanceCustom(); 882 break; 883 884 case ConsoleKey.D0: 885 Console.WriteLine(); 886 return; 887 888 default: 889 Console.WriteLine(" [Beep~~] Invalid option: type 0 ~ 4"); 890 break; 891 } 892 } 893 } 894 } 895 } 896