首页 > 编程语言 >tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x

时间:2022-11-16 23:31:24浏览次数:51  
标签:layers 10 tensorflow1 name queue session time tf 多线程


 

=================================================

 

 

从前文​​tensorflow1.x——如何在python多线程中调用同一个session会话 ​​可以知道,使用python多线程调用同一个session中的计算图并不能有显著的性能提升,虽然有小幅度的提升但是该提升更像是一个python线程发送cuda计算指令的间隔期间另一个python线程发送cuda计算指令,从而填补了空闲,有了小幅度的提升,但是总体来看python多线程调用通过session并不能实现多个计算图的并行执行,当然这样可以用python线程的GIL来解释,因此本文就使用C++线程来调用通过session,以此来判断TensorFlow1.x中是否可以有效的实现多线程并发执行同一个session中的同个计算图的计算。

 

给出代码:TensorFlow1.x

一个线程的情况:

import tensorflow as tf
from tensorflow import keras
import numpy as np
import threading
import time

def build():
n = 8
with tf.device("/gpu:0"):
x = tf.random_normal([n, 10])
x1 = tf.layers.dense(x, 10, activation=tf.nn.elu, name="fc1")
x2 = tf.layers.dense(x1, 10, activation=tf.nn.elu, name="fc2")
x3 = tf.layers.dense(x2, 10, activation=tf.nn.elu, name="fc3")
y = tf.layers.dense(x3, 10, activation=tf.nn.elu, name="fc4")

queue = tf.FIFOQueue(10000, y.dtype, y.shape, shared_name='buffer')
enqueue_ops = []
for _ in range(1):
enqueue_ops.append(queue.enqueue(y))
tf.train.add_queue_runner(tf.train.QueueRunner(queue, enqueue_ops))

return queue

# with sess.graph.as_default():
if __name__ == '__main__':
queue = build()
dequeued = queue.dequeue_many(4)

with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
tf.train.start_queue_runners()

a_time = time.time()
print(a_time)
for _ in range(100000):
sess.run(dequeued)
b_time = time.time()
print(b_time)
print(b_time-a_time)

time.sleep(11111)

View Code

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_深度学习

用时:

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_tensorflow_02

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_多线程_03

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_tensorflow_04

 

 

 

两个线程的情况:

import tensorflow as tf
from tensorflow import keras
import numpy as np
import threading
import time

def build():
n = 8
with tf.device("/gpu:0"):
x = tf.random_normal([n, 10])
x1 = tf.layers.dense(x, 10, activation=tf.nn.elu, name="fc1")
x2 = tf.layers.dense(x1, 10, activation=tf.nn.elu, name="fc2")
x3 = tf.layers.dense(x2, 10, activation=tf.nn.elu, name="fc3")
y = tf.layers.dense(x3, 10, activation=tf.nn.elu, name="fc4")

queue = tf.FIFOQueue(10000, y.dtype, y.shape, shared_name='buffer')
enqueue_ops = []
for _ in range(2):
enqueue_ops.append(queue.enqueue(y))
tf.train.add_queue_runner(tf.train.QueueRunner(queue, enqueue_ops))

return queue

# with sess.graph.as_default():
if __name__ == '__main__':
queue = build()
dequeued = queue.dequeue_many(4)

with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
tf.train.start_queue_runners()

a_time = time.time()
print(a_time)
for _ in range(100000):
sess.run(dequeued)
b_time = time.time()
print(b_time)
print(b_time-a_time)

time.sleep(11111)

View Code

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_tensorflow_05

用时:

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_多线程_06

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_多线程_07

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_深度学习_08

 

 

 

四个线程的情况:

import tensorflow as tf
from tensorflow import keras
import numpy as np
import threading
import time

def build():
n = 8
with tf.device("/gpu:0"):
x = tf.random_normal([n, 10])
x1 = tf.layers.dense(x, 10, activation=tf.nn.elu, name="fc1")
x2 = tf.layers.dense(x1, 10, activation=tf.nn.elu, name="fc2")
x3 = tf.layers.dense(x2, 10, activation=tf.nn.elu, name="fc3")
y = tf.layers.dense(x3, 10, activation=tf.nn.elu, name="fc4")

