在此记录Tenseal的学习笔记

介绍

在张量上进行同态计算的库,是对Seal的python版实现,给开发者提供简单的python接口,无需深究底层密码实现。

当前最新版本:3.11

位置:A library for doing homomorphic encryption operations on tensors

具备以下特点:

  • BFV方案的加解密(整数)
  • CKKS方案的加解密(浮点数)
  • 密文-密文、密文-明文的加法和乘法运算(同态计算)
  • 点积和矩阵乘法
  • 将Seal封装为tenseal.sealapi

安装

环境:MacOS + python3.9

pip安装

此方法安装出来的是Tenseal的库,是编译好的,是直接拿来用的,但不能源码修改,这种方法对于源码学习者,不建议。

前提:安装pip,也就是需要安装python,这里安装的是3.2版,、

一键安装: python3 pip install tenseal



举例:

(1)新建test.py文件

import tenseal as ts

# Setup TenSEAL context
context = ts.context(
ts.SCHEME_TYPE.CKKS,
poly_modulus_degree=8192,
coeff_mod_bit_sizes=[60, 40, 40, 60]
)
context.generate_galois_keys()
context.global_scale = 2**40 v1 = [0, 1, 2, 3, 4]
v2 = [4, 3, 2, 1, 0] # encrypted vectors【编码和加密】
enc_v1 = ts.ckks_vector(context, v1)
enc_v2 = ts.ckks_vector(context, v2) # 密文+密文
result = enc_v1 + enc_v2
result.decrypt() # ~ [4, 4, 4, 4, 4] # 点积:<密文,密文>
result = enc_v1.dot(enc_v2)
print(result.decrypt()) # ~ [10] matrix = [
[73, 0.5, 8],
[81, -5, 66],
[-100, -78, -2],
[0, 9, 17],
[69, 11 , 10],
]
# 密文向量*明文矩阵
result = enc_v1.matmul(matrix)
print(result.decrypt()) # ~ [157, -90, 153]

(2)执行:python3 test.py

cmake 安装

手动cmake安装,适合阅读源码者,这里安装的是最新版:3.11

(1)下载

git clone git://github.com/OpenMined/TenSEAL.git

(2)编译

mkdir build
cmake ..

翻好墙,耐心等待就行!

开始

Tenseal中很多细节都封装了,比如代码中就没有出现密钥生成算法!

同态加密

同态加密(HE)是一种加密技术,它允许对密文进行计算,并生成解密后与对明文进行相同计算的结果一致。

下面举个例子:

x = 7
y = 3 x_encrypted = HE.encrypt(x)
y_encrypted = HE.encrypt(y) z_encrypted = x_encrypted + y_encrypted # z should now be x + y = 10
z = HE.decrypt(z_encrypted)

TenSEALContext对象

TenSEALContext对象保存密钥和参数。

(1)下面创建一个TenSEALContext:

import tenseal as ts
context = ts.context(ts.SCHEME_TYPE.BFV, poly_modulus_degree=4096, plain_modulus=1032193)
context 输出:<tenseal.enc_context.Context object at 0x7fcd0b2e88b0>

需要指定要使用的HE方案(此处为BFV)及其参数。

(2)TenSEALContext现在持有私钥,可以其传递给需要私钥的函数。

public_context = ts.context(ts.SCHEME_TYPE.BFV, poly_modulus_degree=4096, plain_modulus=1032193)
print("Is the context private?", ("Yes" if public_context.is_private() else "No"))//私钥为不空返回 True
print("Is the context public?", ("Yes" if public_context.is_public() else "No"))//私钥为空返回 True sk = public_context.secret_key()//暂存私钥 # the context will drop the secret-key at this point,删除私钥
public_context.make_context_public()
print("Secret-key dropped")
print("Is the context private?", ("Yes" if public_context.is_private() else "No"))
print("Is the context public?", ("Yes" if public_context.is_public() else "No")) 输出:
Is the context private? Yes
Is the context public? No
Secret-key dropped
Is the context private? No
Is the context public? Yes

