Add an internal GPU-compatible version of FIFOQueue.
Just registers a FIFOQueueV2 GPU kernel with the queue resource in host memory, then makes sure the public FIFOQueue requests a CPU placement. Internal for now because adding EnqueueMany and DequeueMany GPU kernels is somewhat complicated, and without those placing the FIFOQueue resource on a GPU device is a nasty surprise. Eventually we may want those kernels. PiperOrigin-RevId: 288322387 Change-Id: I294b1c878b47d2374468ec8f86ce0027b4f09112
This commit is contained in:
parent
feffae326d
commit
f98b3bc701
|
@ -20,5 +20,7 @@ namespace tensorflow {
|
||||||
|
|
||||||
REGISTER_KERNEL_BUILDER(Name("FIFOQueue").Device(DEVICE_CPU), FIFOQueueOp);
|
REGISTER_KERNEL_BUILDER(Name("FIFOQueue").Device(DEVICE_CPU), FIFOQueueOp);
|
||||||
REGISTER_KERNEL_BUILDER(Name("FIFOQueueV2").Device(DEVICE_CPU), FIFOQueueOp);
|
REGISTER_KERNEL_BUILDER(Name("FIFOQueueV2").Device(DEVICE_CPU), FIFOQueueOp);
|
||||||
|
REGISTER_KERNEL_BUILDER(
|
||||||
|
Name("FIFOQueueV2").Device(DEVICE_DEFAULT).HostMemory("handle"),
|
||||||
|
FIFOQueueOp);
|
||||||
} // namespace tensorflow
|
} // namespace tensorflow
|
||||||
|
|
|
@ -52,7 +52,7 @@ class FIFOQueueTest(test.TestCase):
|
||||||
q = data_flow_ops.FIFOQueue(10, dtypes_lib.float32, name="Q")
|
q = data_flow_ops.FIFOQueue(10, dtypes_lib.float32, name="Q")
|
||||||
self.assertTrue(isinstance(q.queue_ref, ops.Tensor))
|
self.assertTrue(isinstance(q.queue_ref, ops.Tensor))
|
||||||
self.assertProtoEquals("""
|
self.assertProtoEquals("""
|
||||||
name:'Q' op:'FIFOQueueV2'
|
name:'Q' device: "/device:CPU:*" op:'FIFOQueueV2'
|
||||||
attr { key: 'component_types' value { list { type: DT_FLOAT } } }
|
attr { key: 'component_types' value { list { type: DT_FLOAT } } }
|
||||||
attr { key: 'shapes' value { list {} } }
|
attr { key: 'shapes' value { list {} } }
|
||||||
attr { key: 'capacity' value { i: 10 } }
|
attr { key: 'capacity' value { i: 10 } }
|
||||||
|
@ -68,7 +68,7 @@ class FIFOQueueTest(test.TestCase):
|
||||||
name="Q")
|
name="Q")
|
||||||
self.assertTrue(isinstance(q.queue_ref, ops.Tensor))
|
self.assertTrue(isinstance(q.queue_ref, ops.Tensor))
|
||||||
self.assertProtoEquals("""
|
self.assertProtoEquals("""
|
||||||
name:'Q' op:'FIFOQueueV2'
|
name:'Q' device: "/device:CPU:*" op:'FIFOQueueV2'
|
||||||
attr { key: 'component_types' value { list {
|
attr { key: 'component_types' value { list {
|
||||||
type: DT_INT32 type : DT_FLOAT
|
type: DT_INT32 type : DT_FLOAT
|
||||||
} } }
|
} } }
|
||||||
|
@ -87,7 +87,7 @@ class FIFOQueueTest(test.TestCase):
|
||||||
name="Q")
|
name="Q")
|
||||||
self.assertTrue(isinstance(q.queue_ref, ops.Tensor))
|
self.assertTrue(isinstance(q.queue_ref, ops.Tensor))
|
||||||
self.assertProtoEquals("""
|
self.assertProtoEquals("""
|
||||||
name:'Q' op:'FIFOQueueV2'
|
name:'Q' device: "/device:CPU:*" op:'FIFOQueueV2'
|
||||||
attr { key: 'component_types' value { list {
|
attr { key: 'component_types' value { list {
|
||||||
type: DT_INT32 type : DT_FLOAT
|
type: DT_INT32 type : DT_FLOAT
|
||||||
} } }
|
} } }
|
||||||
|
@ -399,6 +399,34 @@ class FIFOQueueTest(test.TestCase):
|
||||||
_f()
|
_f()
|
||||||
|
|
||||||
|
|
||||||
|
@test_util.run_all_in_graph_and_eager_modes
|
||||||
|
class GPUCompatibleFIFOQueueTests(test.TestCase):
|
||||||
|
|
||||||
|
def testEnqueueWithShape(self):
|
||||||
|
with test_util.use_gpu():
|
||||||
|
q = data_flow_ops.GPUCompatibleFIFOQueue(
|
||||||
|
10, dtypes_lib.float32, shapes=(3, 2))
|
||||||
|
self.evaluate(q.enqueue(([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]],)))
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
q.enqueue(([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]],))
|
||||||
|
self.assertEqual(1, self.evaluate(q.size()))
|
||||||
|
|
||||||
|
def testEnqueueDequeue(self):
|
||||||
|
with test_util.use_gpu():
|
||||||
|
q = data_flow_ops.GPUCompatibleFIFOQueue(10, dtypes_lib.float32)
|
||||||
|
elems_numpy = [10.0, 20.0, 30.0]
|
||||||
|
elems = [constant_op.constant(x) for x in elems_numpy]
|
||||||
|
|
||||||
|
for x in elems:
|
||||||
|
self.evaluate(q.enqueue((x,)))
|
||||||
|
|
||||||
|
for i in xrange(len(elems)):
|
||||||
|
dequeued_tensor = q.dequeue()
|
||||||
|
self.assertEqual(elems[0].device, dequeued_tensor.device)
|
||||||
|
vals = self.evaluate(dequeued_tensor)
|
||||||
|
self.assertEqual([elems_numpy[i]], vals)
|
||||||
|
|
||||||
|
|
||||||
@test_util.run_v1_only(
|
@test_util.run_v1_only(
|
||||||
"These tests can likely run in 2.x with some fixes, but have not been "
|
"These tests can likely run in 2.x with some fixes, but have not been "
|
||||||
"converted yet. Currently they hold on to operations and rely on "
|
"converted yet. Currently they hold on to operations and rely on "
|
||||||
|
@ -1619,7 +1647,7 @@ class FIFOQueueDictTest(test.TestCase):
|
||||||
name="Q")
|
name="Q")
|
||||||
self.assertTrue(isinstance(q.queue_ref, ops.Tensor))
|
self.assertTrue(isinstance(q.queue_ref, ops.Tensor))
|
||||||
self.assertProtoEquals("""
|
self.assertProtoEquals("""
|
||||||
name:'Q' op:'FIFOQueueV2'
|
name:'Q' device: "/device:CPU:*" op:'FIFOQueueV2'
|
||||||
attr { key: 'component_types' value { list {
|
attr { key: 'component_types' value { list {
|
||||||
type: DT_INT32 type : DT_FLOAT
|
type: DT_INT32 type : DT_FLOAT
|
||||||
} } }
|
} } }
|
||||||
|
@ -1640,7 +1668,7 @@ class FIFOQueueDictTest(test.TestCase):
|
||||||
name="Q")
|
name="Q")
|
||||||
self.assertTrue(isinstance(q.queue_ref, ops.Tensor))
|
self.assertTrue(isinstance(q.queue_ref, ops.Tensor))
|
||||||
self.assertProtoEquals("""
|
self.assertProtoEquals("""
|
||||||
name:'Q' op:'FIFOQueueV2'
|
name:'Q' device: "/device:CPU:*" op:'FIFOQueueV2'
|
||||||
attr { key: 'component_types' value { list {
|
attr { key: 'component_types' value { list {
|
||||||
type: DT_INT32 type : DT_FLOAT
|
type: DT_INT32 type : DT_FLOAT
|
||||||
} } }
|
} } }
|
||||||
|
|
|
@ -754,7 +754,7 @@ class FIFOQueue(QueueBase):
|
||||||
dtypes = _as_type_list(dtypes)
|
dtypes = _as_type_list(dtypes)
|
||||||
shapes = _as_shape_list(shapes, dtypes)
|
shapes = _as_shape_list(shapes, dtypes)
|
||||||
names = _as_name_list(names, dtypes)
|
names = _as_name_list(names, dtypes)
|
||||||
with ops.init_scope():
|
with ops.init_scope(), ops.device("CPU"):
|
||||||
queue_ref = gen_data_flow_ops.fifo_queue_v2(
|
queue_ref = gen_data_flow_ops.fifo_queue_v2(
|
||||||
component_types=dtypes,
|
component_types=dtypes,
|
||||||
shapes=shapes,
|
shapes=shapes,
|
||||||
|
@ -765,6 +765,83 @@ class FIFOQueue(QueueBase):
|
||||||
super(FIFOQueue, self).__init__(dtypes, shapes, names, queue_ref)
|
super(FIFOQueue, self).__init__(dtypes, shapes, names, queue_ref)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO(allenl): If GPU-compatible queues turn out to be useful, we should
|
||||||
|
# implement GPU kernels for EnqueueMany and DequeueMany so we can make the
|
||||||
|
# public FIFOQueue GPU-compatible and remove this internal version.
|
||||||
|
class GPUCompatibleFIFOQueue(QueueBase):
|
||||||
|
"""A queue implementation that dequeues elements in first-in first-out order.
|
||||||
|
|
||||||
|
GPUCompatibleFIFOQueue is like FIFOQueue, but the queue resource may be placed
|
||||||
|
either on a CPU or on a GPU. It is not cross-device: enqueues and dequeues
|
||||||
|
will be colocated with the queue resource. GPUCompatibleFIFOQueue only
|
||||||
|
supports enqueue and dequeue at the moment, not enqueue_many or dequeue_many.
|
||||||
|
|
||||||
|
See `tf.queue.QueueBase` for a description of the methods on this class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
capacity,
|
||||||
|
dtypes,
|
||||||
|
shapes=None,
|
||||||
|
names=None,
|
||||||
|
shared_name=None,
|
||||||
|
name="fifo_queue"):
|
||||||
|
"""Creates a queue that dequeues elements in a first-in first-out order.
|
||||||
|
|
||||||
|
A `FIFOQueue` has bounded capacity; supports multiple concurrent
|
||||||
|
producers and consumers; and provides exactly-once delivery.
|
||||||
|
|
||||||
|
A `FIFOQueue` holds a list of up to `capacity` elements. Each
|
||||||
|
element is a fixed-length tuple of tensors whose dtypes are
|
||||||
|
described by `dtypes`, and whose shapes are optionally described
|
||||||
|
by the `shapes` argument.
|
||||||
|
|
||||||
|
If the `shapes` argument is specified, each component of a queue
|
||||||
|
element must have the respective fixed shape. If it is
|
||||||
|
unspecified, different queue elements may have different shapes,
|
||||||
|
but the use of `dequeue_many` is disallowed.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
capacity: An integer. The upper bound on the number of elements
|
||||||
|
that may be stored in this queue.
|
||||||
|
dtypes: A list of `DType` objects. The length of `dtypes` must equal
|
||||||
|
the number of tensors in each queue element.
|
||||||
|
shapes: (Optional.) A list of fully-defined `TensorShape` objects
|
||||||
|
with the same length as `dtypes`, or `None`.
|
||||||
|
names: (Optional.) A list of string naming the components in the queue
|
||||||
|
with the same length as `dtypes`, or `None`. If specified the dequeue
|
||||||
|
methods return a dictionary with the names as keys.
|
||||||
|
shared_name: (Optional.) If non-empty, this queue will be shared under
|
||||||
|
the given name across multiple sessions.
|
||||||
|
name: Optional name for the queue operation.
|
||||||
|
"""
|
||||||
|
dtypes = _as_type_list(dtypes)
|
||||||
|
shapes = _as_shape_list(shapes, dtypes)
|
||||||
|
names = _as_name_list(names, dtypes)
|
||||||
|
with ops.init_scope():
|
||||||
|
queue_ref = gen_data_flow_ops.fifo_queue_v2(
|
||||||
|
component_types=dtypes,
|
||||||
|
shapes=shapes,
|
||||||
|
capacity=capacity,
|
||||||
|
shared_name=_shared_name(shared_name),
|
||||||
|
name=name)
|
||||||
|
|
||||||
|
super(GPUCompatibleFIFOQueue, self).__init__(
|
||||||
|
dtypes, shapes, names, queue_ref)
|
||||||
|
|
||||||
|
def enqueue_many(self, vals, name=None):
|
||||||
|
"""enqueue_many is not supported on GPUCompatibleFIFOQueue."""
|
||||||
|
raise NotImplementedError(
|
||||||
|
"GPUCompatibleFIFOQueue does not support enqueue_many or dequeue_many, "
|
||||||
|
"only enqueue and dequeue.")
|
||||||
|
|
||||||
|
def dequeue_many(self, n, name=None):
|
||||||
|
"""dequeue_many is not supported on GPUCompatibleFIFOQueue."""
|
||||||
|
raise NotImplementedError(
|
||||||
|
"GPUCompatibleFIFOQueue does not support enqueue_many or dequeue_many, "
|
||||||
|
"only enqueue and dequeue.")
|
||||||
|
|
||||||
|
|
||||||
@tf_export(
|
@tf_export(
|
||||||
"queue.PaddingFIFOQueue",
|
"queue.PaddingFIFOQueue",
|
||||||
v1=["queue.PaddingFIFOQueue", "io.PaddingFIFOQueue", "PaddingFIFOQueue"])
|
v1=["queue.PaddingFIFOQueue", "io.PaddingFIFOQueue", "PaddingFIFOQueue"])
|
||||||
|
|
Loading…
Reference in New Issue