queue = tf.FIFOQueue(10000, y.dtype, y.shape, shared_name='buffer')
enqueue_ops = []
for _ in range(4):
enqueue_ops.append(queue.enqueue(y))
tf.train.add_queue_runner(tf.train.QueueRunner(queue, enqueue_ops))

return queue

# with sess.graph.as_default():
if __name__ == '__main__':
queue = build()
dequeued = queue.dequeue_many(4)

with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
tf.train.start_queue_runners()

a_time = time.time()
print(a_time)
for _ in range(100000):
sess.run(dequeued)
b_time = time.time()
print(b_time)
print(b_time-a_time)

time.sleep(11111)

View Code

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_tensorflow_09

用时:

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_Tensorflow_10

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_深度学习_11

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_多线程_12

 

 

 

八个线程的情况:

import tensorflow as tf
from tensorflow import keras
import numpy as np
import threading
import time

def build():
n = 8
with tf.device("/gpu:0"):
x = tf.random_normal([n, 10])
x1 = tf.layers.dense(x, 10, activation=tf.nn.elu, name="fc1")
x2 = tf.layers.dense(x1, 10, activation=tf.nn.elu, name="fc2")
x3 = tf.layers.dense(x2, 10, activation=tf.nn.elu, name="fc3")
y = tf.layers.dense(x3, 10, activation=tf.nn.elu, name="fc4")

queue = tf.FIFOQueue(10000, y.dtype, y.shape, shared_name='buffer')
enqueue_ops = []
for _ in range(8):
enqueue_ops.append(queue.enqueue(y))
tf.train.add_queue_runner(tf.train.QueueRunner(queue, enqueue_ops))

return queue

# with sess.graph.as_default():
if __name__ == '__main__':
queue = build()
dequeued = queue.dequeue_many(4)

with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
tf.train.start_queue_runners()

a_time = time.time()
print(a_time)
for _ in range(100000):
sess.run(dequeued)
b_time = time.time()
print(b_time)
print(b_time-a_time)

time.sleep(11111)

View Code

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_多线程_13

用时:

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_多线程_14

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_tensorflow_15

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_tensorflow_16

 

 

================================================

 

 

可以看到使用C++多线程调用TensorFlow1.x中的同一个session下的同一个计算图也没有得到线性的加速,大致情况和python多线程的情况类似,确实开多线程调用同个session中的同个计算图性能会得到一定的提升,但是这个提升幅度很小,远不是和线程数成正比关系的,对于这种多线程与单线程相比较小幅度的提升更可能是在同个session的同个计算图中对cuda的调用都是使用一个命令队列的,之所以多线程会有一定性能提升是因为弥补上了cpu端对gpu端cuda发送命令的间隔上的空隙。

 

那么我们使用同一个session的两个计算分支,然后分别用两个线程来运行,那么效果如何呢?

给出代码:

import tensorflow as tf
from tensorflow import keras
import numpy as np
import threading
import time

def build():
n = 8
with tf.device("/gpu:0"):
x = tf.random_normal([n, 10])
x1 = tf.layers.dense(x, 10, activation=tf.nn.elu, name="fc1")
x2 = tf.layers.dense(x1, 10, activation=tf.nn.elu, name="fc2")
x3 = tf.layers.dense(x2, 10, activation=tf.nn.elu, name="fc3")
y = tf.layers.dense(x3, 10, activation=tf.nn.elu, name="fc4")

_x = tf.random_normal([n, 10])
_x1 = tf.layers.dense(_x, 10, activation=tf.nn.elu, name="fc1x")
_x2 = tf.layers.dense(_x1, 10, activation=tf.nn.elu, name="fc2x")
_x3 = tf.layers.dense(_x2, 10, activation=tf.nn.elu, name="fc3x")
_y = tf.layers.dense(_x3, 10, activation=tf.nn.elu, name="fc4x")