(3)TenSEALContext包含的属性很多,因此值得一提的是其他一些有趣的属性。比如用于设置自动重新线性化、重新缩放(仅适用于CKK)和模数切换的属性。这些属性默认启用,如下所示:

print("Automatic relinearization is:", ("on" if context.auto_relin else "off"))
print("Automatic rescaling is:", ("on" if context.auto_rescale else "off"))
print("Automatic modulus switching is:", ("on" if context.auto_mod_switch else "off"))
输出:
Automatic relinearization is: on
Automatic rescaling is: on
Automatic modulus switching is: on

(4)TenSEALContext 还提供一个全局默认的scale(在使用CKKS方案时),当用户不提供时,默认使用这个

# this should throw an error as the global_scale isn't defined yet
try:
print("global_scale:", context.global_scale)
except ValueError:
print("The global_scale isn't defined yet") # you can define it to 2 ** 20 for instance
context.global_scale = 2 ** 20
print("global_scale:", context.global_scale) 输出:
The global_scale isn't defined yet
global_scale: 1048576.0

加密和计算

(1)创建一个加密的整数向量。

plain_vector = [60, 66, 73, 81, 90]
encrypted_vector = ts.bfv_vector(context, plain_vector)
print("We just encrypted our plaintext vector of size:", encrypted_vector.size())
encrypted_vector 输出:
We just encrypted our plaintext vector of size: 5
<tenseal.tensors.bfvvector.BFVVector object at 0x7f8446d27e50>

这里是将一个明文向量加密(编码、加密)为一个BFV密文向量

(2)进行密文加法、减法和乘法。

#密文+明文
add_result = encrypted_vector + [1, 2, 3, 4, 5]
print(add_result.decrypt())
#密文-明文
sub_result = encrypted_vector - [1, 2, 3, 4, 5]
print(sub_result.decrypt())
#密文*明文
mul_result = encrypted_vector * [1, 2, 3, 4, 5]
print(mul_result.decrypt())
#密文+密文
encrypted_add = add_result + sub_result
print(encrypted_add.decrypt())
#密文-密文
encrypted_sub = encrypted_add - encrypted_vector
print(encrypted_sub.decrypt())
#密文*密文
encrypted_mul = encrypted_add * encrypted_sub
print(encrypted_mul.decrypt()) 输出:
[60, 66, 73, 81, 90]
We just encrypted our plaintext vector of size: 5
[61, 68, 76, 85, 95]
[59, 64, 70, 77, 85]
[60, 132, 219, 324, 450]
[120, 132, 146, 162, 180]
[60, 66, 73, 81, 90]
[7200, 8712, 10658, 13122, 16200]

(3)c2p比c2c计算快的多

ciphertext to plaintext (c2p) and ciphertext to ciphertext (c2c)

import tenseal as ts
from time import time # Setup TenSEAL context
context = ts.context(
ts.SCHEME_TYPE.CKKS,
poly_modulus_degree=8192,
coeff_mod_bit_sizes=[60, 40, 40, 60]
)
context.generate_galois_keys()
context.global_scale = 2**40 v1 = [0, 1111, 2222, 3333, 4444]
v2 = [4444, 3333, 2222, 1111, 0] # encrypted vectors【编码和加密】
enc_v1 = ts.ckks_vector(context, v1)
enc_v2 = ts.ckks_vector(context, v2) t_start = time()
_ = enc_v1 * enc_v2 #密文*密文
t_end = time()
print("c2c multiply time: {} ms".format((t_end - t_start) * 1000)) t_start = time()
_ = enc_v1 * v2 #密文*明文
t_end = time()
print("c2p multiply time: {} ms".format((t_end - t_start) * 1000)) t_start = time()
_ = enc_v1.dot(enc_v2) #<密文,密文>
t_end = time()
print(_.decrypt())
print("<c,c> time: {} ms".format((t_end - t_start) * 1000)) t_start = time()
_ = enc_v1.dot_(v2) #<密文,明文>
t_end = time()
print(_.decrypt())
print("<c,p> multiply time: {} ms".format((t_end - t_start) * 1000)) 输出:
c2c multiply time: 10.8489990234375 ms
c2p multiply time: 3.325939178466797 ms
[12343211.655333618]
<c,c> time: 27.49800682067871 ms
[12343211.655338768]
<c,p> multiply time: 22.28689193725586 ms

