When testing the CKKS scheme, I observed that for scale_mod_size = 40
, batch_size = 32768
, and N = 65536
, as the mult_depth
increases from 1 to 24, with 100 multiplications per layer and averaging the time, there is a noticeable decrease in time when the depth is 5 and 21, rather than a consistent linear increase. Could you explain this phenomenon? I would greatly appreciate it. The experimental results are shown in the image.
Is this behavior consistent? That is, if you repeat this experiment over and over, do you notice the same behavior?
Did you use multi-threading? I would use a single-thread setup for such experiments.
Also, are you using a shared or dedicated machine for this test?
This is a strange behavior. It could be caused by system overload or some random compute spikes.
openfhe-python 0.9.0
openfhe-development v1.2.2
from openfhe import *
import time
import matplotlib.pyplot as plt
import numpy as np # Import numpy for generating random float numbers
def main():
scale_mod_size = 40
batch_size = 32768
depths = list(range(1, 25))
mult_times = []
np.random.seed(42)
# Generate two lists of 32768 random floats
x1 = np.random.uniform(low=0.0, high=1.0, size=batch_size).tolist()
x2 = np.random.uniform(low=0.0, high=1.0, size=batch_size).tolist()
# Open a text file for writing results
with open("multiplicative_depth_results.txt", "w") as file:
# Write the header
file.write("Multiplicative Depth\tAvg EvalMult Time (seconds)\n")
for mult_depth in depths:
print(f"\nTesting multiplicative depth: {mult_depth}")
# Step 1: Set parameters
parameters = CCParamsCKKSRNS()
parameters.SetMultiplicativeDepth(mult_depth)
parameters.SetScalingModSize(scale_mod_size)
parameters.SetBatchSize(batch_size)
parameters.SetRingDim(65536)
# Step 2: Generate the context
cc = GenCryptoContext(parameters)
cc.Enable(PKESchemeFeature.PKE)
cc.Enable(PKESchemeFeature.KEYSWITCH)
cc.Enable(PKESchemeFeature.LEVELEDSHE)
print("Ring dimension used by CKKS scheme: " + str(cc.GetRingDimension()))
# Step 3: Key generation
keys = cc.KeyGen()
cc.EvalMultKeyGen(keys.secretKey)
# Convert the generated random floats to CKKS plaintexts
ptx1 = cc.MakeCKKSPackedPlaintext(x1)
ptx2 = cc.MakeCKKSPackedPlaintext(x2)
# Encrypt the plaintexts
c1 = cc.Encrypt(keys.publicKey, ptx1)
c2 = cc.Encrypt(keys.publicKey, ptx2)
# Measure the time taken for homomorphic multiplication (10 times for averaging)
start_time = time.time()
for _ in range(100):
c_mult = cc.EvalMult(c1, c2)
end_time = time.time()
total_time = (end_time - start_time)
avg_time = total_time / 100 # Calculate the average time over 10 runs
mult_times.append(avg_time)
print(f"Multiplicative depth {mult_depth}: Avg EvalMult time {avg_time:.6f} seconds")
# Write the result to the text file
file.write(f"{mult_depth}\t{avg_time:.6f}\n")
# Plot the relationship between multiplicative depth and time taken
plt.figure(figsize=(12, 7))
plt.plot(depths, mult_times, marker='o', linestyle='-', color='b')
plt.title('Multiplicative Depth vs Homomorphic Multiplication Time')
plt.xlabel('Multiplicative Depth (Layers)')
plt.ylabel('Homomorphic Multiplication Time (Seconds)')
plt.grid(True)
plt.xticks(depths) # Ensure all depths have labels on the x-axis
plt.tight_layout()
# Display the plot
plt.show()
# (Optional) Save the plot as an image file
plt.savefig("multiplicative_depth_plot.png")
print("\nTest results saved to 'multiplicative_depth_results.txt', plot saved to 'multiplicative_depth_plot.png'.")
if name == “main”:
main()