queue = tf.FIFOQueue(10000, y.dtype, y.shape, shared_name='buffer')
enqueue_ops = []
for _ in range(1):
enqueue_ops.append(queue.enqueue(y))
enqueue_ops.append(queue.enqueue(_y))
tf.train.add_queue_runner(tf.train.QueueRunner(queue, enqueue_ops))

return queue

# with sess.graph.as_default():
if __name__ == '__main__':
queue = build()
dequeued = queue.dequeue_many(4)

with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
tf.train.start_queue_runners()

a_time = time.time()
print(a_time)
for _ in range(100000):
sess.run(dequeued)
b_time = time.time()
print(b_time)
print(b_time-a_time)

time.sleep(11111)

View Code

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_tensorflow_17

 

运算时间:

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_深度学习_18

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_tensorflow_19

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_Tensorflow_20

 

 

可以看到这个效果其实和同一个session下两个线程调用同个计算分支是相同的效果,那么这个问题会不会是出现在GPU上呢,如果我们的这两个计算分支分别在两个GPU上呢,给出代码:

import tensorflow as tf
from tensorflow import keras
import numpy as np
import threading
import time

def build():
n = 8
with tf.device("/gpu:0"):
x = tf.random_normal([n, 10])
x1 = tf.layers.dense(x, 10, activation=tf.nn.elu, name="fc1")
x2 = tf.layers.dense(x1, 10, activation=tf.nn.elu, name="fc2")
x3 = tf.layers.dense(x2, 10, activation=tf.nn.elu, name="fc3")
y = tf.layers.dense(x3, 10, activation=tf.nn.elu, name="fc4")

with tf.device("/gpu:1"):
_x = tf.random_normal([n, 10])
_x1 = tf.layers.dense(_x, 10, activation=tf.nn.elu, name="fc1x")
_x2 = tf.layers.dense(_x1, 10, activation=tf.nn.elu, name="fc2x")
_x3 = tf.layers.dense(_x2, 10, activation=tf.nn.elu, name="fc3x")
_y = tf.layers.dense(_x3, 10, activation=tf.nn.elu, name="fc4x")


queue = tf.FIFOQueue(10000, y.dtype, y.shape, shared_name='buffer')
enqueue_ops = []
for _ in range(1):
enqueue_ops.append(queue.enqueue(y))
enqueue_ops.append(queue.enqueue(_y))
tf.train.add_queue_runner(tf.train.QueueRunner(queue, enqueue_ops))

return queue

# with sess.graph.as_default():
if __name__ == '__main__':
queue = build()
dequeued = queue.dequeue_many(4)

with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
tf.train.start_queue_runners()

a_time = time.time()
print(a_time)
for _ in range(100000):
sess.run(dequeued)
b_time = time.time()
print(b_time)
print(b_time-a_time)

time.sleep(11111)

View Code

0号显卡由于还在运行Firefox上的电影播放任务因此比一号卡使用率高了些,不过这个代码对两个显卡的利用率应该都是在32%左右。

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_多线程_21

 

 

 

运行时间:

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_Tensorflow_22

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_Tensorflow_23

tensorflow1.x——如何在C++多线程中调用同一个session会话tensorflow1.x_Tensorflow_24

 

 

 

可以看到对结果影响最多的还是使用两个线程分别调用两个显卡上两个不同的计算分支,从这里我们可以给出一个粗略的结论,那就是在TensorFlow中多线程调用session中的计算分支并不能有显著的性能提升,但是使用多线程调用同一个session中的不同GPU上的计算分支却可以极大的提升计算效率,不过这样的话和TensorFlow的多进程运行就比较像了,同时考虑到多线程编程的复杂性因此除了强化学习以外的机器学习代码如果想多线程加速运算那还不如使用单机多进程加速了。

