cltorch: a Hardware-Agnostic Backend for the Torch Deep Neural Network Library, Based on OpenCL

06/15/2016 ∙ by Hugh Perkins, et al. ∙ ASAPP INC 0

This paper presents cltorch, a hardware-agnostic backend for the Torch neural network framework. cltorch enables training of deep neural networks on GPUs from diverse hardware vendors, including AMD, NVIDIA, and Intel. cltorch contains sufficient implementation to run models such as AlexNet, VGG, Overfeat, and GoogleNet. It is written using the OpenCL language, a portable compute language, governed by the Khronos Group. cltorch is the top-ranked hardware-agnostic machine learning framework on Chintala's convnet-benchmarks page. This paper presents the technical challenges encountered whilst creating the cltorch backend for Torch, and looks in detail at the challenges related to obtaining a fast hardware-agnostic implementation. The convolutional layers are identified as the key area of focus for accelerating hardware-agnostic frameworks. Possible approaches to accelerating the convolutional implementation are identified including: implementation of the convolutions using the implicitgemm or winograd algorithm, using a GEMM implementation adapted to the geometries associated with the convolutional algorithm, or using a pluggable hardware-specific convolutional implementation.

READ FULL TEXT VIEW PDF
POST COMMENT

Comments

There are no comments yet.

Authors

page 3

page 4

This week in AI

Get the week's most popular data science and artificial intelligence research sent straight to your inbox every Saturday.

1 Introduction

This work presents an OpenCL backend for the Torch network library. Torch[12]

is a deep learning library. Other commonly used deep learning libraries include: caffe

[17], MXNet[9]

, and Tensorflow

[4].

Deep learning became popular with the success of the AlexNet[20]

deep learning model on the Imagenet

[13] task. Prior to AlexNet, image recognition used hand-engineered features such as SIFT[24] and SURF[6], in order to get state of the art performance. AlexNet used deep learning to extract features automatically, via the expressive power of a hierarchy of layers. A key ingredient for this to work was the massive amount of data available in the ImageNet task, compared to earlier tasks, such as MNIST[22] and CIFAR[19]

. This allowed training complex models without overfitting. ImageNet gave sufficient training data for the gradients to propagate across all layers, despite the vanishing of the gradient signal as it traverses from the output back through the multiple layers. Given the amount of training data involved, and the length of time to train each batch in a deep network, another key ingredient for the success of AlexNet was: the computational capacity available in GPUs. GPUs provided sufficient processing power to be able to run multiple epochs on imagenet, using a deep network, in a reasonable timeframe, ie days or weeks; rather than months or years.

Recent continued developments of SOTA models on ImageNet often improve on AlexNet by adding more layers, making the network deeper. It is probably fair to say that the more processing power we can bring to bear onto neural network models, the easier it will be to create powerful models, to train them in a reasonable timeframe, and to train them on ever larger datasets. LSUN dataset

[38] for example has 20 million images, a 20-fold increase from AlexNet.

In order to obtain continued development of the computational capacity of neural network hardware, it could plausibly be useful to encourage competition between hardware vendors to as great an extent possible. For training neural networks, GPUs are typically used, but there is no particular reason why other hardware, such as Field Programmable Gate Arrays (“FPGAs”), or dedicated Massively Parallel Processors (MPPs), could not be used instead. Even within the domain of GPUs, it is probably fair to say that the vast majority of neural network training is not done on multiple hardware vendor’s hardware, but using NVIDIA CUDA hardware. One reason for this is that NVIDIA was the first vendor to make GPUs accessible for running numerical algorithms in a straightforward fashion, without needing clever hardware hacks. NVIDIA introduced the CUDA toolkit, and they released TESLA GPUs, which were dedicated hardware for running embarrassingly parallel numerical algorithms. More recently, other GPU manufacturers, such as Intel and AMD, have made their own GPUs available for numerical compute applications. However, looking at the machine learning domain, existing neural network libraries were written to use the NVIDIA CUDA toolkit, and it was not therefore possible to run them on non-CUDA hardware. Concretely it is not possible to run Caffe, Tensorflow, Torch, or MXNET on non-CUDA hardware.