在密文上的逻辑回归训练和计算

待补充

近似计算(CKKS)

本节介绍CKKS方案原理及其实现,详细的CKKS解读请参考:

'Part 1, Vanilla Encoding and Decoding'.

'Part 2, Full Encoding and Decoding'.

'Part 3, Encryption and Decryption'.

'Part 4, Multiplication and Relinearization'.

'Part 5, Rescaling'.

CKKS原理

中文参考:

CKKS Part1:普通编码和解码

CKKS Part2: CKKS的编码和解码

CKKS Part3: CKKS的加密和解密

CKKS Part4: CKKS的乘法和重线性化

CKKS Part5: CKKS的重缩放

大致方案流程:

参数

(1)缩放因子(scaling factor)

CKKS方案的第一步是将实数向量编码为明文多项式。

缩放因子指的是编码精度,用数字二进制表示。直观地说,我们讨论的是二进制精度,如下图所示:



(2)模多项式的级数(poly_modulus_degree)

即多项式环上的\(Z_q=Z_q[X]/F(X)\),\(F(X)\)的级数\(N\)。

\(N\)产生的影响:

  • 明文多项式的系数个数
  • 密文元素的大小
  • 方案的计算性能(越大越差)
  • 安全级别(越大越好)

在TenSEAL中,就像在Microsoft SEAL中一样,多项式模的次数必须是2的幂,比如:(1024,2048,4096,8192,16384,32768)

(3)模多项式的系数模数(coefficient modulus sizes)

多项式的系数模数(素数列表),即\(q\)。

\(q\)产生的影响:

  • 密文元素的大小
  • 方案的安全级数\(L\),即乘法次数
  • 安全级别(越大越好)

在TenSEAL中,就像在Microsoft SEAL中一样,系数模数中的每个素数必须最多为60位,并且必须满足mod 2*poly_modulus_degree=1

密钥

(1)私钥

用于解密,不共享,在TenSEALContext对象中

(2)公钥

用于加密

(3)计算密钥(relinearization keys)

用于重线性化(密钥交换),在乘法后用于降低密文维数。可公开

(4)伽罗瓦密钥(Galois Keys)

用于批处理密文的旋转。可公开

批处理向量的旋转的应用是密文求和

内部计算

这些操作由TenSEAL自动执行。

(1)重线性化(Relinearization)

该操作在密文乘法后由TenSEAL自动执行,将密文的维数降到2维。若密文的维数维\(K+1\),则计算密钥的维数为\(K-1\)

(2)重缩放(Rescaling)

每次在密文密文或者密文明文后由TenSEAL自动执行。

计算误差随同态乘法次数增多呈指数增长。为了克服这个问题,大多数HE方案通常使用模交换(module switching)技术。CKKS中,使用重缩放,相当于模数切换。可以降低误差。在同态乘法后使用重缩放,误差线性增长,而不是指数增长。

即给定密文的模数为\(q_1,...,q_k\),经过重缩放后,密文模数变为\(q_1,..,q_{k-1}\),所相应的缩小密文中的“明文值”。

此步骤消耗系数模数\(q_1,...,q_k\)中的一个素数。当你消耗掉所有的时候,你将无法执行更多的乘法运算,即Leveled-FHE方案。

使用

引入

import torch
from torchvision import transforms
from random import randint
import pickle
from PIL import Image
import numpy as np
from matplotlib.pyplot import imshow
from typing import Dict import tenseal as ts

Context

首先生成Context:

ctx = ts.context(ts.SCHEME_TYPE.CKKS, 8192, coeff_mod_bit_sizes=[60, 40, 40, 60])

其中:

  • 方案类型:ts.SCHEME_TYPE.CKKS
  • poly_modulus_degree:8192
  • coeff_mod_bit_sizes:系数模数大小,这里的[60, 40, 40, 60]表示系数模数将包含4个素数,分别为60位、40位、40位和60位。
  • global_scale:缩放因子(scaling factor),即\(2^{40}\)