其实,即使是深度学习框架中性能最强的TensorFlow在设计的最初也是针对单线程调用设计的,这里的单线程是只CPU端的单线程,如果CPU端是多线程调用同一个显卡上的计算图往往会由于cuda的stream默认队列的限制导致并不会有显著性能提升,当然从技术上来说完全深度学习框架完全可以在设计时就考虑到cuda指令执行的stream默认队列问题,或许是设计难度和适用面较窄的问题,即使是TensorFlow也没有提供多线程调用cuda kernel的多个stream队列,或许从目前来看多进程加速深度学习框架计算确实还是最优性价比的解决方法,虽然多进程的同步开销较大、用户编写代码的逻辑变得复杂,但是也完全可以弥补上深度学习框架提供该功能的厂家方的花销代价。

 

-----------------------------------

 

至少从目前来看,多线程调用深度学习框架其实还不如使用多进程调用深度学习框架来的合适,不过多进程调用深度学习框架必然要面对进程之间网络模型的同步问题,这又成了一个提高用户编码难度的一个点了。TensorFlow是属于少数提供多线程封装调用的深度学习框架,即使对于TensorFlow来说使用C++多线程调用不同cuda计算分支的性能也没有多进程调用不同cuda计算分支的性能高,再加上使用TensorFlow中的多线程本就是小众特征,难以切换到其他深度学习框架上使用,因此目前来看多进程调用cuda相比与多线程调用cuda才更是深度学习框架的正解,当然如果未来深度学习框架可以提高C++多线程调用不同计算分支的性能,那么或许以后有一天C++多线程调用深度框架的性能会优于多进程调用的。

 

 

---------------------------------------------

 

标签:layers,10,tensorflow1,name,queue,session,time,tf,多线程
From: https://blog.51cto.com/u_15642578/5857905

相关文章

  • 多线程
    程序同时执行多个任务使用线程可以把占据长时间的程序中的任务放到后台去处理。程序的运行速度可能加快一、线程实现方法  线程是CPU分配资源的基本单位。当一......
  • tensorflow1.x——如何在python多线程中调用同一个session会话
    如何在python多线程中调用同一个session会话? 这个问题源于我在看的一个强化学习代码:​​https://gitee.com/devilmaycry812839668/scalable_agent​​ 在众多的机器学习的......
  • C#多线程(一)线程基础篇
    C#多线程(一)线程基础篇 线程基础视频已经发布到B站参考文章:《ThreadinginC#》(JosephAlbahari)https://www.albahari.com/threading/《ThreadinginC#》中文翻......
  • 多线程下带事务的删除大量数据引起的锁等待超时
    @Override@Transactional(readOnly=false,propagation=Propagation.REQUIRES_NEW)//我建议不要用这个注解,他是声明式事务粒度过大,建议用使用编程式事务,可控性......
  • 异步和多线程有什么区别
    一、异步和多线程有什么区别?其实,异步是目的,而多线程是实现这个目的的方法。 多线程和异步操作两者都可以达到避免调用线程阻塞的目的,从而提高软件的可响应性。甚至......
  • C#多线程之高级篇(上)
    前言抛开死锁不谈,只聊性能问题,尽管锁总能粗暴的满足同步需求,但一旦存在竞争关系,意味着一定会有线程被阻塞,竞争越激烈,被阻塞的线程越多,上下文切换次数越多,调度成本越大,显然......
  • 多线程等待
    多线程等待温故而知新,好久没有用到,突然忘记 方法一CountDownEvent类usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usi......
  • 什么是Session;request.getSession().setAttribute()
    Session:会话控制,是服务器为了保存用户状态而创建的一个特殊的对象。=>用于存储信息的一个对象   当我们在服务端使用session时,首先要获取session,下面这个图就对服务......
  • python 多进程 多线程 协程
    多进程-进程池1fromconcurrent.futuresimportProcessPoolExecutor23withProcessPoolExecutor(max_workers=10)asexecutor:4results=executor.map......
  • 会话保持 Session和cookie
      Session是什么?      Session在网络中称为会话控制,是服务器为了保护用户状态而创建的一个特殊的对象,简而言之,session就是一个对象,用于存储信息。 Sess......