In 2015, this situation started to change. Caffe now can be used on OpenCL hardware, thanks to the efforts of Gu et al [15], Tschopp[35], and Engel[14]. DeepCL[29] provides a dedicated deep learning library for OpenCL. cltorch, described in the present paper, provides a hardware-agnostic backend for the ‘Torch’ neural network library, using OpenCL. These projects facilitate vendor competition, and therefore contribute to the development of state of the art parallel compute hardware.

cltorch is built using OpenCL. OpenCL is a standard governed by the Khronos Group. It aims to make it possible to run a single numerical algorithmic implementation across multiple hardwares, without needing any changes to the implementation. Hardware vendors who are members of the OpenCL consortium include: AMD, Qualcomm, NVIDIA, Intel, and Xilinx.

OpenCL is not the only possible cross-platform approach possible. A recent alternative is AMD’s HCC, which we will touch on briefly, at the end of this paper.

2 Key differences between CUDA and OpenCL

CUDA and OpenCL are not so different, both conceptually, and in the details. However there are some key differences, that make porting challenging.

2.1 language word differences

The names of certain functions are different. For the example, to determine the id of a thread within a block of threads, on CUDA this uses properties, such as threadIdx.x. In OpenCL this uses functions such as get_local_id(0). The meanings of such functions are typically the same, or similar, and these differences can be handled in a straightforward fashion, using search and replace.

2.2 C++ templates

A more fundamental difference is the use of C++, and specifically C++ templates and functors, in CUDA. Torch CUDA framework makes extensive use of these. OpenCL 1.2 is based on C99, and does not have templating. Gu et al solved this problem by using a recent version of OpenCL, which does support C++ templates. Unfortunately, this limits the hardware that such a library can run on. For example NVIDIA hardware, and some mobile hardware, only supports OpenCL 1.1 and OpenCL 1.2. It can be argued that there is no reason to support NVIDIA hardware, because on NVIDIA hardware one can use CUDA instead. However, it seems an interesting goal to be able to run a neural network library across all commonly available GPUs, including those for which an alternative implementation already exists. This would enable a single codebase to be used across all devices, reducing redundant effort.

The C++ templates in Torch CUDA implementation cannot be trivially replaced by hand-written C99 code. For example, they are used to pre-bake geometry-specific parameters, such as loop sizes for loop unrolling. It is not tempting to write unrolled loops by hand for multiple possible geometries. The clBLAS[1] library handles this issue by using Python scripts to directly write out the generated OpenCL kernels and related C code, at compile time. This is an effective solution. It is fast at runtime, because the generated code is entirely generated at build time. However, it means that all possible geometries need to be baked in, at compile time, leading to a combinatorial explosion of possible parameter values.

Therefore, in cltorch, the kernels are generated at runtime, adapted to the exact geometries required. Runtime code generation needs a scripting language to be embedded into the network library. Lua was selected. It is lightweight (about 46KB), and easy to embed. A templating language similar to Jinja2 was used, based on a templater by Nachimwald[26]. This approach was sufficient to express the C++ CUDA templates used by CUDA torch.

2.3 Use of Thrust library

The Torch CUDA library makes extensive use of the Thrust[7] library, to handle for example sorting and reduction operations. Thrust is CUDA-specific, and therefore these methods need careful consideration. For now, this was worked around by using alternative approaches. In the case of reduction, Torch CUDA backend has internal implementations, and this was ported, and used in place of some of the calls to Thrust. Merry [25] provides performance benchmarks of several libraries that might be useful here.

2.4 cl_mem object offsets

cl_mem objects cannot themselves carry an implicit offset within the allocated buffer, unlike their CUDA float * counterparts. In Caffe library OpenCL backends, this was quite a large engineering challenge, since the float *

