From 05933a2f22c29afc8c9680c7725b8d5696b95ef9 Mon Sep 17 00:00:00 2001 From: Adrian Kummerlaender Date: Thu, 27 Jun 2019 21:10:09 +0200 Subject: Add some benchmark plots --- notebook/ldc_2d_initial_benchmark.ipynb | 287 +++++++++++++ notebook/ldc_benchmark.ipynb | 729 ++++++++++++++++++++++++-------- notebook/ldc_interactive.ipynb | 20 +- 3 files changed, 861 insertions(+), 175 deletions(-) create mode 100644 notebook/ldc_2d_initial_benchmark.ipynb diff --git a/notebook/ldc_2d_initial_benchmark.ipynb b/notebook/ldc_2d_initial_benchmark.ipynb new file mode 100644 index 0000000..59f6ffb --- /dev/null +++ b/notebook/ldc_2d_initial_benchmark.ipynb @@ -0,0 +1,287 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from lbm import Lattice, Geometry" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import symbolic.D2Q9 as D2Q9" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def cavity(geometry, x, y):\n", + " if x == 1 or y == 1 or x == geometry.size_x-2:\n", + " return 2\n", + " elif y == geometry.size_y-2:\n", + " return 3\n", + " else:\n", + " return 1" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "boundary = \"\"\"\n", + " if ( m == 2 ) {\n", + " u_0 = 0.0;\n", + " u_1 = 0.0;\n", + " }\n", + " if ( m == 3 ) {\n", + " u_0 = 0.1;\n", + " u_1 = 0.0;\n", + " }\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "\n", + "def MLUPS(cells, steps, time):\n", + " return cells * steps / time * 1e-6" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "def test(nX, nY, nSteps):\n", + " lattice = Lattice(\n", + " descriptor = D2Q9,\n", + " geometry = Geometry(nX, nY),\n", + " moments = D2Q9.moments(optimize = False),\n", + " collide = D2Q9.bgk(tau = 0.56),\n", + " boundary_src = boundary)\n", + " lattice.setup_geometry(cavity)\n", + " \n", + " start = time.time()\n", + " \n", + " for i in range(0,nSteps):\n", + " lattice.evolve()\n", + " lattice.sync()\n", + " \n", + " end = time.time()\n", + " \n", + " return MLUPS(lattice.geometry.volume, nSteps, end - start)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 32 : 15 MLUPS\n", + " 64 : 62 MLUPS\n", + " 96 : 135 MLUPS\n", + " 128 : 233 MLUPS\n", + " 160 : 376 MLUPS\n", + " 192 : 539 MLUPS\n", + " 224 : 744 MLUPS\n", + " 256 : 761 MLUPS\n", + " 288 : 763 MLUPS\n", + " 320 : 776 MLUPS\n", + " 352 : 726 MLUPS\n", + " 384 : 717 MLUPS\n", + " 416 : 750 MLUPS\n", + " 448 : 798 MLUPS\n", + " 480 : 797 MLUPS\n", + " 512 : 818 MLUPS\n", + " 544 : 802 MLUPS\n", + " 576 : 814 MLUPS\n", + " 608 : 814 MLUPS\n", + " 640 : 812 MLUPS\n", + " 672 : 815 MLUPS\n", + " 704 : 815 MLUPS\n", + " 736 : 804 MLUPS\n", + " 768 : 824 MLUPS\n", + " 800 : 728 MLUPS\n", + " 832 : 722 MLUPS\n", + " 864 : 819 MLUPS\n", + " 896 : 826 MLUPS\n", + " 928 : 828 MLUPS\n", + " 960 : 822 MLUPS\n", + " 992 : 824 MLUPS\n", + "1024 : 823 MLUPS\n", + "1056 : 822 MLUPS\n", + "1088 : 822 MLUPS\n", + "1120 : 825 MLUPS\n", + "1152 : 828 MLUPS\n", + "1184 : 821 MLUPS\n", + "1216 : 818 MLUPS\n", + "1248 : 813 MLUPS\n", + "1280 : 828 MLUPS\n", + "1312 : 824 MLUPS\n", + "1344 : 827 MLUPS\n", + "1376 : 826 MLUPS\n", + "1408 : 823 MLUPS\n", + "1440 : 826 MLUPS\n", + "1472 : 824 MLUPS\n", + "1504 : 826 MLUPS\n", + "1536 : 828 MLUPS\n", + "1568 : 823 MLUPS\n", + "1600 : 824 MLUPS\n", + "1632 : 825 MLUPS\n", + "1664 : 828 MLUPS\n", + "1696 : 827 MLUPS\n", + "1728 : 830 MLUPS\n", + "1760 : 831 MLUPS\n", + "1792 : 826 MLUPS\n", + "1824 : 828 MLUPS\n", + "1856 : 826 MLUPS\n", + "1888 : 825 MLUPS\n", + "1920 : 826 MLUPS\n", + "1952 : 826 MLUPS\n", + "1984 : 826 MLUPS\n", + "2016 : 827 MLUPS\n", + "2048 : 840 MLUPS\n", + "2080 : 829 MLUPS\n", + "2112 : 828 MLUPS\n", + "2144 : 828 MLUPS\n", + "2176 : 831 MLUPS\n", + "2208 : 826 MLUPS\n", + "2240 : 828 MLUPS\n", + "2272 : 829 MLUPS\n", + "2304 : 831 MLUPS\n", + "2336 : 828 MLUPS\n", + "2368 : 831 MLUPS\n", + "2400 : 829 MLUPS\n", + "2432 : 829 MLUPS\n", + "2464 : 829 MLUPS\n", + "2496 : 830 MLUPS\n", + "2528 : 830 MLUPS\n", + "2560 : 814 MLUPS\n", + "2592 : 827 MLUPS\n", + "2624 : 828 MLUPS\n", + "2656 : 828 MLUPS\n", + "2688 : 829 MLUPS\n", + "2720 : 828 MLUPS\n", + "2752 : 830 MLUPS\n", + "2784 : 827 MLUPS\n", + "2816 : 829 MLUPS\n", + "2848 : 826 MLUPS\n", + "2880 : 830 MLUPS\n", + "2912 : 829 MLUPS\n", + "2944 : 826 MLUPS\n", + "2976 : 828 MLUPS\n", + "3008 : 828 MLUPS\n", + "3040 : 829 MLUPS\n", + "3072 : 832 MLUPS\n", + "3104 : 826 MLUPS\n", + "3136 : 829 MLUPS\n", + "3168 : 827 MLUPS\n" + ] + } + ], + "source": [ + "results = []\n", + "for size in [ 32*i for i in range(1,100) ]:\n", + " perf = test(nX = size, nY = size, nSteps = 100)\n", + " results.append((size, perf))\n", + " print(\"%4d : %3.0f MLUPS\" % (size, perf))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib\n", + "import matplotlib.pyplot as plt\n", + "matplotlib.use('AGG')" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0, 0.5, 'MLUPS')" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.figure(figsize=(16,8))\n", + "plt.scatter(*zip(*results))\n", + "plt.title('LDC Performance for various grid sizes using a Nvidia K2200')\n", + "plt.xlabel('Size')\n", + "plt.ylabel('MLUPS')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebook/ldc_benchmark.ipynb b/notebook/ldc_benchmark.ipynb index 59f6ffb..2530f6f 100644 --- a/notebook/ldc_benchmark.ipynb +++ b/notebook/ldc_benchmark.ipynb @@ -6,7 +6,8 @@ "metadata": {}, "outputs": [], "source": [ - "from lbm import Lattice, Geometry" + "import matplotlib\n", + "import matplotlib.pyplot as plt" ] }, { @@ -15,7 +16,7 @@ "metadata": {}, "outputs": [], "source": [ - "import symbolic.D2Q9 as D2Q9" + "import numpy" ] }, { @@ -24,43 +25,51 @@ "metadata": {}, "outputs": [], "source": [ - "def cavity(geometry, x, y):\n", - " if x == 1 or y == 1 or x == geometry.size_x-2:\n", - " return 2\n", - " elif y == geometry.size_y-2:\n", - " return 3\n", - " else:\n", - " return 1" + "from result.ldc_2d_benchmark_P100 import ldc_2d_p100\n", + "from result.ldc_2d_benchmark_K2200 import ldc_2d_k2200\n", + "from result.ldc_3d_benchmark_P100 import ldc_3d_p100\n", + "from result.ldc_3d_benchmark_K2200 import ldc_3d_k2200" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "((32, (16, 1), 'single', True), [15, 16, 16, 16, 16, 16, 16, 16, 16, 16])" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "boundary = \"\"\"\n", - " if ( m == 2 ) {\n", - " u_0 = 0.0;\n", - " u_1 = 0.0;\n", - " }\n", - " if ( m == 3 ) {\n", - " u_0 = 0.1;\n", - " u_1 = 0.0;\n", - " }\n", - "\"\"\"" + "ldc_2d_k2200[0]" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "((16, (16, 1, 1), 'symbolic.D3Q19', 'single', True),\n", + " [58, 60, 60, 60, 60, 60, 60, 60, 59, 60])" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "import time\n", - "\n", - "def MLUPS(cells, steps, time):\n", - " return cells * steps / time * 1e-6" + "ldc_3d_k2200[0]" ] }, { @@ -69,176 +78,562 @@ "metadata": {}, "outputs": [], "source": [ - "def test(nX, nY, nSteps):\n", - " lattice = Lattice(\n", - " descriptor = D2Q9,\n", - " geometry = Geometry(nX, nY),\n", - " moments = D2Q9.moments(optimize = False),\n", - " collide = D2Q9.bgk(tau = 0.56),\n", - " boundary_src = boundary)\n", - " lattice.setup_geometry(cavity)\n", - " \n", - " start = time.time()\n", - " \n", - " for i in range(0,nSteps):\n", - " lattice.evolve()\n", - " lattice.sync()\n", - " \n", - " end = time.time()\n", - " \n", - " return MLUPS(lattice.geometry.volume, nSteps, end - start)" + "def descriptor_subset(data, descriptor):\n", + " return list(\n", + " map(lambda m: (m[0][0:2] + m[0][3:], m[1]),\n", + " filter(lambda m: m[0][2] == descriptor, ldc_3d_p100)))" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, + "outputs": [], + "source": [ + "ldc_3d_D3Q19_p100 = descriptor_subset(ldc_3d_p100, 'symbolic.D3Q19')\n", + "ldc_3d_D3Q27_p100 = descriptor_subset(ldc_3d_p100, 'symbolic.D3Q27')\n", + "ldc_3d_D3Q19_k2200 = descriptor_subset(ldc_3d_k2200, 'symbolic.D3Q19')\n", + "ldc_3d_D3Q27_k2200 = descriptor_subset(ldc_3d_k2200, 'symbolic.D3Q27')" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "def has(value, options):\n", + " if options == None:\n", + " return True\n", + " else:\n", + " return value in options\n", + "\n", + "def subset(data, size=None, layout=None, precision=None, optimization=None):\n", + " return list(\n", + " filter(lambda m: has(m[0][0], size) and has(m[0][1][0], layout) and has(m[0][2], precision) and has(m[0][3], optimization),\n", + " data))" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "def mlups_for_size(measurement):\n", + " return (measurement[0][0]**2, numpy.average(measurement[1]))" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "def scatter(data, **kwargs):\n", + " plt.scatter(*zip(*list(data)), **kwargs)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - " 32 : 15 MLUPS\n", - " 64 : 62 MLUPS\n", - " 96 : 135 MLUPS\n", - " 128 : 233 MLUPS\n", - " 160 : 376 MLUPS\n", - " 192 : 539 MLUPS\n", - " 224 : 744 MLUPS\n", - " 256 : 761 MLUPS\n", - " 288 : 763 MLUPS\n", - " 320 : 776 MLUPS\n", - " 352 : 726 MLUPS\n", - " 384 : 717 MLUPS\n", - " 416 : 750 MLUPS\n", - " 448 : 798 MLUPS\n", - " 480 : 797 MLUPS\n", - " 512 : 818 MLUPS\n", - " 544 : 802 MLUPS\n", - " 576 : 814 MLUPS\n", - " 608 : 814 MLUPS\n", - " 640 : 812 MLUPS\n", - " 672 : 815 MLUPS\n", - " 704 : 815 MLUPS\n", - " 736 : 804 MLUPS\n", - " 768 : 824 MLUPS\n", - " 800 : 728 MLUPS\n", - " 832 : 722 MLUPS\n", - " 864 : 819 MLUPS\n", - " 896 : 826 MLUPS\n", - " 928 : 828 MLUPS\n", - " 960 : 822 MLUPS\n", - " 992 : 824 MLUPS\n", - "1024 : 823 MLUPS\n", - "1056 : 822 MLUPS\n", - "1088 : 822 MLUPS\n", - "1120 : 825 MLUPS\n", - "1152 : 828 MLUPS\n", - "1184 : 821 MLUPS\n", - "1216 : 818 MLUPS\n", - "1248 : 813 MLUPS\n", - "1280 : 828 MLUPS\n", - "1312 : 824 MLUPS\n", - "1344 : 827 MLUPS\n", - "1376 : 826 MLUPS\n", - "1408 : 823 MLUPS\n", - "1440 : 826 MLUPS\n", - "1472 : 824 MLUPS\n", - "1504 : 826 MLUPS\n", - "1536 : 828 MLUPS\n", - "1568 : 823 MLUPS\n", - "1600 : 824 MLUPS\n", - "1632 : 825 MLUPS\n", - "1664 : 828 MLUPS\n", - "1696 : 827 MLUPS\n", - "1728 : 830 MLUPS\n", - "1760 : 831 MLUPS\n", - "1792 : 826 MLUPS\n", - "1824 : 828 MLUPS\n", - "1856 : 826 MLUPS\n", - "1888 : 825 MLUPS\n", - "1920 : 826 MLUPS\n", - "1952 : 826 MLUPS\n", - "1984 : 826 MLUPS\n", - "2016 : 827 MLUPS\n", - "2048 : 840 MLUPS\n", - "2080 : 829 MLUPS\n", - "2112 : 828 MLUPS\n", - "2144 : 828 MLUPS\n", - "2176 : 831 MLUPS\n", - "2208 : 826 MLUPS\n", - "2240 : 828 MLUPS\n", - "2272 : 829 MLUPS\n", - "2304 : 831 MLUPS\n", - "2336 : 828 MLUPS\n", - "2368 : 831 MLUPS\n", - "2400 : 829 MLUPS\n", - "2432 : 829 MLUPS\n", - "2464 : 829 MLUPS\n", - "2496 : 830 MLUPS\n", - "2528 : 830 MLUPS\n", - "2560 : 814 MLUPS\n", - "2592 : 827 MLUPS\n", - "2624 : 828 MLUPS\n", - "2656 : 828 MLUPS\n", - "2688 : 829 MLUPS\n", - "2720 : 828 MLUPS\n", - "2752 : 830 MLUPS\n", - "2784 : 827 MLUPS\n", - "2816 : 829 MLUPS\n", - "2848 : 826 MLUPS\n", - "2880 : 830 MLUPS\n", - "2912 : 829 MLUPS\n", - "2944 : 826 MLUPS\n", - "2976 : 828 MLUPS\n", - "3008 : 828 MLUPS\n", - "3040 : 829 MLUPS\n", - "3072 : 832 MLUPS\n", - "3104 : 826 MLUPS\n", - "3136 : 829 MLUPS\n", - "3168 : 827 MLUPS\n" - ] + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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ZX19ROHikIuzx/svFJ7XTcfwT4EpFJ7b1yhcGfzT+tQpFZtwul6Stk9E3EghUJywrZPj8yS72OJBCv9zfhNQQWE09BwKX7cyMAtxkpqJ4UySdkA2/TYvpfRkc9dSX9m06aUURqcOBT4C/gB8L6X0WjOWJgnDmSRJUq54zpkkSVKOGM4kSZJypKi/hLZr166pR48eTbKv1atX06FDfb9KSGoaHpfKK49N5VFzH5fTp09fllLaq671ijqc9ejRg7/85S9Nsq/y8nLKysqaZF9SfXlcKq88NpVHzX1cRsR/172W05qSJEm5YjiTJEnKEcOZJElSjhT1OWe1WbduHQsXLmTt2rU7td/OnTsze/bsndqntq1t27bsv//+tG7durlLkSSpyexy4WzhwoV06tSJHj16EBE7rd9Vq1bRqVOnndafti2lxPLly1m4cCE9e/Zs7nIkSWoyu9y05tq1a+nSpctODWZqehFBly5ddvoIqCRJebfLhTPAYLaL8HWUJJWiXTKc5dHll1/OG2+8sV3bzp8/n759+273visrK7nyyivp1asXffr04YQTTuCll14C4Oabb6ZPnz7079+fAQMGVLWXlZXRu3dvBgwYwIABAzj77LO3e/+SJKn+drlzzvLqZz/7WbPt+/LLL6dnz57MnTuXFi1a8M477zB79mxefPFFHn/8cWbMmEGbNm1YtmwZH330UdV2EydOZNCgQc1WtyRJpajkR84mv7KI437we3p+8wmO+8HvmfzKoh3qb/Xq1QwfPpzDDz+cvn378utf/xoojERt+jWDjh078u1vf5vDDz+cwYMH8/777wPw9ttvM3jwYI466iiuv/56OnbsuEX/GzZsYMyYMRx11FH079+fn/70p9us5+233+all17ipptuokWLwst90EEHMXz4cJYsWULXrl1p06YNAF27dqV79+479PglSdKOKelwNvmVRVz78KssWrGGBCxasYZrH351hwLak08+Sffu3Zk5cyavvfYaJ5988hbrrF69msGDBzNz5kxOOOEE7rnnHgBGjx7N6NGjmTZt2lZD0r333kvnzp2ZNm0a06ZN45577uHdd9/daj2vv/46AwYMoGXLllssO+mkk1iwYAGHHHIIX/7yl3nuuedqLL/wwgurpjXHjBnTkKdBkiRtp5IOZ+OemsOadRtqtK1Zt4FxT83Z7j779evHM888wze+8Q2ef/55OnfuvMU6u+22G6eeeioARx55JPPnzwfgxRdf5JxzzgHgggsuqLX/qVOn8otf/IIBAwZwzDHHsHz5cubOnbtdtXbs2JHp06dz9913s9dee3Heeedx3333VS2fOHEiFRUVVFRUMG7cuO3ahyRJapiSPuds8Yo1DWqvj0MOOYTp06fz29/+lmuvvZaTTjqJ66+/vsY6rVu3rroSsWXLlqxfv77e/aeUuPPOOxk2bFi91u/Tpw8zZ85k48aNVdOa1bVs2ZKysjLKysro168fEyZM4NJLL613PZIkaecq6ZGz7nu0a1B7fSxevJj27dvzxS9+kWuuuYYZM2bUe9vBgwfzm9/8BoBJkybVus6wYcP4yU9+wrp16wB46623WL16NQCf+tSntli/V69eDBo0iBtuuIGUEgBz587l0UcfZc6cOTVG3SoqKjjwwAPrXa8kSdr5SnrkbMyw3lz78Ks1pjbbtW7JmGG9t7vPV199lTFjxtCiRQtat27NT37yk3pve9ttt/HFL36RH//4xwwfPrzWKdHLL7+c+fPnM3DgQFJK7LXXXkyePJlly5ZVha/N/exnP+PrX/86Bx98MO3bt6dLly6MGzeOyspKvvKVr7BixQpatWrFwQcfzN1331213YUXXki7doWg2rVrV5555pkGPhuSJKmhYmsf6MVg0KBBadMVkJvMnj2bQw89tN59TH5lEeOemsPiFWvovkc7xgzrzRlH7LfFek3x800ffvgh7dq1IyKYNGkSv/rVr3j00Ufrte3jjz/OO++8w6hRoxq1xqbW0Nez1JSXl1NWVtbcZUhb8NhUHm06Luv72b+zRcT0lFKd31FV0iNnAGccsV+TvCD1MX36dEaOHElKiT322IPx48fXe9tNFxhIkqSt2/RNDZtmzTZ9UwOQmzxQ8uEsT44//nhmzpzZ3GVIkrTL2tY3NRjOJEmSmtjiFWtotfsrtNnrKaL1CtK6PfifD4axeMURzV1aFcOZJEkqGV33eZ01nR8mWhS+9SB2W0HbfR+mXfvdgOHNW1ympL9KQ5IklZY2ez9VFcw2iRbraLP3U81U0ZYcOZMkqRE115WBqt0/1n3QoPbm4MhZIxs7diw/+tGPtmvb++67j5EjR9a6rLYfRa+vdevW8c1vfpNPfvKT9O3bl6OPPprf/e53AIwfP55+/frRv39/+vbtW/VVHpdeeik9e/as+q3NY489drv3L0mlojF+w1k7Zp8O+zSovTk4claCrrvuOpYsWcJrr71GmzZteP/993nuuedYuHAhN998MzNmzKBz585UVlbywQcf/5/EuHHjOPvss5uxckkqLuOemsO6dn+hwz/VPPl83FO7OXrWTEYPHM3YP49l7Ya1VW1tW7Zl9MDRzVhVTY6czXoAbu0LY/co/DvrgR3u8uabb6Z37958/vOfZ86cj39EvaKigsGDB9O/f3/OPPNM/v73vwNQVlbGpi/TXbZsGT169KjaZsGCBZx88sn07t2b7373u7Xub9y4cRx11FH079+fG264YZu1ffjhh9xzzz3ceeedtGnTBoBu3bpx7rnnsnTpUjp16lQ1KtexY0d69uy53c+DJJW6pRv/TNt9H6bFbiuIgBbZyedLN/65uUsrWcMPGs7YY8eyb4d9CYJ9O+zL2GPHMvygfFwMAKUezmY9AFNGwcoFQCr8O2XUDgW06dOnM2nSJF555RUefvhhpk2bVrXs4osv5oc//CGzZs2iX79+Ww1b1b388stMnDiRiooKHnzwQTb/RYSpU6cyd+5cXn75ZSoqKpg+fTp//OMft9rfvHnz+Kd/+id23333LZYdfvjhdOvWjZ49e3LZZZcxZcqUGsvHjBlTNa154YUX1lm7JJW6dt2m1nryebtuU5upIkEhoE09eyqzLpnF1LOn5iqYQamHs2dvhHVraratW1No307PP/88Z555Ju3bt2f33XfntNNOA2DlypWsWLGCz372swBccskl2wxRm5x44ol06dKFdu3acdZZZ/HCCy/UWD516lSmTp3KEUccwcCBA3nzzTdr/Jh5Q7Rs2ZInn3yShx56iEMOOYSrr76asWPHVi0fN24cFRUVVFRUMHHixO3ahySVktRqRYPaJSj1c85WLmxYez1FRIPWb9WqFRs3bgRg7dq1NZZt3tfm91NKXHvttVx55ZX12tfBBx/MX//6163+VmhEcPTRR3P00Udz4oknctlll9UIaJKk+tu3wz4sWb2k1nZpa0p75Kzz/g1rr4cTTjiBRx55hDVr1rBq1aqqqcHOnTuz55578vzzzwNw//33V42i9ejRg+nTpwPw0EMP1ejv6aef5m9/+xtr1qxh8uTJHHfccTWWDxs2jPHjx1NZWQnAokWLWLp0KQBDhw5l0aKa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c6raKyoqGDx4MP379+fMM8/k73//O1AYRdr0ZbrLli2jR48eVdssWLCAk08+md69e/Pd73631v2NGzeOo446iv79+3PDDZv/lOmWevTowQ033MDAgQPp168fb775JgB/+9vfOOOMM+jfvz+DBw9m1qxZzJ8/n7vuuotbb72VAQMG8Pzzz9fo6+233+all17ipptuokWLwuF00EEHMXz4cJYsWULXrl1p06YNAF27dqV79+71fBYlbY/G+M1gSU2rpMPZyilTWHLd9axfvBhSYv3ixSy57vodCmjTp09n0qRJvPLKKzz88MNMmzatatksNS+nAAAeJklEQVTFF1/MD3/4Q2bNmkW/fv22Graqe/nll5k4cSIVFRU8+OCDbP6LCFOnTmXu3Lm8/PLLVFRUMH36dP74xz/W2W/Xrl2ZMWMG//qv/1o17XrDDTdwxBFHMGvWLL73ve9x8cUX06NHD6666iquvvpqKioqOP7442v08/rrrzNgwABatmy5xT5OOukkFixYwCGHHMKXv/xlnnvuuRrLL7zwwqppzTFjxtRZs6S6NcZvBktqWiUdzpbeehtpbc3h/7R2LUtvvW27+3z++ec588wzad++PbvvvjunnXYaACtXrmTFihV89rOfBeCSSy6pV4g68cQT6dKlC+3ateOss87ihRdeqLF86tSpTJ06lSOOOIKBAwfy5ptvMnfu3Dr7PeusswA48sgjmT9/PgAvvPACF110EQBDhgxh+fLlrFy5st6PfXMdO3Zk+vTp3H333ey1116cd9553HfffVXLN4XOiooKxo0bt937kfSxxvjNYElNq6TPOVu/ZEmD2usrIhq0fqtWrdi4cSMAazcLi5v3tfn9lBLXXnstV155ZYP2uWmqsWXLlqxfv76qr81tvr8NGzZw5JFHAnDaaadxySWXMHPmTDZu3Fg1rVldy5YtKSsro6ysjH79+jFhwgQuvfTSBtUqqf42nVdWn98MlpRPJT1y1mrffRvUXh8nnHACjzzyCGvWrGHVqlVMyaZIO3fuzJ577ll1ztb9999fNYrWo0cPpk+fDsBDDz1Uo7+nn36av/3tb6xZs4bJkydz3HHH1Vg+bNgwxo8fT2VlJQCLFi1i6dKlAAwdOpRFixY1qPaJEycChYsAunbtyu67706nTp1YtWoVUAhbm0a7brzxRnr16sWgQYO44YYb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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" } ], "source": [ - "results = []\n", - "for size in [ 32*i for i in range(1,100) ]:\n", - " perf = test(nX = size, nY = size, nSteps = 100)\n", - " results.append((size, perf))\n", - " print(\"%4d : %3.0f MLUPS\" % (size, perf))" + "plt.figure(figsize=(10,8))\n", + "plt.grid()\n", + "plt.title('Performance of D2Q9 LDC @ K2200')\n", + "plt.xscale('log')\n", + "plt.xlabel('Cells')\n", + "plt.ylabel('MLUPS')\n", + "scatter(map(mlups_for_size, subset(ldc_2d_k2200, layout=[32], precision=['single'], optimization=[True])), label='single, CSE')\n", + "scatter(map(mlups_for_size, subset(ldc_2d_k2200, layout=[32], precision=['double'], optimization=[True])), label='double, CSE')\n", + "scatter(map(mlups_for_size, subset(ldc_2d_k2200, layout=[32], precision=['single'], optimization=[False])), label='single, not-CSE')\n", + "scatter(map(mlups_for_size, subset(ldc_2d_k2200, layout=[32], precision=['double'], optimization=[False])), label='double, not-CSE')\n", + "plt.legend()" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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8MILXH/99bRqVfg12HfffRk1ahRLliyhR48etGvXDoAePXrQu3fvbfr8kiRp2xncajFl9iKuevBlFi1fQwIWLV/DVQ++vE3h7fHHH6d379689NJLvPLKKxx33HGb1Fm9ejVDhw7lpZde4phjjuGOO+4A4NJLL+XSSy9lxowZdQaou+66i65duzJjxgxmzJjBHXfcwdtvv11nf1599VVKSkpo3br1JttGjBjBggULOPDAA/n617/OM888U237OeecUzlVOmbMmIb8GCRJ0jYwuNViwhOvs2bdhmpla9ZtYMITr291mwMHDuTJJ5/kiiuu4Nlnn6Vr166b1Nlpp5044YQTADjssMN45513APj973/P6aefDsDZZ59da/vTpk3jnnvuoaSkhCOPPJIPP/yQefPmbVVfO3XqxMyZM7n99tvZbbfdOPPMM7n77rsrt0+ePJny8nLKy8uZMGHCVh1DkiQ1XNGCW0T0i4jyKl9/jYhvR8QuETE9IuZl37tn9SMibomI+RExJyIGV2lrdFZ/XkSMLlafKyxevqZB5VviwAMPZObMmQwcOJCrrrqK6667bpM6bdu2rVwx2bp1a9avX7/F7aeUuPXWWysD1dtvv82IESPqrN+/f39eeuklNm7cWOv21q1bU1payrXXXsuPf/xjfv3rX29xXyRJUnEULbillF5PKZWklEqAw4CPgYeAK4GnUkoHAE9l7wGOBw7Ivi4EbgOIiF2AscCRwBHA2IqwVyy9u3VoUPmWWLx4MR07duQrX/kKl19+ObNmzdrifYcOHVoZnO67775a64wcOZLbbruNdevWAfDGG2+wevVqAD71qU9tUn+//fZjyJAhjB07lpQSAPPmzePhhx/m9ddfrzZaV15ezt57773F/ZUkScXRWKtKhwNvppT+HBEnA6VZ+SSgDLgCOBm4JxVSxB8ioltE9MrqTk8pLQOIiOnAccAvitXZMSP7cdWDL1ebLu3QtjVjRvbb6jZffvllxowZQ6tWrWjbti233XbbFu9700038ZWvfIX/+I//YNSoUbVOs15wwQW88847DB48mJQSu+22G1OmTOGDDz6oDGY13XnnnXznO99h//33p2PHjuy6665MmDCBVatW8a1vfYvly5fTpk0b9t9/f26//fbK/c455xw6dCiE2B49evDkk0828KchSZK2RmMFt7P4e9DqmVJa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