TenSEAL支持在公钥和对称加密之间切换。默认情况下使用公钥加密。

默认情况下,会自动执行重线性化后和重缩放。通过generate_galois_keys产生伽罗瓦密钥(Galois Keys)

def context():
context = ts.context(ts.SCHEME_TYPE.CKKS, 8192, coeff_mod_bit_sizes=[60, 40, 40, 60])
context.global_scale = pow(2, 40)
context.generate_galois_keys()
return context context = context()

明文张量(PlainTensor)

张量:可以看成一种数据存储格式

PlainTensor类作为一个转换层,将普通数据类型(例如List,array等)转换为tenseal所支持的明文形式

import numpy as np

plain1 = ts.plain_tensor([1,2,3,4], [2,2])
print(" First tensor: Shape = {} Data = {}".format(plain1.shape, plain1.tolist())) plain2 = ts.plain_tensor(np.array([5,6,7,8]).reshape(2,2))
print(" Second tensor: Shape = {} Data = {}".format(plain2.shape, plain2.tolist())) 输出:
First tensor: Shape = [2, 2] Data = [[1.0, 2.0], [3.0, 4.0]]
Second tensor: Shape = [2, 2] Data = [[5.0, 6.0], [7.0, 8.0]]

从上面可以看出:plain1和plain2就是张量形式,包含数据和形状(shape)

加密

CKKS由于明文空间是浮点数或实数,而计算是在多项式环上,所以加密前需要先编码。

(1)编码

编码分为两步:浮点数 -》实数多项式 -》整数多项式