s are passed around inside the library, and offsets are added to these in very many places. In Torch OpenCL backend this was not an issue, since the Torch Tensor structure already incorporates a

storageOffset value.

2.5 Runtime compilation

In order to facilitate being able to run across many hardware platforms, OpenCL kernels are typically compiled on the fly, at runtime. CUDA kernels are compiled at build time, with the rest of the C and C++ code. Compiling the GPU kernels at build time might provide a very slight boost at runtime, but in practice the cost of compiling the OpenCL kernels at runtime was found to be negligible, for deep learning libraries, compared to the time spent on network training. Compiling at runtime has the advantage that one can bake in the exact geometries which are required. By comparison, many of the Torch CUDA functions, such as the ‘Apply’ method for example, bake in several hundred possible geometries, most of which will never be used. Runtime compilation makes it possible to provide some useful functionalities to the framework user, such as being able to provide custom functions, which will be compiled on the fly into GPU kernels. These will run on the GPU, at full speed. Zagoruyko has implemented similar functionality for CUDA Torch as an additional module [39], which runs the CUDA compiler at runtime.

2.6 OpenCL Kernel Debuggers

When using CUDA, there are extensive debugging tools available, to facilitate finding root cause on kernel crashes, or numerical accuracy issues. When using OpenCL on CUDA devices, no such debugging tools are available. Some vendors provide debugging tools for their specific hardware. For example AMD has created CodeXL[2]. However, no cross-platform tool is available. An alternative approach is to use a simulator. Oclgrind [30] provides OpenCL kernel debugging, using a simulated OpenCL device, loadable via ICD.

2.7 Hardware geometry differences

Each specific hardware has its own set of geometry specifications, including, but not limited to:

  • maximum workgroup size

  • warp size

  • local memory size

  • number of registers

A kernel that runs prefectly on NVIDIA hardware might fail to run altogether on eg AMD hardware. For example, the kernel might be written on the assumption that at least 512 workgroup threads are available, whereas AMD hardware typically has a maximum of 256 workgroup threads. Many kernels won’t run across multiple hardware platforms until they have been tested on each platform, and platform-specific issues addressed. It is not in general safe to assume that code that works on one hardware platform will run on all the others.

Moreover, the low-level memory management involved in writing OpenCL code contrasts with the use of caching on CPUs, where the caching is handled automatically by the processor, at runtime. It arguably might not be appropriate for GPUs to handle caching dynamically at runtime, because one of the distinguishing features of GPU cores is their simplicity. However, it does seem plausible that a language that is slightly higher level than OpenCL could perhaps leave the management of the cache and the memory to an effective optimizing compiler to handle? This would free the developer to focus on writing down the algorithms in programmatic form, rather than considering different possible permutations and combinations of memory and loop structures. PENCIL [5] might be one approach here.

2.8 Compiler differences

Each hardware vendor’s compiler emits slightly different warnings and errors. Something that builds cleanly on one vendor’s hardware might fail to build altogether on another’s. In addition, certain syntax, valid C99, entirely crashed certain compilers, a “segfault”. The result is that, in addition to hardware geometry differences, each kernel needs to be built using each supported vendor’s OpenCL compiler, in order to resolve vendor-specific build issues. It is not safe to assume that if it builds on one vendor’s OpenCL compiler, and that the kernel contains only valid C99, that other vendor’s OpenCL compilers will build the kernel successfully.

Therefore, if one wishes to support hardware from three different vendors, one needs to have access to representative hardware from each vendor.

2.9 OpenCL version

There is a compromise between targeting more recent versions of OpenCL, providing additional functionality and performance, or targeting older versions, allowing the use of more possible target platforms. cltorch targets OpenCL 1.2, which is supported by many current GPUs from Intel, NVIDIA, and AMD. Nevertheless, some mobile devices in common use continue to use OpenCL 1.1, and would therefore need special treatment in order to work with cltorch.

