我正在Go中为SQRL客户端实施EnScrypt.该函数需要运行,直到它使用最少的cpu时间.我的Python代码如下所示:
def enscrypt_time(salt,password,seconds,n=9,r=256):
N = 1 << n
start = time.process_time()
end = start + seconds
data = acc = scrypt.hash(password,salt,N,r,1,32)
i = 1
while time.process_time() < end:
data = scrypt.hash(password,data,32)
acc = xor_bytes(acc,data)
i += 1
return i,time.process_time() - start,acc
除了process_time函数之外,将其转换为Go非常简单.
我无法使用time.Time / Timer,因为它们会测量挂钟时间(受系统上可能正在运行的其他所有内容的影响).我需要实际使用的cpu时间,理想情况是函数,或者至少需要运行的线程或进程.
Go等效于process_time是什么?
https://docs.python.org/3/library/time.html#time.process_time
@H_
404_16@最佳答案
您可以使用
runtime.LockOSThread()
将
调用goroutine连接到其当前的OS线程.这将确保不会为此线程安排其他goroutine,因此您的goroutine将运行而不会被中断或暂停.当线程被锁定时,没有其他goroutine会干扰.
在此之后,您只需要一个循环,直到给定的秒数过去.您必须调用runtime.UnlockOSThread()
以“释放”该线程并使其可供其他goroutine执行,最好以延迟语句完成.
看这个例子:
func runUntil(end time.Time) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
for time.Now().Before(end) {
}
}
为了使它等待2秒,它可能看起来像这样:
start := time.Now()
end := start.Add(time.Second * 2)
runUntil(end)
fmt.Println("Verify:",time.Now().Sub(start))
这打印例如:
Verify: 2.0004556s
当然,您也可以指定不到一秒钟,例如等待100毫秒:
start := time.Now()
runUntil(start.Add(time.Millisecond * 100))
fmt.Println("Verify:",time.Now().Sub(start))
输出:
Verify: 100.1278ms
您可以使用此功能的不同版本,如果这更适合您,需要花费时间“等待”作为值time.Duration
:
func wait(d time.Duration) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
for end := time.Now().Add(d); time.Now().Before(end); {
}
}
使用这个:
start = time.Now()
wait(time.Millisecond * 200)
fmt.Println("Verify:",time.Now().Sub(start))
输出:
Verify: 200.1546ms
注意:请注意,上述函数中的循环将无情地使用cpu,因为它们没有睡眠或阻塞IO,它们只查询当前系统时间并将其与截止时间进行比较.
如果攻击者通过多次并发尝试增加系统负载怎么办?
Go运行时限制了可以同时执行goroutine的系统线程.这是由runtime.GOMAXPROCS()
控制的,所以这已经是一个限制.它默认为可用cpu核心数,您可以随时更改它.这也是一个瓶颈,因为通过使用runtime.LockOSThread(),如果锁定线程的数量在任何给定时间等于GOMAXPROCS,那将阻止其他goroutine的执行,直到线程被解锁.
查看相关问题:
Number of threads used by Go runtime
Why does it not create many threads when many goroutines are blocked in writing file in golang?