.. index:: pair: example; gpu_opencl_getting_started.cpp .. _doxid-gpu_opencl_getting_started_8cpp-example: gpu_opencl_getting_started.cpp ============================== This is an example to demonstrate how to build a simple graph and run on OpenCL GPU runtime. Annotated version: :ref:`Getting started with OpenCL extensions and Graph API ` This is an example to demonstrate how to build a simple graph and run on OpenCL GPU runtime. Annotated version: :ref:`Getting started with OpenCL extensions and Graph API ` .. ref-code-block:: cpp /******************************************************************************* * Copyright 2024 Intel Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ //[Headers and namespace] #include "oneapi/dnnl/dnnl_graph.hpp" #include "oneapi/dnnl/dnnl_ocl.hpp" using namespace :ref:`dnnl::graph `; #include #include #include #include #include #include #include #include "example_utils.hpp" #include "graph_example_utils.hpp" using :ref:`data_type ` = :ref:`logical_tensor::data_type `; using :ref:`layout_type ` = :ref:`logical_tensor::layout_type `; using dim = :ref:`logical_tensor::dim `; using dims = :ref:`logical_tensor::dims `; //[Headers and namespace] void ocl_getting_started_tutorial() { dim N = 8, IC = 3, OC1 = 96, OC2 = 96; dim IH = 227, IW = 227, KH1 = 11, KW1 = 11, KH2 = 1, KW2 = 1; dims conv0_input_dims {N, IC, IH, IW}; dims conv0_weight_dims {OC1, IC, KH1, KW1}; dims conv0_bias_dims {OC1}; dims conv1_weight_dims {OC1, OC2, KH2, KW2}; dims conv1_bias_dims {OC2}; //[Create conv's logical tensor] :ref:`logical_tensor ` conv0_src_desc {0, :ref:`data_type::f32 `}; :ref:`logical_tensor ` conv0_weight_desc {1, :ref:`data_type::f32 `}; :ref:`logical_tensor ` conv0_dst_desc {2, :ref:`data_type::f32 `}; //[Create conv's logical tensor] //[Create first conv] :ref:`op ` conv0(0, op::kind::Convolution, {conv0_src_desc, conv0_weight_desc}, {conv0_dst_desc}, "conv0"); conv0.:ref:`set_attr `(:ref:`op::attr::strides `, {4, 4}); conv0.:ref:`set_attr `(:ref:`op::attr::pads_begin `, {0, 0}); conv0.:ref:`set_attr `(:ref:`op::attr::pads_end `, {0, 0}); conv0.:ref:`set_attr `(:ref:`op::attr::dilations `, {1, 1}); conv0.:ref:`set_attr `(:ref:`op::attr::groups `, 1); conv0.:ref:`set_attr `(:ref:`op::attr::data_format `, "NCX"); conv0.:ref:`set_attr `(:ref:`op::attr::weights_format `, "OIX"); //[Create first conv] //[Create first bias_add] :ref:`logical_tensor ` conv0_bias_desc {3, :ref:`data_type::f32 `}; :ref:`logical_tensor ` conv0_bias_add_dst_desc { 4, :ref:`data_type::f32 `, :ref:`layout_type::undef `}; :ref:`op ` conv0_bias_add(1, op::kind::BiasAdd, {conv0_dst_desc, conv0_bias_desc}, {conv0_bias_add_dst_desc}, "conv0_bias_add"); conv0_bias_add.:ref:`set_attr `(:ref:`op::attr::data_format `, "NCX"); //[Create first bias_add] //[Create first relu] :ref:`logical_tensor ` relu0_dst_desc {5, :ref:`data_type::f32 `}; :ref:`op ` relu0(2, op::kind::ReLU, {conv0_bias_add_dst_desc}, {relu0_dst_desc}, "relu0"); //[Create first relu] //[Create second conv] :ref:`logical_tensor ` conv1_weight_desc {6, :ref:`data_type::f32 `}; :ref:`logical_tensor ` conv1_dst_desc {7, :ref:`data_type::f32 `}; :ref:`op ` conv1(3, op::kind::Convolution, {relu0_dst_desc, conv1_weight_desc}, {conv1_dst_desc}, "conv1"); conv1.:ref:`set_attr `(:ref:`op::attr::strides `, {1, 1}); conv1.:ref:`set_attr `(:ref:`op::attr::pads_begin `, {0, 0}); conv1.:ref:`set_attr `(:ref:`op::attr::pads_end `, {0, 0}); conv1.:ref:`set_attr `(:ref:`op::attr::dilations `, {1, 1}); conv1.:ref:`set_attr `(:ref:`op::attr::groups `, 1); conv1.:ref:`set_attr `(:ref:`op::attr::data_format `, "NCX"); conv1.:ref:`set_attr `(:ref:`op::attr::weights_format `, "OIX"); //[Create second conv] //[Create second bias_add] :ref:`logical_tensor ` conv1_bias_desc {8, :ref:`data_type::f32 `}; :ref:`logical_tensor ` conv1_bias_add_dst_desc {9, :ref:`data_type::f32 `}; :ref:`op ` conv1_bias_add(4, op::kind::BiasAdd, {conv1_dst_desc, conv1_bias_desc}, {conv1_bias_add_dst_desc}, "conv1_bias_add"); conv1_bias_add.:ref:`set_attr `(:ref:`op::attr::data_format `, "NCX"); //[Create second bias_add] //[Create second relu] :ref:`logical_tensor ` relu1_dst_desc {10, :ref:`data_type::f32 `}; :ref:`op ` relu1(5, op::kind::ReLU, {conv1_bias_add_dst_desc}, {relu1_dst_desc}, "relu1"); //[Create second relu] //[Create graph and add ops] :ref:`graph ` g(:ref:`engine::kind::gpu `); g.add_op(conv0); g.add_op(conv0_bias_add); g.add_op(relu0); g.add_op(conv1); g.add_op(conv1_bias_add); g.add_op(relu1); //[Create graph and add ops] //[Finalize graph] g.finalize(); //[Finalize graph] //[Get partition] auto partitions = g.get_partitions(); //[Get partition] // Check partitioning results to ensure the examples works. Users do not // need to follow this step. assert(partitions.size() == 2); // //[Create engine] :ref:`dnnl::engine ` eng(:ref:`engine::kind::gpu `, 0); //[Create engine] //[Create stream] :ref:`dnnl::stream ` strm(eng); //[Create stream] // Mapping from logical tensor id to output tensor. It's used to represent // the connection between partitions (e.g partition 0's output // tensor is fed into partition 1). std::unordered_map global_outputs_ts_map; // Memory buffers bound to the partition input/output tensors that help to // manage the lifetime of these tensors. std::vector> data_buffer; // Mapping from id to queried logical tensor from compiled partition used to // record the logical tensors that are previously enabled with ANY layout. std::unordered_map id_to_queried_logical_tensors; // This is a helper function which helps to decide which logical tensor is // needed to be set with `dnnl::graph::logical_tensor::layout_type::any` // layout. This function is not a part of Graph API, but similar logic is // essential for Graph API integration to achieve the best performance. // Typically, users need to implement the similar logic in their code. std::unordered_set ids_with_any_layout; set_any_layout(partitions, ids_with_any_layout); // Mapping from logical tensor id to the concrete shape. In practical usage, // concrete shapes and layouts are not given until compilation stage, hence // need this mapping to mock the step. std::unordered_map concrete_shapes {{0, conv0_input_dims}, {1, conv0_weight_dims}, {3, conv0_bias_dims}, {6, conv1_weight_dims}, {8, conv1_bias_dims}}; // Compile and execute the partitions, including the following steps: // // 1. Update the input/output logical tensors with concrete shape and layout // 2. Compile the partition // 3. Update the output logical tensors with queried ones after compilation // 4. Allocate memory and bind the data buffer for the partition // 5. Execute the partition // // Although they are not part of the APIs, these steps are essential for the // integration of Graph API., hence users need to implement similar logic. for (const auto &:ref:`partition ` : partitions) { if (!:ref:`partition `.:ref:`is_supported `()) { std::cout << "gpu_opencl_getting_started: Got unsupported partition, " "users " "need handle the operators by themselves." << std::endl; continue; } std::vector inputs = :ref:`partition `.:ref:`get_input_ports `(); std::vector outputs = :ref:`partition `.:ref:`get_output_ports `(); // Update input logical tensors with concrete shape and layout for (auto &input : inputs) { const auto id = input.get_id(); // If the tensor is an output of another partition, use the cached // logical tensor if (id_to_queried_logical_tensors.find(id) != id_to_queried_logical_tensors.end()) input = id_to_queried_logical_tensors[id]; else // Create logical tensor with strided layout input = :ref:`logical_tensor ` {id, input.:ref:`get_data_type `(), concrete_shapes[id], layout_type::strided}; } // Update output logical tensors with concrete shape and layout for (auto &output : outputs) { const auto id = output.get_id(); output = :ref:`logical_tensor ` {id, output.:ref:`get_data_type `(), :ref:`DNNL_GRAPH_UNKNOWN_NDIMS `, // set output dims to unknown ids_with_any_layout.count(id) ? :ref:`layout_type::any ` : layout_type::strided}; } //[Compile partition] :ref:`compiled_partition ` cp = :ref:`partition `.:ref:`compile `(inputs, outputs, eng); //[Compile partition] // Update output logical tensors with queried one for (auto &output : outputs) { const auto id = output.get_id(); output = cp.:ref:`query_logical_tensor `(id); id_to_queried_logical_tensors[id] = output; } // Allocate memory for the partition, and bind the data buffers with // input and output logical tensors std::vector inputs_ts, outputs_ts; allocate_ocl_graph_mem(inputs_ts, inputs, data_buffer, global_outputs_ts_map, eng, /*is partition input=*/true); allocate_ocl_graph_mem(outputs_ts, outputs, data_buffer, global_outputs_ts_map, eng, /*is partition input=*/false); //[Execute compiled partition] cp.:ref:`execute `(strm, inputs_ts, outputs_ts); //[Execute compiled partition] } // wait for all compiled partition's execution to finish strm.:ref:`wait `(); } int main(int argc, char **argv) { return handle_example_errors( {:ref:`engine::kind::gpu `}, ocl_getting_started_tutorial); }