2.10 Kernel caching

NVIDIA GPUs cache the kernels automatically, even when building from templates, including where the kernel OpenCL code is supplied as a string, at runtime. For training a neural network model, the compilation time is negligible. However, for development purposes, being able to start the kernels quickly is convenient. Thus, there could be an opportunity for other vendors to provide similar kernel caching functionality.

3 Comparison with other frameworks

In this section, we will compare OpenCL Torch with other deep learning frameworks and backends, and specifically with:

  • CUDA Torch

  • DeepCL

  • OpenCL Caffe

3.1 CUDA Torch

CUDA Torch provides more functionality than the OpenCL Torch implementation currently. For example, the sort function is not currently implemented in OpenCL Torch.

36ptGiven constant hardware, the CUDA implementation runs faster than the OpenCL implementation. There are two principle reasons:

  • Semantically identical OpenCL kernels often run slower than their CUDA counterparts, even on the same hardware

  • there is currently no equivalent of the CUDA CUDNN[10] library available for OpenCL

Figure 1: Per-element timings as function of tensor size

With regards to the first reason, Figure 1 shows a comparison of carrying out per-element operations on a tensor in CUDA Torch vs OpenCL Torch. It shows the bandwidth for per-element operations, as a function of the number of floats per kernel launch, for CUDA and for OpenCL. These kernels are relatively simple, and the code is more or less the same, to within a search and replace eg of threadIdx.x with get_local_id(0). This experiment was performed on an NVIDIA K520 GPU, using CUDA Torch as of 21 May 2016, and OpenCL Torch as of 21 May 2016. The scale is log-log. We can see that the shape of the graph is pretty similar for both OpenCL and CUDA kernel launches. For both backends, the bandwidth is approximately constant down to tensors of floats, then reduces with tensor size, as the overhead of kernel launch and setup becomes dominant. The shape of both graphs, CUDA vs OpenCL, on a log-log scale, looks pretty similar.

Figure 2: Per-element timings for tensors of 1e7 floats

However on a linear scale, there is a noticeable difference between the performance of this kernel using CUDA and using OpenCL. Figure 2 shows the bandwidths on a linear scale for tensors of floats.
We can see that the OpenCL backend is about 33% slower than the CUDA backend, for this geometry, even though the hardware is identical, and the kernel code is approximately identical, to within a search and replace for eg threadIdx.x vs get_local_id(0). An opportunity exists to look at why this is the case, since both CUDA and OpenCL compile via the same PTX IL.

3.2 DeepCL

DeepCL[29] provides a deep learning framework dedicated to OpenCL, that runs on Windows, as well as on linux and Mac. It provides a commandline tool, and a python interface.

DeepCL does not provide as many network layer types, or as complete an implementation of each layer type, compared with OpenCL Torch, or OpenCL Caffe.

3.3 OpenCL Caffe

There are several ports of Caffe[17] to OpenCL: Gu et al[15], Engel[14], and Tschopp[35]

Caffe is quite challenging to port to OpenCL, because it is quite tightly coupled with the underlying CUDA implementation, and it is challenging to factorize this so that an OpenCL implementation will not adversely affect subsequent Caffe development. There is no easy solution for this task, so merging the OpenCL ports to Caffe core is a painstaking time-consuming task, needing careful negotiation between the various parties involved.

In terms of performance, the port by Gu et al contains a batched im2col implementation, discussed below. Tschopp’s Greentea initially used im2col. Recently, Tschopp has started to implement implicitgemm. These algorithms are discussed below.

Having discussed some alternative implementations in terms of features and functionality, let’s look now at performance.

3.4 Performance

Figure 3: Convnet-benchmarks timings

Figure 3 shows the timings for running a single forward-backward iteration through the convolutional layers of six popular convnet models. These timings are from Chintala’s convnet-benchmarks[11]. The timings shown are the forward-backward training times for a single batch. The implementations are described in Table 1.

