Skip to content

SEAL memory leak #25

Open
Open
@isentropic

Description

@sloede Thanks again for this package this has been a learning journey for me with julia, ccall and binary builder. I have succeeded in bumping the version to 4.1.1 locally and unfortunately I did not see any performance gains as I was hoping for. Just as a backstory I'm trying to implement a large machine learning model (CNN) using seal. Before this I was using pyseal and did everything in python, but now I realized that I need multi-threading to bring the performance to the next level. The reason why I was pushing for 4.1.1 is that there was a promised multiplication performance improvement in 3.7.x version. However, for some reason pyseal is consistently like 30% faster than SEAL.jl even after I compile SEAL_jll manually. Though my benchmarks suggested that single ckks cipher-cipher multiplication if anything is faster in SEAL.jl but when I'm doing a lot of them many times, pyseal seems to take over. Maybe this has to do with the following.

I attempted to do multithreading, i.e. perform many cipher-cipher multiplications in parallel using FLoops.jl. It worked fine, but then I noticed sudden crashes which I attributed to non-stopping memory growth. Then I decided to test this with a single thread, and this is a mwe i came up with:

using SEAL
function create_seal_params(poly_modulus_degree=2^13, modulus_chain=[60, 60, 60])
    parms = EncryptionParameters(SchemeType.ckks)

    set_poly_modulus_degree!(parms, poly_modulus_degree)
    set_coeff_modulus!(parms, coeff_modulus_create(poly_modulus_degree, modulus_chain))

    context = SEALContext(parms)

    keygen = KeyGenerator(context)
    public_key_ = PublicKey()
    create_public_key!(public_key_, keygen)
    secret_key_ = secret_key(keygen)
    encryptor = Encryptor(context, public_key_)
    evaluator = Evaluator(context)
    decryptor = Decryptor(context, secret_key_)

    encoder = CKKSEncoder(context)
    nslots = slot_count(encoder)
    scale = 2^50
    return (; encryptor, evaluator, decryptor, encoder, nslots)
end


function encrypt_into_cipher(number, initial_scale, encoder, encryptor)
    plain = Plaintext()
    encode!(plain, number, initial_scale, encoder)
    encrypted = Ciphertext()
    encrypt!(encrypted, plain, encryptor)
    return encrypted
end

function cipher_multiplication(cipher1, cipher2, evaluator)
    result = Ciphertext()
    multiply!(result, cipher1, cipher2, evaluator)
    return result
end

function cipher_addition(cipher1, cipher2, evaluator)
    result = Ciphertext()
    add!(result, cipher1, cipher2, evaluator)
    return result
end

function test()
    seal_params = create_seal_params()
    nciphers = 1_000_000
    ciphers = [encrypt_into_cipher(randn(), 2^50, seal_params.encoder, seal_params.encryptor) for i in 1:nciphers]


    while true
        i = rand(1:nciphers)
        j = rand(1:nciphers)
        k = rand(1:nciphers)
        ciphers[k] = cipher_addition(ciphers[i], ciphers[k], seal_params.evaluator)
    end
end

My julia is

julia> versioninfo()
Julia Version 1.9.4
Commit 8e5136fa297 (2023-11-14 08:46 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 128 × Intel(R) Xeon(R) Gold 6338 CPU @ 2.00GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-14.0.6 (ORCJIT, icelake-server)
  Threads: 1 on 128 virtual cores

using a fresh Project.toml environment, this script linearly consumes more and more memory and I assume would eventually crash, GC.gc() does not help. I was trying to diagnose how is this happening, found a good text about memory management in ccall but I have no idea really. This is all new.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions