Skip to content

[WIP] Experiment with mlir return format and pointwise conv #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions benchmarks/DeepLearning/Ops/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
add_subdirectory(DepthwiseConv2DNhwcHwcOp)
add_subdirectory(Conv2DNhwcHwcfOp)
add_subdirectory(Conv2DNchwFchwOp)
add_subdirectory(PointwiseConv2DNhwcHwcfOp)
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
set(BUDDY_OPT_ATTR avx512f)
set(LLVM_MLIR_BINARY_DIR ${BUDDY_OPT_BUILD_DIR}/../llvm/build/bin)

add_custom_command(OUTPUT pointwise-conv-2d-nhwc-hwcf.o
COMMAND ${LLVM_MLIR_BINARY_DIR}/mlir-opt ${BUDDY_SOURCE_DIR}/benchmarks/DeepLearning/Ops/PointwiseConv2DNhwcHwcfOp/PointwiseConv2DNhwcHwcf.mlir
-linalg-bufferize
-std-bufferize
-tensor-constant-bufferize
-tensor-bufferize
-func-bufferize
-finalizing-bufferize
-buffer-deallocation
-convert-linalg-to-loops
-convert-scf-to-std
-convert-linalg-to-llvm
-lower-affine
--convert-memref-to-llvm
-convert-std-to-llvm='emit-c-wrappers=1'
-reconcile-unrealized-casts|
${LLVM_MLIR_BINARY_DIR}/mlir-translate --mlir-to-llvmir |
${LLVM_MLIR_BINARY_DIR}/llc
-mtriple=x86_64-unknown-linux-gnu
-mattr=${BUDDY_OPT_ATTR}
--filetype=obj
-o ${BUDDY_BINARY_DIR}/../benchmarks/DeepLearning/Ops/PointwiseConv2DNhwcHwcfOp/pointwise-conv-2d-nhwc-hwcf.o
)

add_library(PointwiseConv2DNhwcHwcf pointwise-conv-2d-nhwc-hwcf.o)

set_target_properties(PointwiseConv2DNhwcHwcf PROPERTIES LINKER_LANGUAGE CXX)

add_executable(pointwise-conv-2d-nhwc-hwcf-benchmark Main.cpp MLIROptBenchmark.cpp)