Identifier Framework Backend Algorithm
cutorch Torch CUDA im2col
fbfft Torch CUDA fft
cudnnv4 Torch CUDA CUDNNv4
neon Nervana Neon CUDA winograd
greentea Caffe OpenCL im2col
cltorch Torch OpenCL im2col
Table 1: Convolutional Implementations

Thus, we compare between torch implementations, between OpenCL Torch and OpenCL Caffe, and with a state of the art convolutional implementation for CUDA cards neon. DeepCL is included in the per-layer timings on the convnet-benchmarks page, but is not included in the full network timings shown here, because it lacks certain key implementation details for these models. Looking at Figure 3, we can see that the OpenCL implementations are significantly slower than the CUDA implementations. cltorch is marginally faster than Tschopp’s Greentea im2col implementation. cltorch is around 3-4 times slower than the Torch cutorch implementation. neon is about 20 times faster than cltorch, on the Googlenet v1 model. Gu et al’s OpenCL Caffe is not present on the benchmarks page, because it uses AMD-specific OpenCL extensions, and therefore cannot run on the NVIDIA Titan X used here. It could be interesting to obtain benchmarks on hardware from other vendors, for example on AMD GPUs.

Let’s look at performance in more detail, and look at what options we have to improve the performance of convolutions in hardware-agnostic neural network frameworks.

4 Performance Analysis

In this section, we will analyse options for improving the performance of hardware-agnostic neural net frameworks. We will see that the convolutional layers dominate the training time. We will compare common algorithms for computing the convolution, discuss GEMM, hardware-specific implementations, and HCC.

4.1 Importance of convolution

Figure 4: VGG A per-layer timings

The training time for a convolutional network is dominated by the convolutional layers. Figure 4 shows the per-layer forward time for VGG model ‘A’, on an NVIDIA Titan X GPU, for im2col+clBLAS, im2col+cublas and cudnnv4 convolutional implementations.

Figure 5: VGG timings grouped by layer type

The vast majority of the time is going into the convolutional layers, rather than into pooling, or RELU. Figure

5 sums the timings by layer type, and confirms this analysis. It is clear that the convolutional layer is where we should be focusing our attention.

These graphs shows timings for three Torch GPU backends, described in Table 2. Looking again at Figure 5, we can see that the convolutional performance of cltorch is about three times slower than for the CUDA backends for Torch, and therefore there are opportunities to improve in this area.

Implementation Torch GPU backend
im2col+cublas cutorch (CUDA Torch)
cudnnv4 cutorch with cudnnv4 library
im2col+clBLAS cltorch (OpenCL Torch, this paper)
Table 2: Torch GPU Backends

4.2 Convolutional Algorithms

In order to go further with hardware-agnostic deep learning frameworks, it could be interesting to analyse how the CUDA learning frameworks obtain excellent performance.

48pt There are four algorithms in common use:

  • im2col

  • implicitgemm

  • fft

  • winograd

In addition, cuda-convnet[18] provides a very fast direct convolutional implementation for very specific convolutional geometries.

Let’s look at each of these algorithms in turn, starting with cuda-convnet

4.2.1 Cuda-convnet

Cuda-convnet provides a very fast convolutional implementation for very specific convolutional geometries. Unfortunately it generalizes poorly to other geometries, or hardware. For example [17] found that using im2col gave a speedup of 1.3 times on K20, compared to cuda-convnet, which is optimized for GTX580.

4.2.2 im2col

im2col [17, 8] is a very general method for implementing convolutions, off-loading the geometry-specific specializations to the underlying GPU BLAS implementation. im2col converts the convolution of a single 3d image tensor with the 4d weights tensor into a matrix multiplication of two 2-dimensional matrices. This uses more memory than direct convolution, but it generalizes well to many convolutional geometries. It can take advantage of existing optimizations in the existing SGEMM implementations. The idea for using matrix multiplications for carrying out convolutions is not new, but the observation that this produces excellent performance on GPUs, that generalizes well to many geometries, was made independently by [17] and [8].

