diff --git a/tutorials/README.md b/tutorials/README.md new file mode 100644 index 0000000000..7bc07f9e3a --- /dev/null +++ b/tutorials/README.md @@ -0,0 +1,12 @@ +# Tutorials + +This folder contains tutorials introducing Highway. The +tutorials are in Jupyter notebooks. They can be run on +your own machine, but can also be run through a web browser +on [Google Colab](https://colab.research.google.com) or on +[MyBinder](https://mybinder.org/) or other cloud services that +allow one to execute Jupyter Notebooks. + +- Vector Add +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/google/highway/tutorials/vector_add.ipynb) +[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/google/highway/tutorials/vector_add.ipnyb/HEAD) diff --git a/tutorials/vector_add.ipynb b/tutorials/vector_add.ipynb new file mode 100644 index 0000000000..7252e48867 --- /dev/null +++ b/tutorials/vector_add.ipynb @@ -0,0 +1,328 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "Copyright 2026 Google LLC\n", + "SPDX-License-Identifier: Apache-2.0\n", + "\n", + "Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "you may not use this file except in compliance with the License.\n", + "You may obtain a copy of the License at\n", + "\n", + " http://www.apache.org/licenses/LICENSE-2.0\n", + "\n", + "Unless required by applicable law or agreed to in writing, software\n", + "distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "See the License for the specific language governing permissions and\n", + "limitations under the License" + ], + "metadata": { + "id": "YNknTdrHd024" + } + }, + { + "cell_type": "markdown", + "source": [ + "First setup an environment to enable one to learn how to use Highway." + ], + "metadata": { + "id": "Si4d423YTtoJ" + } + }, + { + "cell_type": "code", + "source": [ + "!apt update -qq;\n", + "!apt-get update -qq;\n", + "!apt update\n", + "!apt install cmake gcc g++ git libatomic1 libgtest-dev ninja-build\n", + "!apt-get upgrade" + ], + "metadata": { + "id": "mZAXwNtqT6Pu" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Now get the highway code and install Highway. This may take about 10 minutes. To not extend build time further, do not build tests and examples." + ], + "metadata": { + "id": "DvphHZwhVSA5" + } + }, + { + "cell_type": "code", + "source": [ + "#!git clone https://github.com/google/highway\n", + "%cd highway\n", + "!mkdir build\n", + "%cd build\n", + "!cmake -GNinja \\\n", + " -DHWY_ENABLE_TESTS=OFF \\\n", + " -DHWY_ENABLE_EXAMPLES=OFF \\\n", + " -DHWY_ENABLE_CONTRIB=ON \\\n", + " -DCMAKE_INSTALL_PREFIX=/content/highway_install ..\n", + "!ninja\n", + "!ninja install\n", + "%cd ..\n", + "%cd .." + ], + "metadata": { + "id": "XJGOuRrHVX61" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [], + "metadata": { + "id": "TmL-d260daxu" + } + }, + { + "cell_type": "markdown", + "source": [ + "As a first example, let us try out a program to do a vector addition." + ], + "metadata": { + "id": "I4JbTFcUeHrH" + } + }, + { + "cell_type": "code", + "source": [ + "%%writefile vector_add.cpp\n", + "#include \n", + "#include \n", + "\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "\n", + "float SumArray(const float* array, size_t count) {\n", + " float sum = 0;\n", + " for (size_t i = 0 ; i < count; i++) {\n", + " sum += array[i];\n", + " }\n", + " return sum;\n", + "}\n", + "\n", + "int main() {\n", + " const size_t count = 1025;\n", + " std::vector data(count);\n", + " std::iota(data.begin(), data.end(), 1.0f);\n", + " //start timer\n", + " auto start = std::chrono::high_resolution_clock::now();\n", + " float sum = SumArray(data.data(), count);\n", + " //stop timer and print time and sum\n", + " auto stop = std::chrono::high_resolution_clock::now();\n", + " std::chrono::duration diff = stop - start;\n", + " std::cout << \"Execution time: \" << diff.count() << \" ms\" << std::endl;\n", + " std::cout << \"Sum: \" << sum << std::endl;\n", + "\n", + " return 0;\n", + "}" + ], + "metadata": { + "id": "eEuRa_9Qe29p" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Now compile and run this program with no optimization." + ], + "metadata": { + "id": "FUsIWK5nggb8" + } + }, + { + "cell_type": "code", + "source": [ + "!g++ -std=c++17 -O0 -o vector_add.o -c vector_add.cpp\n", + "!g++ -o vectoradd vector_add.o\n", + "!./vector_add" + ], + "metadata": { + "id": "y4V99UFfgpau" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Now compile and run an optimized executable." + ], + "metadata": { + "id": "tsRbfP4Hi4SJ" + } + }, + { + "cell_type": "code", + "source": [ + "!g++ -O3 vector_add.cpp -o vector_add\n", + "!./vector_add" + ], + "metadata": { + "id": "k5DgYSprip_0" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Now use Highway to do explicit vectorization." + ], + "metadata": { + "id": "QhhDEzBsjdjj" + } + }, + { + "cell_type": "code", + "source": [ + "%%writefile vector_add_hwy.cpp\n", + "#include \n", + "#include \n", + "\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "\n", + "\n", + "// >>>> for dynamic dispatch only, skip if you want static dispatch\n", + "\n", + "// First undef to prevent error when re-included.\n", + "#undef HWY_TARGET_INCLUDE\n", + "// For dynamic dispatch, specify the name of the current file (unfortunately\n", + "// __FILE__ is not reliable) so that foreach_target.h can re-include it.\n", + "// The absolute path seems to be required\n", + "#define HWY_TARGET_INCLUDE \"/content/vector_add_hwy.cpp\"\n", + "// Generates code for each enabled target by re-including this source file.\n", + "#include \"hwy/foreach_target.h\" // IWYU pragma: keep\n", + "\n", + "// <<<< end of dynamic dispatch\n", + "//\n", + "#include \"hwy/highway.h\"\n", + "\n", + "HWY_BEFORE_NAMESPACE();\n", + "namespace hwy {\n", + "namespace HWY_NAMESPACE {\n", + "namespace hn = hwy::HWY_NAMESPACE;\n", + "\n", + "float SumArraySIMD(const float* HWY_RESTRICT array, size_t count) {\n", + " const hn::ScalableTag d;\n", + " using V = hn::Vec;\n", + " V sum = hn::Zero(d);\n", + " size_t i = 0;\n", + " const size_t N = hn::Lanes(d);\n", + " if (count >= N) {\n", + " for (; i <= count - N; i += N) {\n", + " sum = hn::Add(sum, hn::LoadU(d, array + i));\n", + " }\n", + " }\n", + " float total = hn::ReduceSum(d, sum);\n", + " // Simple scalar remainder handling\n", + " for (; i < count; ++i) {\n", + " total += array[i];\n", + " }\n", + " return total;\n", + "}\n", + "\n", + "} // namespace HWY_NAMESPACE\n", + "} // namespace hwy\n", + "HWY_AFTER_NAMESPACE();\n", + "\n", + "#if HWY_ONCE\n", + "namespace hwy {\n", + "HWY_EXPORT(SumArraySIMD);\n", + "\n", + "float CallSumArraySIMD(const float* array, size_t count) {\n", + " return HWY_DYNAMIC_DISPATCH(SumArraySIMD)(array, count);\n", + "}\n", + "\n", + "} // namespace hwy\n", + "\n", + "int main() {\n", + " const size_t count = 1025;\n", + " std::vector data(count);\n", + " std::iota(data.begin(), data.end(), 1.0f);\n", + " //start timer\n", + " auto start = std::chrono::high_resolution_clock::now();\n", + " float sum = hwy::CallSumArraySIMD(data.data(), count);\n", + " //stop timer and print time and sum\n", + " auto stop = std::chrono::high_resolution_clock::now();\n", + " std::chrono::duration diff = stop - start;\n", + " std::cout << \"Execution time: \" << diff.count() << \" ms\" << std::endl;\n", + " std::cout << \"Sum: \" << sum << std::endl;\n", + "\n", + " return 0;\n", + "}\n", + "#endif // HWY_ONCE" + ], + "metadata": { + "id": "ANEqe3CtkMTj" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Compile and run explicitly vectorized version." + ], + "metadata": { + "id": "TfsXskkcluH1" + } + }, + { + "cell_type": "code", + "source": [ + "!g++ -std=c++17 -O3 -I/content/highway_install/include -o vector_add_hwy.o -c vector_add_hwy.cpp\n", + "!g++ -o vector_add_hwy vector_add_hwy.o -L/content/highway_install/lib -lhwy_contrib -lhwy\n", + "!./vector_add_hwy" + ], + "metadata": { + "id": "_f93Lxefly6n" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Try increasing the array size, then recompile and run the examples again. For what array size is the explicitly SIMD vectorized program faster than the program with O0 optimization? What about for O3 optimization?\n", + "\n", + "Ideally, make a plot showing execution time against array size for the program with O0 optimization, O3 optimization and explicitly vectorized using Highway." + ], + "metadata": { + "id": "JSd31IBzA1L6" + } + } + ] +} \ No newline at end of file