target_link_libraries(pointwise-conv-2d-nhwc-hwcf-benchmark
GoogleBenchmark
PointwiseConv2DNhwcHwcf
Container
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//===- MLIROptBenchmark.cpp -----------------------------------------------===//
//
// 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.
//
//===----------------------------------------------------------------------===//
//
// This file implements the benchmark for pointwise conv2d(nhwc-hwcf) operation.
//
//===----------------------------------------------------------------------===//

#include "Utils/Container.h"
#include <benchmark/benchmark.h>

// kNanosecond, kMicrosecond, kMillisecond, kSecond.
#define UNIT benchmark::kMillisecond
#define ITERATION 100

namespace {

// Declare the mobilenet C interface.
extern "C" void
_mlir_ciface_pointwise_conv_2d_nhwc_hwcf(const MemRef<float, 4> *input,
const MemRef<float, 4> *filter,
MemRef<float, 4> *output);
extern "C" MemRef<float, 4>
_mlir_ciface_pointwise_conv_2d_nhwc_hwcf_with_return(
const MemRef<float, 4> *input, const MemRef<float, 4> *filter);
extern "C" MemRef<float, 4>
_mlir_ciface_pointwise_conv_2d_nhwc_hwcf_with_return_origin(
const MemRef<float, 4> *input, const MemRef<float, 4> *filter);

intptr_t sizesInput[4] = {1, 4, 5, 2};
intptr_t sizesFilter[4] = {1, 1, 2, 7};
intptr_t sizesOutput[4] = {1, 4, 5, 7};

// Create input, filter, and output.
MemRef<float, 4> inputMemRef(sizesInput, 2);
MemRef<float, 4> filterMemRef(sizesFilter, 3);

MemRef<float, 4> inputMemReturn(sizesInput, 2);
MemRef<float, 4> filterMemReturn(sizesFilter, 3);

MemRef<float, 4> outputMemRef(sizesOutput, 0);
// Define benchmark function.void
void BM_PointwiseConv2DNhwcHwcf(benchmark::State &state) {
for (auto _ : state) {
for (int i = 0; i < state.range(0); ++i) {
// MemRef<float, 4> outputMemRef(sizesOutput, 0);
_mlir_ciface_pointwise_conv_2d_nhwc_hwcf(&inputMemRef, &filterMemRef,
&outputMemRef);
}
}
}

MemRef<float, 4> outputMemReturn(sizesOutput, 0);
void BM_PointwiseConv2DNhwcHwcfReturn(benchmark::State &state) {
for (auto _ : state) {
for (int i = 0; i < state.range(0); ++i) {
// MemRef<float, 4> outputMemReturn(sizesOutput, 0);
outputMemReturn = _mlir_ciface_pointwise_conv_2d_nhwc_hwcf_with_return(
&inputMemReturn, &filterMemReturn);
}
}
}

MemRef<float, 4> outputMemReturnOrigin(sizesOutput, 0);
void BM_PointwiseConv2DNhwcHwcfReturnOrigin(benchmark::State &state) {
for (auto _ : state) {
for (int i = 0; i < state.range(0); ++i) {
// MemRef<float, 4> outputMemReturn(sizesOutput, 0);
outputMemReturnOrigin =
_mlir_ciface_pointwise_conv_2d_nhwc_hwcf_with_return_origin(
&inputMemReturn, &filterMemReturn);
}
}
}
// Register benchmarking function with different arguments.
BENCHMARK(BM_PointwiseConv2DNhwcHwcf)->Arg(ITERATION)->Unit(UNIT);
BENCHMARK(BM_PointwiseConv2DNhwcHwcfReturn)->Arg(ITERATION)->Unit(UNIT);
BENCHMARK(BM_PointwiseConv2DNhwcHwcfReturnOrigin)->Arg(ITERATION)->Unit(UNIT);
} // namespace

// Print result function.
void printResult() {
// Clear the output memref.
MemRef<float, 4> outputMemRef(sizesOutput, 0);
// Run the mlir function.
_mlir_ciface_pointwise_conv_2d_nhwc_hwcf(&inputMemRef, &filterMemRef,
&outputMemRef);

std::cout << "inputMemRef: " << inputMemRef << std::endl;
std::cout << "filterMemRef: " << filterMemRef << std::endl;
std::cout << "outputMemRef: " << outputMemRef << std::endl;

MemRef<float, 4> outputMemReturn(sizesOutput, 0);
// Run the mlir function.
outputMemReturn = _mlir_ciface_pointwise_conv_2d_nhwc_hwcf_with_return(
&inputMemReturn, &filterMemReturn);

std::cout << "inputMemReturn: " << inputMemReturn << std::endl;
std::cout << "filterMemReturn: " << filterMemReturn << std::endl;
std::cout << "outputMemReturn: " << outputMemReturn << std::endl;

MemRef<float, 4> outputMemReturnOrigin(sizesOutput, 0);
// Run the mlir function.
outputMemReturnOrigin = _mlir_ciface_pointwise_conv_2d_nhwc_hwcf_with_return(
&inputMemReturn, &filterMemReturn);
std::cout << "inputMemReturn: " << inputMemReturn << std::endl;
std::cout << "filterMemReturn: " << filterMemReturn << std::endl;
std::cout << "outputMemReturnOrigin: " << outputMemReturnOrigin << std::endl;
}
33 changes: 33 additions & 0 deletions benchmarks/DeepLearning/Ops/PointwiseConv2DNhwcHwcfOp/Main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//===- Main.cpp -----------------------------------------------------------===//
//
// 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.
//
//===----------------------------------------------------------------------===//
//
// This is the main file of the pointwise conv2d(nhwc_hwc) benchmark.
//
//===----------------------------------------------------------------------===//

#include <benchmark/benchmark.h>

void printResult();

int main(int argc, char **argv) {
// Run benchmarks.
::benchmark::Initialize(&argc, argv);
::benchmark::RunSpecifiedBenchmarks();
// Print result.
printResult();

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Generated from Mobilenet.mlir file
func @pointwise_conv_2d_nhwc_hwcf_with_return_origin(%input: tensor<1x4x5x2xf32>, %filter: tensor<1x1x2x7xf32>) -> tensor<1x4x5x7xf32> {
%0 = linalg.init_tensor [1, 4, 5, 7] : tensor<1x4x5x7xf32>
%1 = linalg.conv_2d_nhwc_hwcf {
dilations = dense<1> : tensor<2xi64>,
strides = dense<1> : tensor<2xi64>
} ins(%input, %filter : tensor<1x4x5x2xf32>, tensor<1x1x2x7xf32>) outs(%0 : tensor<1x4x5x7xf32>) -> tensor<1x4x5x7xf32>
return %1 : tensor<1x4x5x7xf32>
}

func @pointwise_conv_2d_nhwc_hwcf_with_return(%arg0: tensor<1x4x5x2xf32>, %arg1: tensor<1x1x2x7xf32>) -> tensor<1x4x5x7xf32> {
%0 = linalg.init_tensor [1, 4, 5, 7] : tensor<1x4x5x7xf32>
%1 = tensor.collapse_shape %arg0 [[0, 1, 2], [3]] : tensor<1x4x5x2xf32> into tensor<20x2xf32>
%2 = tensor.collapse_shape %arg1 [[0, 1, 2], [3]] : tensor<1x1x2x7xf32> into tensor<2x7xf32>
%3 = tensor.collapse_shape %0 [[0, 1, 2], [3]] : tensor<1x4x5x7xf32> into tensor<20x7xf32>
%4 = linalg.matmul ins(%1, %2 : tensor<20x2xf32>, tensor<2x7xf32>) outs(%3 : tensor<20x7xf32>) -> tensor<20x7xf32>
%5 = tensor.expand_shape %4 [[0, 1, 2], [3]] : tensor<20x7xf32> into tensor<1x4x5x7xf32>
return %5 : tensor<1x4x5x7xf32>
}

// generate from iree processed mobilenet mlir file
func @pointwise_conv_2d_nhwc_hwcf(%input: memref<?x?x?x?xf32>, %filter: memref<1x1x?x?xf32>, %output: memref<?x?x?x?xf32>) {
linalg.conv_2d_nhwc_hwcf
{dilations = dense<1> : tensor<2xi64>, strides = dense<1> : tensor<2xi64>}
ins(%input, %filter : memref<?x?x?x?xf32>, memref<1x1x?x?xf32>)
outs(%output : memref<?x?x?x?xf32>)
return
}

// test for specific shape
func @pointwise_conv_2d_nhwc_hwcf_spec(%input: memref<1x4x5x2xf32>, %filter: memref<1x1x2x7xf32>, %output: memref<1x4x5x7xf32>) {
linalg.conv_2d_nhwc_hwcf
{dilations = dense<1> : tensor<2xi64>, strides = dense<1> : tensor<2xi64>}
ins(%input, %filter : memref<1x4x5x2xf32>, memref<1x1x2x7xf32>)
outs(%output : memref<1x4x5x7xf32>)
return
}
24 changes: 19 additions & 5 deletions include/Utils/Container.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#ifndef UTILS_CONTAINER
#define UTILS_CONTAINER

#include <iostream>
#include <memory>
#include <opencv2/opencv.hpp>
#include <stdint.h>
Expand All @@ -46,6 +47,19 @@ template <typename T, size_t N> class MemRef {
// Constructor from a vector of png images.
// Assume that all the images have the same shape.
MemRef(const std::vector<PNGImage> &imgs, intptr_t sizes[N]);

// Copy assignment and copy constructor
MemRef &operator=(const MemRef &rhs) = delete;
MemRef(const MemRef &) = delete;

// Move Assignment and Move Constructor NumberArrays
MemRef &operator=(MemRef &&rhs) noexcept;
MemRef(MemRef &&) noexcept;

// String overloading
template <typename U, std::size_t M>
friend std::ostream &operator<<(std::ostream &os, const MemRef<U, M> &memref);

// Desctrutor.
~MemRef();
// Permute the dimensions.
Expand Down Expand Up @@ -74,16 +88,16 @@ template <typename T, size_t N> class MemRef {
size_t product(intptr_t sizes[N]) const;

// Data.
T *allocated;
T *aligned;
T *allocated{nullptr};
T *aligned{nullptr};
// Offset.
intptr_t offset = 0;
// Shape.
intptr_t sizes[N];
intptr_t sizes[N]{};
// Strides.
intptr_t strides[N];
intptr_t strides[N]{};
// Number of elements.
size_t size;
size_t size{};
};

#include "Utils/Container.cpp"
Expand Down
40 changes: 39 additions & 1 deletion lib/Utils/Container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ MemRef<T, N>::MemRef(intptr_t sizes[N], T init) {
}
setStrides();
size = product(sizes);
T *data = new T[size];
T* data = new T[size]{};
aligned = data;
allocated = data;
std::fill(data, data + size, init);
Expand Down Expand Up @@ -187,6 +187,44 @@ MemRef<T, N>::MemRef(const std::vector<PNGImage> &imgs, intptr_t sizes[N]) {
allocated = data;
}

// Move Assignment and Move Constructor
template <typename T, std::size_t N>
MemRef<T, N>::MemRef(MemRef &&other) noexcept
: size(std::move(other.size)), sizes{}, strides{},
allocated(other.allocated), aligned(other.allocated) {
std::swap(sizes, other.sizes);
std::swap(strides, other.strides);
other.allocated = other.aligned = nullptr;
}

template <typename T, std::size_t N>
MemRef<T, N> &MemRef<T, N>::operator=(MemRef<T, N> &&rhs) noexcept {
if (this != &rhs) {
std::swap(strides, rhs.strides);
std::swap(offset, rhs.offset);
std::swap(sizes, rhs.sizes);
std::swap(size, rhs.size);
std::swap(allocated, rhs.allocated);
std::swap(aligned, rhs.aligned);
rhs.allocated = rhs.aligned = nullptr;
}
return *this;
}

template <typename T, std::size_t N>
std::ostream &operator<<(std::ostream &os, const MemRef<T, N> &memref) {
os << "[ ";
size_t size =
std::accumulate(memref.sizes, memref.sizes + N, 1, std::multiplies<T>());
for (int i = 0; i < size; ++i)
os << memref.allocated[i] << " ";
os << "] of shape: [ ";
for (auto s : memref.sizes)
os << s << " ";
os << "]";
return os;
}

template <typename T, std::size_t N> MemRef<T, N>::~MemRef() {
delete[] allocated;
}
Expand Down