For im2col in cltorch, the BLAS implementation is handled by clBLAS[1]. clBLAS is highly optimized for many high-performance computing workloads, but till recently had not been used for deep learning im2col workloads. The geometries produced by im2col do not fit within the regime of geometries for which clBLAS has been optimized on previously. Therefore, the SGEMM performance for these geometries is relatively less good. Gu et al[15] found that the overall convolutional speed of clBLAS for these workloads is not competitive with cublas. clBLAS works well for large, square matrices, with dimensions of around 1024 or 4096 along the side, and dimensions a multiple of 8 or 16. Gu et al showed that using batched im2col, over multiple images, puts clBLAS into a more favorable regime, giving a performance boost of around 4-5 times, on AMD hardware.

Therefore it could be interesting to incorporate this technique generally into other OpenCL backends.

In general, the performance of im2col will be strongly dependent on the performance of the underlying GEMM implementation.

4.2.3 implicitgemm

Similar to im2col, implicitgemm[10] lowers the convolutions onto matrix multiplication. Whereas im2col fully materializes the lowered matrix in off-chip memory, implicitgemm materializes the lowered matrix lazily, in on-chip memory. This reduces memory requirements, and can improve performance. implicitgemm is implemented in NVIDIA’s CUDNNv1[10].

An OpenCL implementation of implicitgemm is being developed by Tschopp [35]. This looks like a promising way forward to improve OpenCL convolutional performance.

4.2.4 Fft

fft transforms the convolutional problem from the spatial domain into the frequency domain. It is constant-time with respect to the size of the convolutional kernel. It typically requires a large amount of memory.
For models such as Alexnet, which have large kernels in the first layer, fft gives noticeable speed benefits. Specifically, in the second layer of AlexNet, fft

shows a clear benefit for the stride 1 5x5 kernels. However, recently the focus of convolutional networks has turned to smaller kernels. VGG uses 3x3 kernels. GoogleNet includes 1x1 kernels. On these models, the benefits of

fft are less clear, compared to other algorithms.

A competitive implementation of fft is fbfft[36]. In Figure 3, it can be seen that fbfft is fast on AlexNet, and on Overfeat, but offers no advantage on VGG. No timings are available for GoogleNet, which contains 1x1 convolutions, and is far from the favorable regime for fft.

As for im2col, fft needs a fast GEMM implementation. The performance of fft will be strongly dependent on the speed of the underlying GEMM.

4.2.5 Winograd

The Winograd FIR algorithms[37] are an algorithmic optimization that reduces the number of mathematical operations required for convolution. Winograd outlined the general principles in the 80s. Lavin and Gray provided a specific implementation for GPU convolution in [21], and implemented these algorithms in neon[3]. winograd is an algorithmic improvement, and therefore could be implemented also in OpenCL, plausibly providing a speedup similar to the speedup which it provides to neon. Note however that this is not the only optimization in neon. We will look later at neon’s use of SASS, including for the GEMM implementation, which provides additional speedups.

As for im2col and fft, winograd transforms the problem, but still relies on GEMM. And therefore its performance is strongly related also to the efficiency of the underlying GEMM implementation.

Having touched on neon, we should look at neon specifically, as the current state of the art for convolutional implementations, on NVIDIA CUDA hardware.

4.2.6 Neon

neon[3] uses at least the following optimizations to obtain its very fast, near-optimal convolutional performance:

  • winograd algorithm[37, 21]

  • SASS implementation

  • … including a SASS implementation of GEMM

  • fully fused kernels

As discussed, the winograd algorithm could be implemented in OpenCL, and looks a promising possible way forward to improving OpenCL convolutional performance. Note that as for fft and im2col, winograd is also dependent on an underlying GEMM implementation. Therefore, whilst writing winograd in OpenCL could plausibly produce an efficient implementation, an efficient implementation of GEMM would also be required.

