parallel_fhe.rs 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::time::Instant;
  19. use rayon::prelude::*;
  20. use tfhe::integer::ciphertext::RadixCiphertext;
  21. use tfhe::integer::ServerKey;
  22. use crate::NUMBER_OF_BLOCKS;
  23. // Calculate the element sum of the given vector in parallel
  24. fn vector_sum(server_key: &ServerKey, orders: Vec<RadixCiphertext>) -> RadixCiphertext {
  25. orders.into_par_iter().reduce(
  26. || server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS),
  27. |mut acc: RadixCiphertext, mut ele: RadixCiphertext| {
  28. server_key.smart_add_parallelized(&mut acc, &mut ele)
  29. },
  30. )
  31. }
  32. fn fill_orders(
  33. server_key: &ServerKey,
  34. orders: &mut [RadixCiphertext],
  35. total_volume: RadixCiphertext,
  36. ) {
  37. let mut volume_left_to_transact = total_volume;
  38. for order in orders {
  39. let mut filled_amount =
  40. server_key.smart_min_parallelized(&mut volume_left_to_transact, order);
  41. server_key.smart_sub_assign_parallelized(&mut volume_left_to_transact, &mut filled_amount);
  42. *order = filled_amount;
  43. }
  44. }
  45. /// FHE implementation of the volume matching algorithm.
  46. ///
  47. /// This version of the algorithm utilizes parallelization to speed up the computation.
  48. ///
  49. /// Matches the given encrypted [sell_orders] with encrypted [buy_orders] using the given
  50. /// [server_key]. The amount of the orders that are successfully filled is written over the original
  51. /// order count.
  52. pub fn volume_match(
  53. sell_orders: &mut [RadixCiphertext],
  54. buy_orders: &mut [RadixCiphertext],
  55. server_key: &ServerKey,
  56. ) {
  57. println!("Calculating total sell and buy volumes...");
  58. let time = Instant::now();
  59. // Total sell and buy volumes can be calculated in parallel because they have no dependency on
  60. // each other.
  61. let (mut total_sell_volume, mut total_buy_volume) = rayon::join(
  62. || vector_sum(server_key, sell_orders.to_owned()),
  63. || vector_sum(server_key, buy_orders.to_owned()),
  64. );
  65. println!(
  66. "Total sell and buy volumes are calculated in {:?}",
  67. time.elapsed()
  68. );
  69. println!("Calculating total volume to be matched...");
  70. let time = Instant::now();
  71. let total_volume =
  72. server_key.smart_min_parallelized(&mut total_sell_volume, &mut total_buy_volume);
  73. println!(
  74. "Calculated total volume to be matched in {:?}",
  75. time.elapsed()
  76. );
  77. println!("Filling orders...");
  78. let time = Instant::now();
  79. rayon::join(
  80. || fill_orders(server_key, sell_orders, total_volume.clone()),
  81. || fill_orders(server_key, buy_orders, total_volume.clone()),
  82. );
  83. println!("Filled orders in {:?}", time.elapsed());
  84. }