Skip to content

Commit

Permalink
[DAP] Add IIR vectorization pass. (#241)
Browse files Browse the repository at this point in the history
  • Loading branch information
taiqzheng authored Dec 26, 2023
1 parent 55c2b70 commit 1ded040
Show file tree
Hide file tree
Showing 23 changed files with 714 additions and 31 deletions.
53 changes: 53 additions & 0 deletions docs/IIRVectorizationAlgorithm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Algorithm Explanation

This document shows the details of the algorithms used in DAPVectorization pass.

## IIR Vectorization Implementation

IIR filter can represent in different forms, typically ZPK(Zero-Pole-Gain) form or SOS(Second-Order Sections) form. Filter can be defined in ZPK form and then transformed to SOS form.

### Scalar Computation for IIR Operation

Currently, our IIR operation supports filter with SOS form. When the filter has only one set of parameters, denoted as {$𝑏_0, 𝑏_1, b_2, a_1, a_2$}, distinguishing parameters by subscripts. The equation is shown in the following form:

**IIR with one set of params:**
$$ y_n = 𝑏_0 𝑥_𝑛 + 𝑏_1 𝑥_{𝑛−1} − 𝑎_1 𝑦_{𝑛−1} + 𝑏_2 𝑥_{𝑛−2} − 𝑎_2 𝑦_{𝑛−2} $$

When the filter have multiple sets of filters, the operation use a cascade method for calculation. Take two sets of params as an example, filter parameters denoted as {$𝑏_0^0, 𝑏_1^0, b_2^0, a_1^0, a_2^0$} and {$𝑏_0^1, 𝑏_1^1, b_2^1, a_1^1, a_2^1$}, superscript indicates parameters from different sets. The process is listed below:

**IIR with two sets of params:**
$$y_n^0 = 𝑏_0^0 𝑥_𝑛^0 + 𝑏_1^0 𝑥_{𝑛−1}^0 − 𝑎_1^0 𝑦_{𝑛−1}^0 + 𝑏_2^0 𝑥_{𝑛−2}^0 − 𝑎_2^0 𝑦_{𝑛−2}^0 $$
$$x_n^1 = y_n^0$$
$$y_n^1 = 𝑏_0^1 𝑥_𝑛^1 + 𝑏_1^1 𝑥_{𝑛−1}^1 − 𝑎_1^1 𝑦_{𝑛−1}^1 + 𝑏_2^1 𝑥_{𝑛−2}^1 − 𝑎_2^0 𝑦_{𝑛−2}^1$$

### Vectorization for IIR Operation

This section shows the implementation of IIR Vectorization algorithm. The example shown below contains 4 sets of parameters, with superscript {$0, 1, 2, 3$} representing each set of parameters.

1. **Segment IIR Equation & Generate Vector Params**
![Segment IIR Equation to three parts due to different time moment](./Images/IIRSegmentation.png)
IIR equation were segmented into 3 parts, each part were calculated in different time moment. When $S2$ was calculated at time $t_i$, it will be used to calculate $S1$ at time $t_{i+1}$, then produce the final result at time $t_{i+2}$.

![Generate SOS params in vector form](./Images/IIRVectorParams.png)
In the above image, vector $B0$ were the collection of all $b_0$ params, other vectors $B1, B2, A1, A2$ each collect there corresponding params.

2. **Computing One Set of Params**
![Computing step 1](./Images/IIRComputing1.png)
The first step in computation, calculate $y_0^0$ with the following equation:
$$𝑦_0^0=𝑏_0^0𝑥_0+s_1^0$$
At time moment $0$, the initial values of $S1, S2$ were set to $0$.
![Computing step 2](./Images/IIRComputing2.png)
The second step in computation, calculate $s_1^0$ with the following equation:
$$𝑠_1^0=𝑏_1^0𝑥_0−𝑎_1^0𝑦_0^0+s_2^0 $$
![Computing step 3](./Images/IIRComputing3.png)
The third step in computation, calculate $s_2^0$ with the following equation:
$$𝑠_2^0=𝑏_2^0𝑥_0−𝑎_2^0𝑦_0^0$$

The above three steps happen in the same time moment $t$, which is the same loop iteration in program. The order of these three steps cannot change, because the value from vector $S1, S2$ were actually produced before time moment $t$.
3. **Cascade Method**
![Cascade step 1](./Images/IIRCascade1.png)
Now the values $y_0^0$, $s_1^0$ and $s_2^0$ were produced, here the whole system will get a new input $x1$ and move on the computation.
![Cascade step 2](./Images/IIRCascade2.png)
The $y_0^0$ were moved right and the new input $x1$ were pushed in. The value in vector $S1$ and $S2$ are not changed and will jump back to the second step. The difference in the next iteration is that two sets of parameters are used and this is where the performance improves.

When the example above came to the fourth iteration, the computation will be using all the parameters. This situation occurs for the vast majority of the time during the computation. Also, considering a longer vector length(currently support 4, 8, 16, 32, 64), it can achieve a 10x performance improvement.
Binary file added docs/Images/IIRCascade1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/Images/IIRCascade2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/Images/IIRComputing1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/Images/IIRComputing2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/Images/IIRComputing3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/Images/IIRSegmentation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/Images/IIRVectorParams.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 15 additions & 9 deletions examples/DAPDialect/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,34 @@ message(STATUS "Spliting size: ${SPLITING_SIZE}")
# Buddy DAP Dialect FIR operation
#-------------------------------------------------------------------------------

add_executable(firLowpass firLowpass.cpp)
add_dependencies(firLowpass buddy-opt)
target_link_libraries(firLowpass
add_executable(buddy-fir FIRLowpass.cpp)
add_dependencies(buddy-fir buddy-opt)
target_link_libraries(buddy-fir
BuddyLibDAP
)

#-------------------------------------------------------------------------------
# Buddy DAP Dialect Biquad Operation
#-------------------------------------------------------------------------------

add_executable(biquad biquad.cpp)
add_dependencies(biquad buddy-opt)
target_link_libraries(biquad
add_executable(buddy-biquad biquad.cpp)
add_dependencies(buddy-biquad buddy-opt)
target_link_libraries(buddy-biquad
BuddyLibDAP
)

#-------------------------------------------------------------------------------
# Buddy DAP Dialect IIR Operation
#-------------------------------------------------------------------------------

add_executable(iirLowpass iirLowpass.cpp)
add_dependencies(iirLowpass buddy-opt)
target_link_libraries(iirLowpass
add_executable(buddy-iir-scalar IIRLowpass.cpp)
add_dependencies(buddy-iir-scalar buddy-opt)
target_link_libraries(buddy-iir-scalar
BuddyLibDAP
)

add_executable(buddy-iir-vectorization IIRVectorization.cpp)
add_dependencies(buddy-iir-vectorization buddy-opt)
target_link_libraries(buddy-iir-vectorization
BuddyLibDAPVectorization
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===- FirLowpass.cpp - Example of DAP fir filter ----------------------===//
//===- FIRLowpass.cpp - Example of DAP FIR Filter -------------------------===//
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -38,7 +38,7 @@ int main(int argc, char *argv[]) {
if (argc == 3) {
saveFileName = argv[2];
}
cout << "Usage: FirLowpass [loadPath] [savePath]" << endl;
cout << "Usage: FIRLowpass [loadPath] [savePath]" << endl;
cout << "Current specified path: \n";
cout << "Load: " << fileName << endl;
cout << "Save: " << saveFileName << endl;
Expand All @@ -53,6 +53,6 @@ int main(int argc, char *argv[]) {
output.getAudioFile().setAudioBuffer(nullptr);
dap::fir(&aud.getMemRef(), &kernel, &output.getMemRef());
cout << "Saving file:" << endl;
cout << (output.save(saveFileName) ? "OK" : "NOT OK") << endl;
cout << (output.save(saveFileName) ? "OK" : "ERROR") << endl;
return 0;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===- iirLowpass.cpp - Example of DAP iir filter -------------------------===//
//===- IIRLowpass.cpp - Example of DAP IIR Filter -------------------------===//
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -30,21 +30,23 @@ using namespace std;

int main(int argc, char *argv[]) {
string fileName = "../../tests/Interface/core/NASA_Mars.wav";
string saveFileName = "IIR_NASA_Mars.wav";
string saveFileName = "IIR_LOWPASS_NASA_Mars.wav";
if (argc >= 2) {
fileName = argv[1];
}
if (argc == 3) {
saveFileName = argv[2];
}
cout << "Usage: FirLowpass [loadPath] [savePath]" << endl;
cout << "Usage: IIRLowpass [loadPath] [savePath]" << endl;
cout << "Current specified path: \n";
cout << "Load: " << fileName << endl;
cout << "Save: " << saveFileName << endl;
// Order of butterworth filter
int order = 8;
// Each SOS matrix has 6 paramters.
intptr_t kernelSize[2] = {int(order / 2), 6};
MemRef<float, 2> kernel(kernelSize);

// cutoff frequency = 1000, fs = 48000.
dap::iirLowpass<float, 2>(kernel, dap::butterworth<float>(order), 1000,
48000);

Expand All @@ -54,10 +56,10 @@ int main(int argc, char *argv[]) {
output.fetchMetadata(aud.getAudioFile());
output.getAudioFile().setAudioBuffer(nullptr);

dap::iir(&aud.getMemRef(), &kernel, &output.getMemRef());
dap::IIR(&aud.getMemRef(), &kernel, &output.getMemRef());

cout << "Saving file:" << endl;
cout << (output.save(saveFileName) ? "OK" : "NOT OK") << endl;
cout << (output.save(saveFileName) ? "OK" : "ERROR") << endl;

return 0;
}
66 changes: 66 additions & 0 deletions examples/DAPDialect/IIRVectorization.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//===- IIRVectorization.cpp - Example of DAP IIR Vectorization ------------===//
//
// 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 an end to end example for iir filter in buddy-mlir. It
// generates coefficients for a filter and apply it on a piece of mono audio,
// then saves the audio.
// This file will be linked with the object file which use dap vectorization
// pass to generate the executable file.
//
//===----------------------------------------------------------------------===//

#include <buddy/DAP/DAP.h>
#include <iostream>

using namespace dap;
using namespace std;

int main(int argc, char *argv[]) {
string fileName = "../../tests/Interface/core/NASA_Mars.wav";
string saveFileName = "IIR_VECTORIZATION_PASS_NASA_Mars.wav";
if (argc >= 2) {
fileName = argv[1];
}
if (argc == 3) {
saveFileName = argv[2];
}
cout << "Usage: IIRVectorizationPass [loadPath] [savePath]" << endl;
cout << "Current specified path: \n";
cout << "Load: " << fileName << endl;
cout << "Save: " << saveFileName << endl;
// Order for butterworth filter.
int order = 8;
// Each SOS matrix has 6 paramters.
intptr_t kernelSize[2] = {int(order / 2), 6};
MemRef<float, 2> kernel(kernelSize);
// cutoff frequency = 1000, fs = 48000.
dap::iirLowpass<float, 2>(kernel, dap::butterworth<float>(order), 1000,
48000);

auto aud = dap::Audio<float, 1>(fileName);
aud.getAudioFile().printSummary();
dap::Audio<float, 1> output;
output.fetchMetadata(aud.getAudioFile());
output.getAudioFile().setAudioBuffer(nullptr);

dap::IIR(&aud.getMemRef(), &kernel, &output.getMemRef(),
/*isVectorization=*/true);

cout << "Saving file:" << endl;
cout << (output.save(saveFileName) ? "OK" : "ERROR") << endl;

return 0;
}
4 changes: 2 additions & 2 deletions examples/DAPDialect/biquad.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===- biquad.cpp - Example of DAP iir filter -----------------------------===//
//===- biquad.cpp - Example of DAP Biquad Filter --------------------------===//
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -53,6 +53,6 @@ int main(int argc, char *argv[]) {
dap::biquad(&aud.getMemRef(), &kernel, &output.getMemRef());

cout << "Saving file:" << endl;
cout << (output.save(saveFileName) ? "OK" : "NOT OK") << endl;
cout << (output.save(saveFileName) ? "OK" : "ERROR") << endl;
return 0;
}
24 changes: 14 additions & 10 deletions frontend/Interfaces/buddy/DAP/DSP/IIR.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ namespace detail {
// Declare the Fir C interface.
extern "C" {
// TODO: support both float and double.
void _mlir_ciface_mlir_iir(MemRef<float, 1> *inputBuddyConv1D,
MemRef<float, 2> *kernelBuddyConv1D,
MemRef<float, 1> *outputBuddyConv1D);

void _mlir_ciface_buddy_iir(MemRef<float, 1> *inputBuddyConv1D,
MemRef<float, 2> *kernelBuddyConv1D,
MemRef<float, 1> *outputBuddyConv1D);

void _mlir_ciface_buddy_iir_vectorization(MemRef<float, 1> *inputBuddyConv1D,
MemRef<float, 2> *kernelBuddyConv1D,
MemRef<float, 1> *outputBuddyConv1D);
}
} // namespace detail

Expand All @@ -62,15 +62,19 @@ void iirLowpass(MemRef<T, N> &input, const zpk<T> &filter, T frequency, T fs) {
}
}

template <typename T, size_t N, size_t M>
void iir(MemRef<float, N> *input, MemRef<T, M> *filter,
MemRef<float, N> *output) {
// Filter parameters are represented by Second Order Section (SOS) filter, which
// accept a MemRef with 2 dimension only (with the second dimension set to 6).
template <typename T, size_t N>
void IIR(MemRef<float, N> *input, MemRef<T, 2> *filter,
MemRef<float, N> *output, bool isVectorization=false) {
if (N != 1)
assert(0 && "Only mono audio is supported for now.");
if (M != 2)
assert(0 && "Second Order Section (SOS) filter is only supported for now.");
detail::_mlir_ciface_buddy_iir(input, filter, output);
if (!isVectorization)
detail::_mlir_ciface_buddy_iir(input, filter, output);
else
detail::_mlir_ciface_buddy_iir_vectorization(input, filter, output);
}

} // namespace dap

#endif // FRONTEND_INTERFACES_BUDDY_DAP_DSP_IIR
33 changes: 33 additions & 0 deletions frontend/Interfaces/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,36 @@ SET_TARGET_PROPERTIES(BuddyLibDAP PROPERTIES
LINKER_LANGUAGE CXX
ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_DIRECTORY}
)

add_custom_command(OUTPUT DAPVectorization.o
COMMAND cat ${CMAKE_CURRENT_SOURCE_DIR}/DAP.mlir |
sed 's/buddy_fir/buddy_fir_vectorization/' |
sed 's/buddy_iir/buddy_iir_vectorization/' |
sed 's/buddy_biquad/buddy_biquad_vectorization/' |
${CMAKE_BINARY_DIR}/bin/buddy-opt
-vectorize-dap
-convert-linalg-to-affine-loops
-arith-expand
-lower-affine
-convert-scf-to-cf
-convert-math-to-llvm
-convert-vector-to-llvm
-finalize-memref-to-llvm
-llvm-request-c-wrappers
-convert-func-to-llvm
-reconcile-unrealized-casts |
${LLVM_MLIR_BINARY_DIR}/mlir-translate -mlir-to-llvmir |
${LLVM_MLIR_BINARY_DIR}/llc
-mtriple=${BUDDY_TARGET_TRIPLE}
-mattr=${BUDDY_OPT_ATTR}
-filetype=obj
-o ${CMAKE_CURRENT_BINARY_DIR}/DAPVectorization.o
DEPENDS buddy-opt
)

add_library(BuddyLibDAPVectorization STATIC DAPVectorization.o)

SET_TARGET_PROPERTIES(BuddyLibDAPVectorization PROPERTIES
LINKER_LANGUAGE CXX
ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_DIRECTORY}
)
58 changes: 58 additions & 0 deletions midend/include/Utils/DAPUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//====- DAPUtils.h --------------------------------------------------------===//
//
// 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 defines DAP dialect specific utility functions for the buddy
// compiler ecosystem.
//
//===----------------------------------------------------------------------===//

#ifndef INCLUDE_UTILS_DAPUTILS_H
#define INCLUDE_UTILS_DAPUTILS_H

#include "Utils/Utils.h"
#include <stdarg.h>

using namespace mlir;

namespace buddy {
namespace dap {

// Generate 5 vector params from SOS matrices
SmallVector<Value, 5> generateSOSParams(OpBuilder &rewriter, Location loc,
VectorType vectorTy, Value f0, Value f1,
Value c0, Value c1, Value c2, Value c4,
Value c5, Value filterSize,
Value kernel);

// Processing iir operation, result are stored in output MemRef
void biquadProcess(OpBuilder &rewriter, Location loc, VectorType vectorTy,
Value f0, Value c0, Value c1, Value cUpperBound,
Value iUpperBound, SmallVector<Value, 5> SOSParams,
ArrayRef<int64_t> arrayRef, Value N, Value input,
Value output);

// Total process for a specific vector length iir vectorization process
void iirVectorizationProcess(OpBuilder &rewriter, Location loc, uint64_t vecLen,
FloatType floatType, Value f0, Value f1, Value c0,
Value c1, Value c2, Value c4, Value c5,
Value filterSize, Value kernel,
ArrayRef<int64_t> arrayRef, Value N, Value input,
Value output);

} // namespace dap
} // namespace buddy

#endif // INCLUDE_UTILS_DAPUTILS_H
1 change: 1 addition & 0 deletions midend/lib/Conversion/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ add_subdirectory(LowerBud)
add_subdirectory(LowerDIP)
add_subdirectory(LowerRVV)
add_subdirectory(LowerDAP)
add_subdirectory(DAPVectorization)
add_subdirectory(MatMulOptimization)
add_subdirectory(TransposeOptimization)
add_subdirectory(ConvOptimization)
Expand Down
Loading

0 comments on commit 1ded040

Please sign in to comment.