The SASS implementations are not directly accessible from OpenCL, since they are not just vendor-specific, but architecture specific. SASS written for CUDA Maxwell devices is incompatible with SASS written for Kepler devices, and visa versa. For example, the SASS implementations make assumptions about the available registers. SPIR-V provides a portable IL language, which could help obtain efficient optimizations. However SPIR-V is at the level of PTX, rather than at the level of SASS. PTX is higher-level than SASS. It is device independent IL, albeit vendor-specific.

How much contribution does the SASS implementation, the custom GEMM, and the fused kernels provide? We can estimate this by running a convolution twice, on the same device, one with the Maxwell optimizations on, and one with them turned off, by modifying the code to incorrectly detect the device as Kepler. Figure

6 shows the results for this. This is for forward propagation of a batch of 128 224x224 images, using 3x3 convolutions, running on an NVIDIA Titan X. We can see that the Maxwell optimizations reduce the batch time by about 33%.

Figure 6: Neon, effect of Maxwell optimizations

Therefore, an OpenCL implementation of winograd would be at least around 33% slower than a hardware-optimized low-level implementation, such as neon. Taking into account the earlier experiments of running “Apply” in OpenCL and in CUDA, on the same device, which showed an additional performance drop of around 33%, all other things equal, we could expect an OpenCL implementation of winograd to approach around 40% of the execution speed of neon.

However, note that neon depends also on the speed of the underlying GEMM implementation, as do also im2col and fft, so we should discuss GEMM briefly.

4.3 Gemm

GEMM is at the heart of convolution. It is used by all the convolution algorithms detailed above, with the exception of the direct convolution algorithm in cuda-convnet. Thus, it is important that the GEMM implementation should be the most efficient possible. Currently, cltorch uses the clBLAS GEMM implementation. Gu et al showed that clBLAS is an effective GEMM implementation that can be competitive with the CUDA cublas implementation. Gu et al showed that it is important to ensure that the matrix sizes fall close to the optimal regime for clBLAS. Gu et al showed that by using batching, lower multiple images into matrix multiplication in a single batch, the clBLAS implementation was around 4-5 times faster, on AMD hardware.

A possible alternative to clBLAS is ViennaCL, which provides a highly hardware-agnostic implementation of GEMM, working not just on OpenCL, but also on CUDA and OpenMP.

Figure 7: Comparison of CLBlast with clBLAS, on Radeon HD7950

In terms of performance, Nugteren is developing CLBlast[27], based on CLTune auto-tuner[28]. CLBlast shows a performance benefit relative to clBLAS for matrices whose sides are not a multiple of . Specifically matrices with a side of , for integer , show a clear benefit, Figure 7, [27]. Tschopp’s implicitgemm implementation is based loosely on the CLBlast GEMM implementation, but using fully fused kernels, rather than factorizing the matrix lowering operation and the GEMM into separate kernels.

It could be interesting to benchmark OpenCL GEMM implementations, under workloads associated with the convolutional algorithms discussed above. Lokhmotov’s GEMMbench [23] could potentially be useful for this.

Going back to neon, assuming that one could obtain a GEMM implementation competitive with cublas for OpenCL, then it looks like it could be possible to write an OpenCL implementation of winograd, that could approach around 20-30% of the speed of neon (ie 70-80% slower than neon). This would be a significant improvement on the current im2col implementations. However, can we do better?

4.4 Pluggable Implementations

It seems challenging to approach the performance of close-to-the-metal SASS from within OpenCL or SPIR-V, because there is a limit to how far one can improve the performance in languages designed to be portable. To go further, two possible approaches could be:

  • implementation of highly optimizing compilers, for OpenCL, or

  • create pluggable hardware-specific convolutional implementations