假设,模多项式的级数为\(N\),那么将\(N/2\)个浮点数编码到明文元素中,然后加密,同态计算就是对密文(多项式)上的系数计算(逐coefficient (一个系数就是一个slot?)),从而实现SIMD操作。整个过程叫做"打包"(batching



(2)加/解密

加密:对一个明文多项式加密

下面举一个例子:将明文张量(PlainTensor)加密为密文张量(encrypted tensor)

为了创建密文张量,TenSEAL会自动执行编码和加密。这适用于CKKS和BFV方案。

将明文张量(PlainTensor)加密为密文张量(encrypted tensor),存储形式为【密文、shape】

下面有几种密文张量形式:

  • BFVVector:1D(1维)整数数组
  • CKKSVector:1D(1维)浮点数数组
  • CKKSTensor:N维浮点数数组,支持密文张量的reshaping或者broadcasting操作

import tenseal as ts
import numpy as np # Setup TenSEAL context
context = ts.context(
ts.SCHEME_TYPE.CKKS,
poly_modulus_degree=8192,
coeff_mod_bit_sizes=[60, 40, 40, 60]
)
context.generate_galois_keys()
context.global_scale = 2**40 plain1 = ts.plain_tensor([1,2,3,4], [2,2])
print(" First tensor: Shape = {} Data = {}".format(plain1.shape, plain1.tolist())) plain2 = ts.plain_tensor(np.array([5,6,7,8]).reshape(2,2))
print(" Second tensor: Shape = {} Data = {}".format(plain2.shape, plain2.tolist())) encrypted_tensor1 = ts.ckks_tensor(context, plain1)
encrypted_tensor2 = ts.ckks_tensor(context, plain2) print(" Shape = {}".format(encrypted_tensor1.shape))
print(" Encrypted Data = {}.".format(encrypted_tensor1)) encrypted_tensor_from_np = ts.ckks_tensor(context, np.array([5,6,7,8]).reshape([2,2]))
print(" Shape = {}".format(encrypted_tensor_from_np.shape)) 输出:
First tensor: Shape = [2, 2] Data = [[1.0, 2.0], [3.0, 4.0]]
Second tensor: Shape = [2, 2] Data = [[5.0, 6.0], [7.0, 8.0]]
Shape = [2, 2]
Encrypted Data = <tenseal.tensors.ckkstensor.CKKSTensor object at 0x7f9ddd530400>.
Shape = [2, 2]

从上面看出,将普通数据(list:[1,2,3,4])转换为明文张量(plain1),再加密为密文张量(encrypted_tensor1),内部存储【密文数据,shape】

同态计算

下面是CKKS所支持的密文张量计算:

下面举例:

import tenseal as ts
import numpy as np # Setup TenSEAL context
context = ts.context(
ts.SCHEME_TYPE.CKKS,
poly_modulus_degree=8192,
coeff_mod_bit_sizes=[60, 40, 40, 60]
)
context.generate_galois_keys()
context.global_scale = 2**40 def decrypt(enc):
return enc.decrypt().tolist() plain1 = ts.plain_tensor([1,2,3,4], [2,2])
print("First tensor: Shape = {} Data = {}".format(plain1.shape, plain1.tolist())) plain2 = ts.plain_tensor(np.array([5,6,7,8]).reshape(2,2))
print("Second tensor: Shape = {} Data = {}".format(plain2.shape, plain2.tolist())) encrypted_tensor1 = ts.ckks_tensor(context, plain1)
encrypted_tensor2 = ts.ckks_tensor(context, plain2) #密文(张量)+ 密文(张量)
result = encrypted_tensor1 + encrypted_tensor2
print("Plain equivalent: {} + {}\nDecrypted result: {}.".format(plain1.tolist(), plain2.tolist(), decrypt(result))) #密文(张量)- 密文(张量)
result = encrypted_tensor1 - encrypted_tensor2
print("Plain equivalent: {} - {}\nDecrypted result: {}.".format(plain1.tolist(), plain2.tolist(), decrypt(result))) #密文(张量)* 密文(张量)
result = encrypted_tensor1 * encrypted_tensor2
print("Plain equivalent: {} * {}\nDecrypted result: {}.".format(plain1.tolist(), plain2.tolist(), decrypt(result))) #密文(张量)* 明文(张量)
plain = ts.plain_tensor([5,6,7,8], [2,2])
result = encrypted_tensor1 * plain
print("Plain equivalent: {} * {}\nDecrypted result: {}.".format(plain1.tolist(), plain.tolist(), decrypt(result))) #取反:密文(张量)
result = -encrypted_tensor1
print("Plain equivalent: -{}\nDecrypted result: {}.".format(plain1.tolist(), decrypt(result))) #求幂:密文(张量)^3
result = encrypted_tensor1 ** 3
print("Plain equivalent: {} ^ 3\nDecrypted result: {}.".format(plain1.tolist(), decrypt(result))) #多项式计算(整数):1 + X^2 + X^3,X是密文(张量)
result = encrypted_tensor1.polyval([1,0,1,1])
print("X = {}".format(plain1.tolist()))
print("1 + X^2 + X^3 = {}.".format(decrypt(result))) #多项式计算(浮点数):1 + X^2 + X^3,X是密文(张量)
result = encrypted_tensor1.polyval([0.5, 0.197, 0, -0.004])
print("X = {}".format(plain1.tolist()))
print("0.5 + 0.197 X - 0.004 x^X = {}.".format(decrypt(result))) 输出:
First tensor: Shape = [2, 2] Data = [[1.0, 2.0], [3.0, 4.0]]
Second tensor: Shape = [2, 2] Data = [[5.0, 6.0], [7.0, 8.0]]
Plain equivalent: [[1.0, 2.0], [3.0, 4.0]] + [[5.0, 6.0], [7.0, 8.0]]
Decrypted result: [[6.000000000510762, 7.99999999944109], [10.000000000176103, 11.999999999918177]].
Plain equivalent: [[1.0, 2.0], [3.0, 4.0]] - [[5.0, 6.0], [7.0, 8.0]]
Decrypted result: [[-3.999999998000314, -3.9999999987240265], [-4.0000000013643, -4.0000000013791075]].
Plain equivalent: [[1.0, 2.0], [3.0, 4.0]] * [[5.0, 6.0], [7.0, 8.0]]
Decrypted result: [[5.000000678675058, 12.000001612431278], [21.000002812898412, 32.000004287986336]].
Plain equivalent: [[1.0, 2.0], [3.0, 4.0]] * [[5.0, 6.0], [7.0, 8.0]]
Decrypted result: [[5.000000676956037, 12.000001612473657], [21.000002810086173, 32.00000428474004]].
Plain equivalent: -[[1.0, 2.0], [3.0, 4.0]]
Decrypted result: [[-1.0000000012552241, -2.000000000358531], [-2.9999999994059015, -3.999999999269536]].
Plain equivalent: [[1.0, 2.0], [3.0, 4.0]] ^ 3
Decrypted result: [[1.0000008094463497, 8.000006439159353], [27.000021714154222, 64.00005146475934]].
X = [[1.0, 2.0], [3.0, 4.0]]
1 + X^2 + X^3 = [[3.000000945752252, 13.000006978595758], [37.00002291844665, 81.000053606697]].
X = [[1.0, 2.0], [3.0, 4.0]]
0.5 + 0.197 X - 0.004 x^X = [[0.6930000194866153, 0.8620000226394146], [0.9829999914891329, 1.0319998662943677]].

其中密文张量乘法后需要重线性化:



其中多项式计算(浮点数),来自:Logistic regression over encrypted data from fully homomorphic encryption

demo

下面对MNIST数据集的分类,使用一个卷积和两个完全连接的层以及一个平方激活函数。

它是同态加密的一个重要用例:来自:https://github.com/youben11/encrypted-evaluation

对卷积不了解,后期补充!

性能测试

下面将提供一些关于如何对同态加密应用程序进行基准测试的提示,并选择最合适的参数。

序列化:通信传输时需要序列化,比如:读写就是序列化

代码和结果:https://github.com/OpenMined/TenSEAL/blob/main/tutorials/Tutorial 3 - Benchmarks.ipynb

Context 序列化

结果:

  • 对称加密方案创建的Context比公钥加密方案创建的Context更小。
  • 减少系数模数(coefficient modulus)的长度会减少Context的大小,但也会减少可用乘法的深度\(L\),也会影响精度(对于CKKS)。
  • Galois密钥只会增加公共Context的大小(没有私钥)。仅当需要执行密文旋转时发送它们。
  • 重新线性密钥只会增加公共Context的大小。仅当需要执行密文乘法时才发送它们。
  • 当我们发送私钥时,可以重新生成重新线性化/伽罗瓦密钥,而无需发送它们。

密文(Ciphertext)序列化

设置的参数不同,会影响密文的序列化

对称或者公钥加密方案实际上并不影响密文的大小,只影响Context的大小。

下面结果是针对堆成加密场景:

【明文数据大小:8.8 KB】

  • 多项式模\(N\)的增加导致密文的增加。
  • 系数模数(coefficient modulus)的长度影响密文大小。
  • 系数模数大小的值会影响密文大小以及精度。
  • 对于一组固定的多项式模数\(N\)和系数模数,更改精度不会影响密文大小。

MNIST上的加密卷积

后续补充!

总结

Tenseal库的更多相关文章

  1. TENSEAL: A LIBRARY FOR ENCRYPTED TENSOR OP- ERATIONS USING HOMOMORPHIC ENCRYPTION 解读

    本文记录阅读该paper的笔记,这篇论文是TenSeal库的原理介绍. 摘要 机器学习算法已经取得了显著的效果,并被广泛应用于各个领域.这些算法通常依赖于敏感和私有数据,如医疗和财务记录.因此,进一步 ...

  2. 菜鸟Python学习笔记第一天:关于一些函数库的使用

    2017年1月3日 星期二 大一学习一门新的计算机语言真的很难,有时候连函数拼写出错查错都能查半天,没办法,谁让我英语太渣. 关于计算机语言的学习我想还是从C语言学习开始为好,Python有很多语言的 ...

  3. TinyWeb v1.0 正式完成第一个Release版本(功能基于 libuv 跨平台库)

    使用方法很简单,很容易融入现有项目,使现有项目拥有Web网站功能和WebSocket,以及Socket直连! 并且包含了一个跨平台(windows/linux)工具集合; 嗯,也挺棒的^,^ 在项目中 ...

  4. 在 Laravel 中使用图片处理库 Integration/Image

    系统需求 PHP >= 5.3 Fileinfo Extension GD Library (>=2.0) … or … Imagick PHP extension (>=6.5.7 ...

  5. [APUE]标准IO库(下)

    一.标准IO的效率 对比以下四个程序的用户CPU.系统CPU与时钟时间对比 程序1:系统IO 程序2:标准IO getc版本 程序3:标准IO fgets版本 结果: [注:该表截取自APUE,上表中 ...

  6. [APUE]标准IO库(上)

    一.流和FILE对象 系统IO都是针对文件描述符,当打开一个文件时,即返回一个文件描述符,然后用该文件描述符来进行下面的操作,而对于标准IO库,它们的操作则是围绕流(stream)进行的. 当打开一个 ...

  7. Python标准库--typing

    作者:zhbzz2007 出处:http://www.cnblogs.com/zhbzz2007 欢迎转载,也请保留这段声明.谢谢! 1 模块简介 Python 3.5 增加了一个有意思的库--typ ...

  8. Windows 常用运行库下载 (DirectX、VC++、.Net Framework等)

    经常听到有朋友抱怨他的电脑运行软件或者游戏时提示缺少什么 d3dx9_xx.dll 或 msvcp71.dll.msvcr71.dll又或者是 .Net Framework 初始化之类的错误而无法正常 ...

  9. vs2010静态链接MFC库报链接错误

    由于需要将MFC程序在其它电脑上运行,所以需要将动态链接的MFC改成静态链接,本以为很简单,没想到链接的时候出现下面的链接错误: uafxcw.lib(afxmem.obj) : error LNK2 ...

随机推荐

  1. servlet中的ServletConfig对象

    ServletConfig对象对应web.xml文件中的<servlet>节点.当Tomcat初始化一个Servlet时,会创建ServletConfig对象,并将该Servlet的配置信 ...

  2. 推荐一些好用的 HTML5 & JavaScript 游戏引擎开发库

    推荐一些好用的 HTML5 & JavaScript 游戏引擎开发库 0. 引言 如果你是一个游戏开发者,并且正在寻找一个可以与 JavaScript 和 HTML5 无缝工作的游戏引擎.那么 ...

  3. 微信小程序&mpvue问题总结(1)

    微信小程序进入到首页的时候,日志打印出"created", "onlaunch", "mounted",具体代码如下:那么,在小程序中 cr ...

  4. canvas系列教程07-canvas动画基础1

    上面我们玩了一个图表,大家学好结构,然后在那个基础上去扩展各种图表,慢慢就可以形成自己的图表库了.也可以多看看一些国外的图表库简单的版本,分析分析,读代码对提高用处很大.我说了canvas两大主流用途 ...

  5. sparksql Seq生成DataFrame

    首先,使用样例类: case class User(id:Int,name: String,gender:String, age: Int) 之后使用Seq创建Dataframe val alice: ...

  6. 模拟FCFS调度算法(先来先服务)

    文章目录 一.FCFS的介绍 二.代码演示 三.代码分析 1.使用节点模拟进程 2.SimulateFCFS(核心模拟FCFS类) 3.创建一个节点为n的队列(模拟就绪队列) 4.核心计算分析 5.输 ...

  7. SpringBoot-总结

    SpringBoot一站式开发 官网:https://spring.io/projects/spring-boot Spring Boot可以轻松创建独立的.基于Spring的生产级应用程序,它可以让 ...

  8. Java的虚拟线程(协程)特性开启预览阶段,多线程开发的难度将大大降低

    高并发.多线程一直是Java编程中的难点,也是面试题中的要点.Java开发者也一直在尝试使用多线程来解决应用服务器的并发问题.但是多线程并不容易,为此一个新的技术出现了,这就是虚拟线程. 传统多线程的 ...

  9. 【面试普通人VS高手】Kafka的零拷贝原理?

    最近一个学员去滴滴面试,在第二面的时候遇到了这个问题: "请你简单说一下Kafka的零拷贝原理" 然后那个学员努力在大脑里检索了很久,没有回答上来. 那么今天,我们基于这个问题来看 ...

  10. 2021牛客暑期多校训练营3 J 思维

    传送门 J-Counting Triangles_2021牛客暑期多校训练营3 (nowcoder.com) 题目 Goodeat finds an undirected complete graph ...