The former approach, of creating highly optimizing compilers, is an active area of research, and there are no easy answers. AEcute metadata [16], and the more recent PENCIL[5] are two approaches to facilitate generation of hardware-optimized code. PENCIL allows expression of algorithms in a higher-level language, which can be used to generate device-optimized OpenCL.

Rather than attempting to make the convolutional implementations portable, an alternative approach could be to make them pluggable, loadable at runtime, and strongly hardware-specific. Thus they could be written in low-level assembler, and make full use of hardware-specific optimizations, such as knowledge of the exact memory dimensions, register layout, and other hardware characteristics The neural network library frameworks could themselves be written in a portable language, such as OpenCL.

Concretely, this could work in a similar way to the ICD abstraction layer in OpenCL. OpenCL’s ICD binds with the vendor-provided OpenCL implementation at runtime. In the case of convolution, the machine learning framework could simply call a function conv, within a Khronos-provided API. The Khronos API would route the call to a convolutional implementation appropriate to the current targeted hardware device. This might look something like Figure 8.

Figure 8: Proposal for runtime convolution linking

Note that this architecture says nothing about who will write the highly optimized hardware-specific convolutional implementation. For example we could imagine a virtual OpenCL platform, that wraps the neon implementation, for CUDA hardware, see Figure 9.

Figure 9: Proposal for virtual OpenCL platform

4.5 Hcc

Discussion of non-CUDA deep learning implementations would not be complete without touching on AMD’s HCC. HCC is an alternative open standard to OpenCL. It is not backed by Khronos, but it is an open standard, backed by one of the major competitors to NVIDIA. It is currently implemented only on AMD hardware, but there is no reason why other hardware vendors couldnt support it in the future. Since only one vendor owns the specifications currently, it can potentially evolve rapidly.

Note that HCC will not in itself directly solve the challenge of writing a fast convolutional layer. The key advantages of HCC could be agility, and compatibility with CUDA language. The AMD implementation of HCC might become faster than the AMD implementation of OpenCL. However, it seems unlikely that convolutional implementations written in pure HCC would approach the performance of Gray’s SASS implementations in neon. Therefore, the use of pluggable architectures, such as that outlined above, might be key to obtaining the fastest cross-platform convolutional performance.

HCC solves many of the challenges faced during the development of cltorch, such as the use of c++ templates in the torch CUDA code. It could therefore significantly facilitate the development of hardware- agnostic deep learning frameworks in the future. The extent to which HCC can contribute to hardware-agnostic deep learning framework will depend on the extent to which multiple hardware vendors adopt the standard.

5 Possible future evolutions

In previous sections, possible future evolutions for portable neural network compute have been described in detail. Summarizing here, several possible future evolutions could improve the performance of portable deep learning libraries:

  • OpenCL implementation of implicitgemm, for example that currently under development by Tschopp

  • OpenCL implementation of Winograd algorithms

  • Improvements to the OpenCL GEMM implementations, for example that currently under development by Nugteren

  • hardware-specific convolutional implementations, loaded at runtime via a pluggable architecture

Finally, HCC might facilitate the future development of hardware-agnostic learning frameworks, by facilitating the porting process, and reducing the disparity between CUDA and non-CUDA code-bases.

6 Conclusion

cltorch provides a hardware-agnostic backend for the Torch deep learning library. It is written using the portable language ‘OpenCL’. cltorch provides sufficient functionality to train recent models such as: AlexNet, GoogleNet, Overfeat, and VGG. Challenges faced during the creation of the cltorch backend were presented. Current OpenCL neural network framework implementations face challenges obtaining training speeds competitive with CUDA implementations. Possible approaches to improve the speed of the convolutional implementation have been presented. Finally, HCC could solve many of the challenges faced during the development and reduce the disparity between CUDA and non-CUDA codebases, but the extent to which it will become a hardware-agnostic standard, implemented by multiple vendors is as yet uncertain.

References