Last active
August 28, 2019 12:07
-
-
Save ajtulloch/439191f566cfe433ac7e1d7cea627368 to your computer and use it in GitHub Desktop.
Block-Sparse GEMM.ipynb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"metadata": { | |
"toc": true | |
}, | |
"cell_type": "markdown", | |
"source": "<h1>Table of Contents<span class=\"tocSkip\"></span></h1>\n<div class=\"toc\"><ul class=\"toc-item\"><li><span><a href=\"#Block-Sparse-GEMM-Performance\" data-toc-modified-id=\"Block-Sparse-GEMM-Performance-1\"><span class=\"toc-item-num\">1 </span>Block-Sparse GEMM Performance</a></span></li><li><span><a href=\"#Constants\" data-toc-modified-id=\"Constants-2\"><span class=\"toc-item-num\">2 </span>Constants</a></span></li><li><span><a href=\"#Baseline\" data-toc-modified-id=\"Baseline-3\"><span class=\"toc-item-num\">3 </span>Baseline</a></span></li><li><span><a href=\"#TVM,-input-sparsity-pattern-specialized-codegen\" data-toc-modified-id=\"TVM,-input-sparsity-pattern-specialized-codegen-4\"><span class=\"toc-item-num\">4 </span>TVM, input-sparsity-pattern specialized codegen</a></span><ul class=\"toc-item\"><li><span><a href=\"#Generated-code\" data-toc-modified-id=\"Generated-code-4.1\"><span class=\"toc-item-num\">4.1 </span>Generated code</a></span></li></ul></li><li><span><a href=\"#TVM,-input-sparsity-pattern-specialized,-register-blocked\" data-toc-modified-id=\"TVM,-input-sparsity-pattern-specialized,-register-blocked-5\"><span class=\"toc-item-num\">5 </span>TVM, input-sparsity-pattern specialized, register-blocked</a></span></li><li><span><a href=\"#TVM,-non-specialized\" data-toc-modified-id=\"TVM,-non-specialized-6\"><span class=\"toc-item-num\">6 </span>TVM, non-specialized</a></span><ul class=\"toc-item\"><li><span><a href=\"#Generated-Code\" data-toc-modified-id=\"Generated-Code-6.1\"><span class=\"toc-item-num\">6.1 </span>Generated Code</a></span></li></ul></li><li><span><a href=\"#Plotting\" data-toc-modified-id=\"Plotting-7\"><span class=\"toc-item-num\">7 </span>Plotting</a></span></li><li><span><a href=\"#Experiments-with-BSR-specialization-+-loops\" data-toc-modified-id=\"Experiments-with-BSR-specialization-+-loops-8\"><span class=\"toc-item-num\">8 </span>Experiments with BSR specialization + loops</a></span><ul class=\"toc-item\"><li><span><a href=\"#Basic-implementation\" data-toc-modified-id=\"Basic-implementation-8.1\"><span class=\"toc-item-num\">8.1 </span>Basic implementation</a></span></li><li><span><a href=\"#Arbitary-register-size-nest\" data-toc-modified-id=\"Arbitary-register-size-nest-8.2\"><span class=\"toc-item-num\">8.2 </span>Arbitary register size nest</a></span></li><li><span><a href=\"#Variable-loop-only-max-iteration\" data-toc-modified-id=\"Variable-loop-only-max-iteration-8.3\"><span class=\"toc-item-num\">8.3 </span>Variable loop only max iteration</a></span></li><li><span><a href=\"#Variable-loop-only-max-iteration-tests-with-allocate\" data-toc-modified-id=\"Variable-loop-only-max-iteration-tests-with-allocate-8.4\"><span class=\"toc-item-num\">8.4 </span>Variable loop only max iteration tests with allocate</a></span></li><li><span><a href=\"#Variable-loop,-always-do-variable-loop\" data-toc-modified-id=\"Variable-loop,-always-do-variable-loop-8.5\"><span class=\"toc-item-num\">8.5 </span>Variable loop, always do variable loop</a></span></li><li><span><a href=\"#Experiments-with-optimal-partitioning\" data-toc-modified-id=\"Experiments-with-optimal-partitioning-8.6\"><span class=\"toc-item-num\">8.6 </span>Experiments with optimal partitioning</a></span></li><li><span><a href=\"#Plotting\" data-toc-modified-id=\"Plotting-8.7\"><span class=\"toc-item-num\">8.7 </span>Plotting</a></span></li></ul></li></ul></div>" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "## Block-Sparse GEMM Performance" | |
}, | |
{ | |
"metadata": { | |
"trusted": false | |
}, | |
"cell_type": "code", | |
"source": "import tvm\nimport numpy as np\nimport scipy.sparse as sp\n\nimport os\nos.environ['TVM_NUM_THREADS'] = '1'\nos.environ['MKL_NUM_THREADS'] = '1'\nos.environ['OMP_NUM_THREADS'] = '1'\n\nimport numpy\nimport ctypes\nmkl_rt = ctypes.CDLL('libmkl_rt.dylib')\nmkl_get_max_threads = mkl_rt.mkl_get_max_threads\n\n\ndef mkl_set_num_threads(cores):\n mkl_rt.mkl_set_num_threads(ctypes.byref(ctypes.c_int(cores)))\n\n\nmkl_set_num_threads(1)\n\n\ndef random_bsr_matrix(M, N, BS_R, BS_C, density, dtype):\n import itertools\n Y = np.zeros((M, N), dtype=dtype)\n assert M % BS_R == 0\n assert N % BS_C == 0\n nnz = int(density * M * N)\n num_blocks = int(nnz / (BS_R * BS_C)) + 1\n candidate_blocks = np.asarray(\n list(itertools.product(range(0, M, BS_R), range(0, N, BS_C))))\n assert candidate_blocks.shape[0] == M // BS_R * N // BS_C\n chosen_blocks = candidate_blocks[np.random.choice(\n candidate_blocks.shape[0], size=num_blocks, replace=False)]\n for i in range(len(chosen_blocks)):\n r, c = chosen_blocks[i]\n Y[r:r + BS_R, c:c + BS_C] = np.random.randn(BS_R, BS_C)\n s = sp.bsr_matrix(Y, blocksize=(BS_R, BS_C))\n assert s.data.shape == (num_blocks, BS_R, BS_C)\n assert s.indices.shape == (num_blocks, )\n assert s.indptr.shape == (M // BS_R + 1, )\n return s\n\n\ndef emit_prefetch(ib, load):\n ib.emit(\n tvm.make.Evaluate(\n tvm.make.Call(\n \"int32\",\n \"prefetch\",\n [\n tvm.call_pure_intrin(\"handle\", \"tvm_address_of\", load),\n 0, # read\n 3, # MM_HINT_T0\n 1\n ], # data cache\n tvm.expr.Call.Intrinsic,\n None,\n 0)))", | |
"execution_count": 2, | |
"outputs": [] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "## Constants" | |
}, | |
{ | |
"metadata": { | |
"trusted": false | |
}, | |
"cell_type": "code", | |
"source": "M = 1\nN = 64\nK = 64\ndensity = 0.05\nBS = 16", | |
"execution_count": 3, | |
"outputs": [] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "## Baseline" | |
}, | |
{ | |
"metadata": { | |
"trusted": false | |
}, | |
"cell_type": "code", | |
"source": "def bsr_matvec_nt(X, WS):\n N, K = WS.shape\n R, C = WS.blocksize\n\n indices = WS.indices\n indptr = WS.indptr\n data = WS.data\n\n Y = np.zeros((X.shape[0], N))\n for nb in range(0, N, R):\n for jj in range(indptr[nb // R], indptr[nb // R + 1]):\n j = indices[jj]\n block_ij = data[jj]\n\n for r in range(R):\n for c in range(C):\n Y[0, nb + r] += block_ij[r, c] * X[0, C * j + c]\n\n return Y\n\n\nW = np.random.randn(N, K)\nX = np.random.randn(1, K)\nWS = sp.bsr_matrix(W, blocksize=(BS, 1))\nnp.testing.assert_almost_equal(WS.todense(), W)\nY = X.dot(W.T)\nYS = WS.dot(X.T).T\nYZ = bsr_matvec_nt(X, WS)\n\nnp.testing.assert_almost_equal(Y, YS)\nnp.testing.assert_almost_equal(Y, YZ)", | |
"execution_count": 4, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"trusted": false | |
}, | |
"cell_type": "code", | |
"source": "# perf\nr = %timeit -q -o YS = WS.dot(X.T).T\nprint(\"N: {N}, K: {K}, BS: {BS}x1, t: {t:.3}, SparseBLAS GFLOP/s: {GFLOPs:.3}\".format(\n N=N, K=K, BS=BS, t=r.average, GFLOPs=2 * M * N * K / r.average / 10 ** 9))\nr = %timeit -q -o X.dot(W.T)\n\nprint(\"N: {N}, K: {K}, BS: {BS}x1, t: {t:.3}, BLAS GFLOP/s: {GFLOPs:.3}\".format(\n N=N, K=K, BS=BS, t=r.average, GFLOPs=2 * M * N * K / r.average / 10 ** 9))", | |
"execution_count": 4, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": "N: 64, K: 64, BS: 16x1, t: 1.55e-05, SparseBLAS GFLOP/s: 0.53\nN: 64, K: 64, BS: 16x1, t: 1.51e-06, BLAS GFLOP/s: 5.42\n" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "## TVM, input-sparsity-pattern specialized codegen" | |
}, | |
{ | |
"metadata": { | |
"scrolled": true, | |
"trusted": false | |
}, | |
"cell_type": "code", | |
"source": "def bsr_matvec_nt_codegen(X, WS, prefetch_length=None, prefetch_interval=None):\n N, K = WS.shape\n R, C = WS.blocksize\n assert C == 1\n indices = WS.indices\n indptr = WS.indptr\n data = WS.data\n import tvm\n vecty = 'float32x{R}'.format(R=R)\n\n def tvm_bsr_codegen(ins, outs):\n ib = tvm.ir_builder.create()\n load_count = 0\n\n for nb in range(0, N, R):\n acc = tvm.const(0, vecty)\n for jj in range(indptr[nb // R], indptr[nb // R + 1]):\n if prefetch_length and prefetch_interval and load_count % prefetch_interval == 0:\n jj_prefetch = jj + prefetch_length\n load = ins[1].vload([jj_prefetch, 0, 0], vecty)\n emit_prefetch(ib, load)\n\n j = indices[jj]\n x_k = ins[0].vload([0, C * j], 'float32').astype(vecty)\n w_nk_vec = ins[1].vload([jj, 0, 0], vecty)\n acc += x_k * w_nk_vec\n load_count += 1\n\n ib.emit(outs[0].vstore([0, nb], acc))\n return ib.get()\n\n Xtvm = tvm.placeholder(X.shape, name=\"X\", dtype=str(X.dtype))\n WSdatatvm = tvm.placeholder(WS.data.shape,\n name=\"WS.data\",\n dtype=str(WS.data.dtype))\n Ytvm = tvm.extern((1, N), [Xtvm, WSdatatvm], tvm_bsr_codegen)\n s = tvm.create_schedule(Ytvm.op)\n f = tvm.build(s, [Xtvm, WSdatatvm, Ytvm], \"llvm -mcpu=core-avx2\")\n Xnd = tvm.nd.array(X)\n WSdata_nd = tvm.nd.array(WS.data)\n Ynd = tvm.nd.array(np.zeros((1, N)).astype(np.float32))\n f(Xnd, WSdata_nd, Ynd)\n te = f.time_evaluator(f.entry_name,\n ctx=tvm.cpu(0),\n repeat=5,\n min_repeat_ms=5000)\n fte = lambda: te(Xnd, WSdata_nd, Ynd)\n return np.array(Ynd.asnumpy()), fte, f\n\nN = 1024\nK = 1024\nBS = 16\n\nWS = random_bsr_matrix(N, K, BS, 1, density=density, dtype=\"float32\")\nX = np.random.randn(1, K).astype(np.float32)\nYS = WS.dot(X.T).T\n\nfor prefetch_length in [8, 16, 32]:\n for prefetch_interval in [8, 16, 32]:\n YZ, fte, f = bsr_matvec_nt_codegen(X,\n WS,\n prefetch_length=prefetch_length,\n prefetch_interval=prefetch_interval)\n np.testing.assert_allclose(YS, YZ, atol=1e-5, rtol=1e-5)\n\n result = fte()\n print(\n \"N: {N}, K: {K}, BS: {BS}x1, Prefetch Length: {prefetch_length}, Prefetch Interval: {prefetch_interval}, t: {t:.3}, TVM Sparsity-Spec GFLOP/s: {GFLOPs:.3}\"\n .format(N=N,\n K=K,\n BS=BS,\n prefetch_interval=prefetch_interval,\n prefetch_length=prefetch_length,\n t=result.mean,\n GFLOPs=2 * N * K / result.mean / 10**9))", | |
"execution_count": 144, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": "N: 1024, K: 1024, BS: 16x1, Prefetch Length: 8, Prefetch Interval: 8, t: 6.77e-06, TVM Sparsity-Spec GFLOP/s: 3.1e+02\n" | |
}, | |
{ | |
"ename": "KeyboardInterrupt", | |
"evalue": "", | |
"output_type": "error", | |
"traceback": [ | |
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", | |
"\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", | |
"\u001b[0;32m<ipython-input-144-8feeafb70462>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 64\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtesting\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0massert_allclose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mYS\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mYZ\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0matol\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1e-5\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrtol\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1e-5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 65\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 66\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfte\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 67\u001b[0m print(\n\u001b[1;32m 68\u001b[0m \u001b[0;34m\"N: {N}, K: {K}, BS: {BS}x1, Prefetch Length: {prefetch_length}, Prefetch Interval: {prefetch_interval}, t: {t:.3}, TVM Sparsity-Spec GFLOP/s: {GFLOPs:.3}\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", | |
"\u001b[0;32m<ipython-input-144-8feeafb70462>\u001b[0m in \u001b[0;36m<lambda>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 45\u001b[0m \u001b[0mrepeat\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 46\u001b[0m min_repeat_ms=5000)\n\u001b[0;32m---> 47\u001b[0;31m \u001b[0mfte\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mlambda\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mte\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mXnd\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mWSdata_nd\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mYnd\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 48\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mYnd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0masnumpy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfte\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 49\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", | |
"\u001b[0;32m~/src/tvm/python/tvm/module.py\u001b[0m in \u001b[0;36mevaluator\u001b[0;34m(*args)\u001b[0m\n\u001b[1;32m 192\u001b[0m \u001b[0;34m\"\"\"Internal wrapped evaluator.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 193\u001b[0m \u001b[0;31m# Wrap feval so we can add more stats in future.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 194\u001b[0;31m \u001b[0mblob\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfeval\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 195\u001b[0m \u001b[0mfmt\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"@\"\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;34m\"d\"\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mrepeat\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 196\u001b[0m \u001b[0mresults\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mstruct\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0munpack\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfmt\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mblob\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", | |
"\u001b[0;32m~/src/tvm/python/tvm/_ffi/_ctypes/function.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, *args)\u001b[0m\n\u001b[1;32m 207\u001b[0m if _LIB.TVMFuncCall(\n\u001b[1;32m 208\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhandle\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalues\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtcodes\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mctypes\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mc_int\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnum_args\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 209\u001b[0;31m ctypes.byref(ret_val), ctypes.byref(ret_tcode)) != 0:\n\u001b[0m\u001b[1;32m 210\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mget_last_ffi_error\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 211\u001b[0m \u001b[0m_\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtemp_args\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", | |
"\u001b[0;31mKeyboardInterrupt\u001b[0m: " | |
] | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "### Generated code" | |
}, | |
{ | |
"metadata": { | |
"scrolled": true, | |
"trusted": false | |
}, | |
"cell_type": "code", | |
"source": "print(f.get_source(\"asm\"))", | |
"execution_count": 7, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": "\t.section\t__TEXT,__text,regular,pure_instructions\n\t.macosx_version_min 10, 14\n\t.globl\t_default_function\n\t.p2align\t4, 0x90\n_default_function:\nLfunc_begin0:\n\t.cfi_startproc\n\tpushq\t%rbp\n\t.cfi_def_cfa_offset 16\n\tpushq\t%r15\n\t.cfi_def_cfa_offset 24\n\tpushq\t%r14\n\t.cfi_def_cfa_offset 32\n\tpushq\t%r13\n\t.cfi_def_cfa_offset 40\n\tpushq\t%r12\n\t.cfi_def_cfa_offset 48\n\tpushq\t%rbx\n\t.cfi_def_cfa_offset 56\n\tpushq\t%rax\n\t.cfi_def_cfa_offset 64\n\t.cfi_offset %rbx, -56\n\t.cfi_offset %r12, -48\n\t.cfi_offset %r13, -40\n\t.cfi_offset %r14, -32\n\t.cfi_offset %r15, -24\n\t.cfi_offset %rbp, -16\n\tcmpl\t$3, %edx\n\tjne\tLBB0_1\n\tmovq\t(%rdi), %rax\n\tmovq\t8(%rdi), %rcx\n\tmovl\t(%rsi), %ebp\n\tmovl\t4(%rsi), %r13d\n\tmovq\t16(%rdi), %r11\n\tmovl\t8(%rsi), %r15d\n\tmovq\t(%rax), %rdx\n\tmovq\t24(%rax), %r14\n\tmovq\t32(%rax), %rsi\n\ttestq\t%rsi, %rsi\n\tje\tLBB0_7\n\tcmpl\t$1, 8(%rsi)\n\tjne\tLBB0_72\n\tcmpl\t$64, (%rsi)\n\tjne\tLBB0_72\nLBB0_7:\n\tmovl\t8(%rax), %r12d\n\tmovl\t12(%rax), %r8d\n\tmovq\t(%rcx), %rdi\n\tmovq\t24(%rcx), %r10\n\tmovq\t32(%rcx), %rsi\n\ttestq\t%rsi, %rsi\n\tje\tLBB0_11\n\tcmpl\t$1, 16(%rsi)\n\tjne\tLBB0_73\n\tcmpl\t$1, 8(%rsi)\n\tjne\tLBB0_73\n\tcmpl\t$16, (%rsi)\n\tjne\tLBB0_73\nLBB0_11:\n\tmovq\t(%r11), %rsi\n\tmovq\t24(%r11), %r9\n\tmovq\t32(%r11), %rbx\n\ttestq\t%rbx, %rbx\n\tje\tLBB0_14\n\tcmpl\t$1, 8(%rbx)\n\tjne\tLBB0_74\n\tcmpl\t$64, (%rbx)\n\tjne\tLBB0_74\nLBB0_14:\n\tcmpl\t$13, %ebp\n\tja\tLBB0_16\n\tmovl\t$8344, %ebx\n\tbtl\t%ebp, %ebx\n\tjae\tLBB0_16\n\tcmpl\t$13, %r13d\n\tja\tLBB0_19\n\tmovl\t$8344, %ebx\n\tbtl\t%r13d, %ebx\n\tjae\tLBB0_19\n\tcmpl\t$13, %r15d\n\tja\tLBB0_22\n\tmovl\t$8344, %ebx\n\tbtl\t%r15d, %ebx\n\tjae\tLBB0_22\n\tcmpl\t$1, %r12d\n\tjne\tLBB0_24\n\tcmpl\t$2, 16(%rax)\n\tjne\tLBB0_26\n\tcmpb\t$2, 20(%rax)\n\tjne\tLBB0_30\n\tcmpb\t$32, 21(%rax)\n\tjne\tLBB0_30\n\tmovzwl\t22(%rax), %ebx\n\tcmpl\t$1, %ebx\n\tjne\tLBB0_30\n\tcmpl\t$1, (%r14)\n\tjne\tLBB0_32\n\tcmpl\t$64, 8(%r14)\n\tjne\tLBB0_34\n\tcmpq\t$0, 40(%rax)\n\tjne\tLBB0_36\n\tcmpl\t$3, 16(%rcx)\n\tjne\tLBB0_38\n\tcmpb\t$2, 20(%rcx)\n\tjne\tLBB0_42\n\tcmpb\t$32, 21(%rcx)\n\tjne\tLBB0_42\n\tmovzwl\t22(%rcx), %eax\n\tcmpl\t$1, %eax\n\tjne\tLBB0_42\n\tcmpl\t$13, (%r10)\n\tjne\tLBB0_44\n\tcmpl\t$16, 8(%r10)\n\tjne\tLBB0_46\n\tcmpl\t$1, 16(%r10)\n\tjne\tLBB0_48\n\tcmpq\t$0, 40(%rcx)\n\tjne\tLBB0_50\n\tcmpl\t$1, 8(%rcx)\n\tjne\tLBB0_52\n\tcmpl\t12(%rcx), %r8d\n\tjne\tLBB0_54\n\tcmpl\t$2, 16(%r11)\n\tjne\tLBB0_56\n\tcmpb\t$2, 20(%r11)\n\tjne\tLBB0_60\n\tcmpb\t$32, 21(%r11)\n\tjne\tLBB0_60\n\tmovzwl\t22(%r11), %eax\n\tcmpl\t$1, %eax\n\tjne\tLBB0_60\n\tcmpl\t$1, (%r9)\n\tjne\tLBB0_62\n\tcmpl\t$64, 8(%r9)\n\tjne\tLBB0_64\n\tcmpq\t$0, 40(%r11)\n\tjne\tLBB0_66\n\tcmpl\t$1, 8(%r11)\n\tjne\tLBB0_68\n\tcmpl\t12(%r11), %r8d\n\tjne\tLBB0_70\n\tcallq\tl_default_function_compute_\n\txorl\t%eax, %eax\n\tjmp\tLBB0_3\nLBB0_16:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.4(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_19:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.5(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_22:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.6(%rip), %rdi\nLBB0_2:\n\tcallq\t*(%rax)\n\tmovl\t$-1, %eax\nLBB0_3:\n\taddq\t$8, %rsp\n\tpopq\t%rbx\n\tpopq\t%r12\n\tpopq\t%r13\n\tpopq\t%r14\n\tpopq\t%r15\n\tpopq\t%rbp\n\tretq\nLBB0_1:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_72:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.1(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_73:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.2(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_74:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.3(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_24:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.7(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_26:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.8(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_30:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.9(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_32:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.10(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_34:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.11(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_36:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.12(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_38:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.13(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_42:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.14(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_44:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.15(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_46:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.16(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_48:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.17(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_50:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.18(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_52:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.19(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_54:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.20(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_56:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.21(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_60:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.22(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_62:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.23(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_64:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.24(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_66:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.25(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_68:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.26(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_70:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.27(%rip), %rdi\n\tjmp\tLBB0_2\nLfunc_end0:\n\t.cfi_endproc\n\n\t.p2align\t4, 0x90\nl_default_function_compute_:\nLfunc_begin1:\n\t.cfi_startproc\n\tprefetcht0\t2048(%rdi)\n\tvbroadcastss\t212(%rdx), %ymm3\n\tvbroadcastss\t132(%rdx), %ymm4\n\tvbroadcastss\t124(%rdx), %ymm2\n\tvbroadcastss\t16(%rdx), %ymm5\n\tvbroadcastss\t12(%rdx), %ymm1\n\tvxorps\t%xmm0, %xmm0, %xmm0\n\tvmovaps\t(%rdi), %ymm6\n\tvfmadd213ps\t%ymm0, %ymm1, %ymm6\n\tvmovaps\t32(%rdi), %ymm7\n\tvfmadd213ps\t%ymm0, %ymm1, %ymm7\n\tvfmadd231ps\t96(%rdi), %ymm5, %ymm7\n\tvfmadd231ps\t64(%rdi), %ymm5, %ymm6\n\tvfmadd231ps\t128(%rdi), %ymm2, %ymm6\n\tvfmadd231ps\t160(%rdi), %ymm2, %ymm7\n\tvfmadd231ps\t224(%rdi), %ymm4, %ymm7\n\tvfmadd231ps\t192(%rdi), %ymm4, %ymm6\n\tvfmadd231ps\t256(%rdi), %ymm3, %ymm6\n\tvfmadd231ps\t288(%rdi), %ymm3, %ymm7\n\tvmovaps\t%ymm7, 32(%rsi)\n\tvmovaps\t%ymm6, (%rsi)\n\tvbroadcastss\t200(%rdx), %ymm3\n\tvbroadcastss\t92(%rdx), %ymm4\n\tvmovaps\t352(%rdi), %ymm5\n\tvfmadd213ps\t%ymm0, %ymm4, %ymm5\n\tvfmadd132ps\t320(%rdi), %ymm0, %ymm4\n\tvfmadd231ps\t384(%rdi), %ymm3, %ymm4\n\tvfmadd231ps\t416(%rdi), %ymm3, %ymm5\n\tvmovaps\t%ymm5, 96(%rsi)\n\tvmovaps\t%ymm4, 64(%rsi)\n\tvbroadcastss\t48(%rdx), %ymm3\n\tvbroadcastss\t32(%rdx), %ymm4\n\tvmovaps\t448(%rdi), %ymm5\n\tvfmadd213ps\t%ymm0, %ymm4, %ymm5\n\tvfmadd132ps\t480(%rdi), %ymm0, %ymm4\n\tvfmadd231ps\t544(%rdi), %ymm3, %ymm4\n\tvfmadd231ps\t512(%rdi), %ymm3, %ymm5\n\tvfmadd231ps\t576(%rdi), %ymm2, %ymm5\n\tvfmadd231ps\t608(%rdi), %ymm2, %ymm4\n\tvmovaps\t%ymm4, 160(%rsi)\n\tvmovaps\t%ymm5, 128(%rsi)\n\tvbroadcastss\t184(%rdx), %ymm2\n\tvbroadcastss\t96(%rdx), %ymm3\n\tvmovaps\t640(%rdi), %ymm4\n\tvfmadd213ps\t%ymm0, %ymm1, %ymm4\n\tvfmadd231ps\t672(%rdi), %ymm1, %ymm0\n\tvfmadd231ps\t736(%rdi), %ymm3, %ymm0\n\tvfmadd231ps\t704(%rdi), %ymm3, %ymm4\n\tvfmadd231ps\t768(%rdi), %ymm2, %ymm4\n\tvfmadd231ps\t800(%rdi), %ymm2, %ymm0\n\tvmovaps\t%ymm0, 224(%rsi)\n\tvmovaps\t%ymm4, 192(%rsi)\n\tvzeroupper\n\tretq\nLfunc_end1:\n\t.cfi_endproc\n\n\t.section\t__DATA,__data\n\t.globl\t___TVMAPISetLastError\n\t.weak_definition\t___TVMAPISetLastError\n\t.p2align\t3\n___TVMAPISetLastError:\n\t.quad\t0\n\n\t.section\t__TEXT,__const\nl_.str:\n\t.asciz\t\"Assert fail: (num_args == 3), default_function: num_args should be 3\"\n\nl_.str.1:\n\t.asciz\t\"Assert fail: ((1 == int32(arg0.strides[1])) && (64 == int32(arg0.strides[0]))), arg0.strides: expected to be compact array\"\n\nl_.str.2:\n\t.asciz\t\"Assert fail: (((1 == int32(arg1.strides[2])) && (1 == int32(arg1.strides[1]))) && (16 == int32(arg1.strides[0]))), arg1.strides: expected to be compact array\"\n\nl_.str.3:\n\t.asciz\t\"Assert fail: ((1 == int32(arg2.strides[1])) && (64 == int32(arg2.strides[0]))), arg2.strides: expected to be compact array\"\n\nl_.str.4:\n\t.asciz\t\"Assert fail: ((((arg0.code == 3) || (arg0.code == 13)) || (arg0.code == 7)) || (arg0.code == 4)), default_function: Expect arg[0] to be pointer\"\n\nl_.str.5:\n\t.asciz\t\"Assert fail: ((((arg1.code == 3) || (arg1.code == 13)) || (arg1.code == 7)) || (arg1.code == 4)), default_function: Expect arg[1] to be pointer\"\n\nl_.str.6:\n\t.asciz\t\"Assert fail: ((((arg2.code == 3) || (arg2.code == 13)) || (arg2.code == 7)) || (arg2.code == 4)), default_function: Expect arg[2] to be pointer\"\n\nl_.str.7:\n\t.asciz\t\"Assert fail: (dev_type == 1), device_type need to be 1\"\n\nl_.str.8:\n\t.asciz\t\"Assert fail: (2 == tvm_struct_get(arg0, 0, 4)), arg0.ndim is expected to equal 2\"\n\nl_.str.9:\n\t.asciz\t\"Assert fail: (((tvm_struct_get(arg0, 0, 5) == (uint8)2) && (tvm_struct_get(arg0, 0, 6) == (uint8)32)) && (tvm_struct_get(arg0, 0, 7) == (uint16)1)), arg0.dtype is expected to be float32\"\n\nl_.str.10:\n\t.asciz\t\"Assert fail: (1 == int32(arg0.shape[0])), Argument arg0.shape[0] has an unsatisfied constraint\"\n\nl_.str.11:\n\t.asciz\t\"Assert fail: (64 == int32(arg0.shape[1])), Argument arg0.shape[1] has an unsatisfied constraint\"\n\nl_.str.12:\n\t.asciz\t\"Assert fail: ((uint64)0 == tvm_struct_get(arg0, 0, 8)), Argument arg0.byte_offset has an unsatisfied constraint\"\n\nl_.str.13:\n\t.asciz\t\"Assert fail: (3 == tvm_struct_get(arg1, 0, 4)), arg1.ndim is expected to equal 3\"\n\nl_.str.14:\n\t.asciz\t\"Assert fail: (((tvm_struct_get(arg1, 0, 5) == (uint8)2) && (tvm_struct_get(arg1, 0, 6) == (uint8)32)) && (tvm_struct_get(arg1, 0, 7) == (uint16)1)), arg1.dtype is expected to be float32\"\n\nl_.str.15:\n\t.asciz\t\"Assert fail: (13 == int32(arg1.shape[0])), Argument arg1.shape[0] has an unsatisfied constraint\"\n\nl_.str.16:\n\t.asciz\t\"Assert fail: (16 == int32(arg1.shape[1])), Argument arg1.shape[1] has an unsatisfied constraint\"\n\nl_.str.17:\n\t.asciz\t\"Assert fail: (1 == int32(arg1.shape[2])), Argument arg1.shape[2] has an unsatisfied constraint\"\n\nl_.str.18:\n\t.asciz\t\"Assert fail: ((uint64)0 == tvm_struct_get(arg1, 0, 8)), Argument arg1.byte_offset has an unsatisfied constraint\"\n\nl_.str.19:\n\t.asciz\t\"Assert fail: (1 == tvm_struct_get(arg1, 0, 10)), Argument arg1.device_type has an unsatisfied constraint\"\n\nl_.str.20:\n\t.asciz\t\"Assert fail: (dev_id == tvm_struct_get(arg1, 0, 9)), Argument arg1.device_id has an unsatisfied constraint\"\n\nl_.str.21:\n\t.asciz\t\"Assert fail: (2 == tvm_struct_get(arg2, 0, 4)), arg2.ndim is expected to equal 2\"\n\nl_.str.22:\n\t.asciz\t\"Assert fail: (((tvm_struct_get(arg2, 0, 5) == (uint8)2) && (tvm_struct_get(arg2, 0, 6) == (uint8)32)) && (tvm_struct_get(arg2, 0, 7) == (uint16)1)), arg2.dtype is expected to be float32\"\n\nl_.str.23:\n\t.asciz\t\"Assert fail: (1 == int32(arg2.shape[0])), Argument arg2.shape[0] has an unsatisfied constraint\"\n\nl_.str.24:\n\t.asciz\t\"Assert fail: (64 == int32(arg2.shape[1])), Argument arg2.shape[1] has an unsatisfied constraint\"\n\nl_.str.25:\n\t.asciz\t\"Assert fail: ((uint64)0 == tvm_struct_get(arg2, 0, 8)), Argument arg2.byte_offset has an unsatisfied constraint\"\n\nl_.str.26:\n\t.asciz\t\"Assert fail: (1 == tvm_struct_get(arg2, 0, 10)), Argument arg2.device_type has an unsatisfied constraint\"\n\nl_.str.27:\n\t.asciz\t\"Assert fail: (dev_id == tvm_struct_get(arg2, 0, 9)), Argument arg2.device_id has an unsatisfied constraint\"\n\n\t.globl\t___tvm_main__\n\t.weak_definition\t___tvm_main__\n___tvm_main__:\n\t.asciz\t\"default_function\"\n\n\t.section\t__DWARF,__debug_str,regular,debug\nLinfo_string:\n\t.asciz\t\"TVM\"\n\t.asciz\t\"model.tvm\"\n\t.asciz\t\"/tmp/\"\n\t.section\t__DWARF,__debug_abbrev,regular,debug\nLsection_abbrev:\n\t.byte\t1\n\t.byte\t17\n\t.byte\t0\n\t.byte\t37\n\t.byte\t14\n\t.byte\t19\n\t.byte\t5\n\t.byte\t3\n\t.byte\t14\n\t.byte\t16\n\t.byte\t6\n\t.byte\t27\n\t.byte\t14\n\t.ascii\t\"\\261B\"\n\t.byte\t7\n\t.byte\t0\n\t.byte\t0\n\t.byte\t0\n\t.section\t__DWARF,__debug_info,regular,debug\nLsection_info:\nLcu_begin0:\n\t.long\t34\n\t.short\t2\n.set Lset0, Lsection_abbrev-Lsection_abbrev\n\t.long\tLset0\n\t.byte\t8\n\t.byte\t1\n\t.long\t0\n\t.short\t2\n\t.long\t4\n.set Lset1, Lline_table_start0-Lsection_line\n\t.long\tLset1\n\t.long\t14\n\t.quad\t1\n\t.section\t__DWARF,__debug_macinfo,regular,debug\nLdebug_macinfo:\n\t.byte\t0\n\t.section\t__DWARF,__apple_names,regular,debug\nLnames_begin:\n\t.long\t1212240712\n\t.short\t1\n\t.short\t0\n\t.long\t1\n\t.long\t0\n\t.long\t12\n\t.long\t0\n\t.long\t1\n\t.short\t1\n\t.short\t6\n\t.long\t-1\n\t.section\t__DWARF,__apple_objc,regular,debug\nLobjc_begin:\n\t.long\t1212240712\n\t.short\t1\n\t.short\t0\n\t.long\t1\n\t.long\t0\n\t.long\t12\n\t.long\t0\n\t.long\t1\n\t.short\t1\n\t.short\t6\n\t.long\t-1\n\t.section\t__DWARF,__apple_namespac,regular,debug\nLnamespac_begin:\n\t.long\t1212240712\n\t.short\t1\n\t.short\t0\n\t.long\t1\n\t.long\t0\n\t.long\t12\n\t.long\t0\n\t.long\t1\n\t.short\t1\n\t.short\t6\n\t.long\t-1\n\t.section\t__DWARF,__apple_types,regular,debug\nLtypes_begin:\n\t.long\t1212240712\n\t.short\t1\n\t.short\t0\n\t.long\t1\n\t.long\t0\n\t.long\t20\n\t.long\t0\n\t.long\t3\n\t.short\t1\n\t.short\t6\n\t.short\t3\n\t.short\t5\n\t.short\t4\n\t.short\t11\n\t.long\t-1\n\n.subsections_via_symbols\n\t.section\t__DWARF,__debug_line,regular,debug\nLsection_line:\nLline_table_start0:\n\n" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "## TVM, input-sparsity-pattern specialized, register-blocked" | |
}, | |
{ | |
"metadata": { | |
"trusted": false | |
}, | |
"cell_type": "code", | |
"source": "def bsr_matvec_nt_codegen(X, WS, REGISTERS):\n N, K = WS.shape\n R, C = WS.blocksize\n assert C == 1\n indices = WS.indices\n indptr = WS.indptr\n data = WS.data\n import tvm\n vecty = 'float32x{R}'.format(R=R)\n\n def tvm_bsr_codegen(ins, outs):\n ib = tvm.ir_builder.create()\n load_count = 0\n \n N_vec = (N // (R * REGISTERS)) * (R * REGISTERS)\n for nb in range(0, N_vec, R):\n accs = ib.allocate(vecty, REGISTERS, name=\"ACCS\")\n for r in range(REGISTERS):\n accs[r] = tvm.const(0, vecty)\n acc = tvm.const(0, vecty)\n for jj in range(indptr[nb // R], indptr[nb // R + 1]):\n j = indices[jj]\n x_k = ins[0].vload([0, C * j], 'float32').astype(vecty)\n w_nk_vec = ins[1].vload([jj, 0, 0], vecty)\n acc += x_k * w_nk_vec\n load_count += 1\n\n ib.emit(outs[0].vstore([0, nb], acc))\n return ib.get()\n\n Xtvm = tvm.placeholder(X.shape, name=\"X\", dtype=str(X.dtype))\n WSdatatvm = tvm.placeholder(WS.data.shape,\n name=\"WS.data\",\n dtype=str(WS.data.dtype))\n Ytvm = tvm.extern((1, N), [Xtvm, WSdatatvm], tvm_bsr_codegen)\n s = tvm.create_schedule(Ytvm.op)\n f = tvm.build(s, [Xtvm, WSdatatvm, Ytvm], \"llvm -mcpu=core-avx2\")\n Xnd = tvm.nd.array(X)\n WSdata_nd = tvm.nd.array(WS.data)\n Ynd = tvm.nd.array(np.zeros((1, N)).astype(np.float32))\n f(Xnd, WSdata_nd, Ynd)\n te = f.time_evaluator(f.entry_name,\n ctx=tvm.cpu(0),\n repeat=5,\n min_repeat_ms=5000)\n fte = lambda: te(Xnd, WSdata_nd, Ynd)\n return np.array(Ynd.asnumpy()), fte, f\n\nN = 1024\nK = 1024\nBS = 16\n\nWS = random_bsr_matrix(N, K, BS, 1, density=density, dtype=\"float32\")\nX = np.random.randn(1, K).astype(np.float32)\nYS = WS.dot(X.T).T\n\nfor prefetch_length in [8, 16, 32]:\n for prefetch_interval in [8, 16, 32]:\n YZ, fte, f = bsr_matvec_nt_codegen(X,\n WS,\n prefetch_length=prefetch_length,\n prefetch_interval=prefetch_interval)\n np.testing.assert_allclose(YS, YZ, atol=1e-5, rtol=1e-5)\n\n result = fte()\n print(\n \"N: {N}, K: {K}, BS: {BS}x1, Prefetch Length: {prefetch_length}, Prefetch Interval: {prefetch_interval}, t: {t:.3}, TVM Sparsity-Spec GFLOP/s: {GFLOPs:.3}\"\n .format(N=N,\n K=K,\n BS=BS,\n prefetch_interval=prefetch_interval,\n prefetch_length=prefetch_length,\n t=result.mean,\n GFLOPs=2 * N * K / result.mean / 10**9))", | |
"execution_count": null, | |
"outputs": [] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "## TVM, non-specialized" | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "import itertools\nimport topi\n\n\ndef sparse_dense_bsrmv(data, weight_data, weight_indices, weight_indptr):\n import topi\n (M, K) = topi.util.get_const_tuple(data.shape)\n (_, BS_R, BS_C) = topi.util.get_const_tuple(weight_data.shape)\n (NB_plus_1, ) = topi.util.get_const_tuple(weight_indptr.shape)\n NB = NB_plus_1 - 1\n\n oshape = (M, NB, BS_R)\n\n def f(i, nb, r):\n row_start = weight_indptr[nb]\n row_end = weight_indptr[nb + 1]\n row_elems = row_end - row_start\n elem_idx = tvm.reduce_axis((0, row_elems), name=\"elem_idx\")\n jj = row_start + elem_idx\n c = tvm.reduce_axis((0, BS_C), name=\"c\")\n j = weight_indices[jj]\n block_ij_val = weight_data[jj][r][c]\n assert weight_data.dtype in (\"float32\", )\n x_val = data[0, BS_C * j + c]\n return tvm.sum(block_ij_val * x_val, axis=[elem_idx, c])\n\n Y = tvm.compute(oshape,\n f,\n name=\"sparse_dense_bsrmv_block\",\n tag=\"sparse_dense_bsrmv_block\")\n return tvm.compute((M, NB * BS_R),\n lambda m, n: Y[m, n // BS_R, n % BS_R],\n name=\"sparse_dense_bsrmv\",\n tag=\"sparse_dense_bsrmv\")\n\n\ndef schedule_sparse_dense(outs):\n s = tvm.create_schedule([x.op for x in outs])\n\n def callback(op):\n if op.tag == \"sparse_dense_csrmv\" and op != outs[0].op:\n (_, vi) = s[op].op.axis\n s[op].vectorize(vi)\n (yo, yi) = s[outs[0].op].split(s[outs[0].op].op.axis[1], 32)\n s[op].compute_at(s[outs[0]], yo)\n s[outs[0].op].vectorize(yi)\n if op.tag == \"sparse_dense_bsrmv\":\n Y_bsrmv = op.input_tensors[0]\n assert Y_bsrmv.op.tag == \"sparse_dense_bsrmv_block\"\n Y_reshape = op\n (m, nb, br) = s[Y_bsrmv].op.axis\n BS_R = topi.util.get_const_int(br.dom.extent)\n (elem_idx, c) = s[Y_bsrmv].op.reduce_axis\n s[Y_bsrmv].reorder(nb, m, elem_idx, br, c)\n s[Y_bsrmv].vectorize(br)\n (mo, no) = s[Y_reshape].op.axis\n (noo, noi) = s[Y_reshape].split(no, BS_R)\n # s[Y_reshape].unroll(noo)\n s[Y_bsrmv].compute_at(s[Y_reshape], noi)\n s[Y_reshape].vectorize(noi)\n s[Y_reshape].unroll(mo)\n if op != s[outs[0]].op:\n (yo, yi) = s[outs[0].op].split(s[outs[0].op].op.axis[1], 32)\n s[Y_reshape].compute_at(s[outs[0]], yo)\n # s[outs[0].op].parallel(yo)\n # s[outs[0].op].unroll(yo)\n s[outs[0].op].vectorize(yi)\n else:\n # s[Y_reshape].parallel(noo)\n # s[Y_reshape].unroll(noo)\n pass\n\n topi.util.traverse_inline(s, outs[0].op, callback)\n return s\n\n\ndef benchmark(M, BS_R, BS_C, N, K, SPARSITY):\n WS = random_bsr_matrix(N,\n K,\n BS_R,\n BS_C,\n density=1.0 - SPARSITY,\n dtype=\"float32\")\n W = WS.todense()\n X = np.random.randn(M, K).astype(np.float32)\n\n np.testing.assert_almost_equal(WS.todense(), W)\n Y = X.dot(W.T)\n\n import copy\n WS_tvm_ph = copy.copy(WS)\n WS_tvm_ph.data = tvm.placeholder(WS.data.shape,\n dtype=str(\"float32\"),\n name=\"WS.data\")\n WS_tvm_ph.indices = tvm.placeholder(WS.indices.shape,\n dtype=str(WS.indices.dtype),\n name=\"WS.indices\")\n WS_tvm_ph.indptr = tvm.placeholder(WS.indptr.shape,\n dtype=str(WS.indptr.dtype),\n name=\"WS.indptr\")\n X_tvm_ph = tvm.placeholder(X.shape, dtype=str(X.dtype), name=\"X\")\n Y_tvm = sparse_dense_bsrmv(X_tvm_ph, WS_tvm_ph.data, WS_tvm_ph.indices,\n WS_tvm_ph.indptr)\n s = schedule_sparse_dense([Y_tvm])\n\n # print(tvm.lower(s, [WS_tvm_ph.data, WS_tvm_ph.indices, WS_tvm_ph.indptr, X_tvm_ph, Y_tvm], simple_mode=True))\n\n with tvm.target.create(\"llvm -mcpu=core-avx2\"):\n func = tvm.build(\n s,\n [\n WS_tvm_ph.data, WS_tvm_ph.indices, WS_tvm_ph.indptr, X_tvm_ph,\n Y_tvm\n ],\n )\n\n Y_tvm = tvm.ndarray.empty(Y_tvm.shape, Y_tvm.dtype)\n func(tvm.ndarray.array(WS.data), tvm.ndarray.array(WS.indices),\n tvm.ndarray.array(WS.indptr), tvm.ndarray.array(X), Y_tvm)\n\n Y_tvm_result = np.array(Y_tvm.asnumpy().reshape(M, N))\n np.testing.assert_allclose(Y_tvm_result, Y, rtol=1e-1, atol=1e-1)\n dense_ts = %timeit -q -o X.dot(W.T)\n ftimer = func.time_evaluator(func.entry_name, tvm.cpu(), min_repeat_ms=5000, repeat=5)\n\n results = ftimer(tvm.ndarray.array(WS.data), \n tvm.ndarray.array(WS.indices), \n tvm.ndarray.array(WS.indptr), \n tvm.ndarray.array(X), \n Y_tvm)\n\n print(\"N: {N}, K: {K}, BS: {BS}x1, t: {t:.3}, TVM Unspecialized GFLOP/s: {GFLOPs:.3}\".format(\n N=N, K=K, BS=BS, t=results.mean, GFLOPs=2 * N * K / results.mean / 10 ** 9)) \n return func\n\nfunc = benchmark(M=1, BS_R=BS, BS_C=1, N=N, K=K, SPARSITY=1.0 - density) ", | |
"execution_count": 101, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": "N: 1024, K: 1024, BS: 8x1, t: 8.47e-06, TVM Unspecialized GFLOP/s: 2.48e+02\n", | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "### Generated Code" | |
}, | |
{ | |
"metadata": { | |
"scrolled": true, | |
"trusted": false | |
}, | |
"cell_type": "code", | |
"source": "print(func.get_source(\"asm\"))", | |
"execution_count": 11, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": "\t.section\t__TEXT,__text,regular,pure_instructions\n\t.macosx_version_min 10, 14\n\t.globl\t_default_function\n\t.p2align\t4, 0x90\n_default_function:\nLfunc_begin0:\n\t.cfi_startproc\n\tpushq\t%rbp\n\t.cfi_def_cfa_offset 16\n\tpushq\t%r15\n\t.cfi_def_cfa_offset 24\n\tpushq\t%r14\n\t.cfi_def_cfa_offset 32\n\tpushq\t%r13\n\t.cfi_def_cfa_offset 40\n\tpushq\t%r12\n\t.cfi_def_cfa_offset 48\n\tpushq\t%rbx\n\t.cfi_def_cfa_offset 56\n\tsubq\t$72, %rsp\n\t.cfi_def_cfa_offset 128\n\t.cfi_offset %rbx, -56\n\t.cfi_offset %r12, -48\n\t.cfi_offset %r13, -40\n\t.cfi_offset %r14, -32\n\t.cfi_offset %r15, -24\n\t.cfi_offset %rbp, -16\n\tcmpl\t$5, %edx\n\tjne\tLBB0_1\n\tmovq\t(%rdi), %rax\n\tmovq\t8(%rdi), %rbx\n\tmovl\t(%rsi), %r15d\n\tmovl\t4(%rsi), %edx\n\tmovq\t16(%rdi), %r13\n\tmovl\t8(%rsi), %r12d\n\tmovq\t24(%rdi), %r11\n\tmovl\t12(%rsi), %ecx\n\tmovq\t32(%rdi), %r10\n\tmovl\t16(%rsi), %esi\n\tmovq\t(%rax), %rdi\n\tmovq\t%rdi, 48(%rsp)\n\tmovq\t24(%rax), %r14\n\tmovq\t32(%rax), %rdi\n\ttestq\t%rdi, %rdi\n\tje\tLBB0_8\n\tcmpl\t$1, 16(%rdi)\n\tjne\tLBB0_110\n\tcmpl\t$1, 8(%rdi)\n\tjne\tLBB0_110\n\tcmpl\t$16, (%rdi)\n\tjne\tLBB0_110\nLBB0_8:\n\tmovl\t8(%rax), %r8d\n\tmovl\t12(%rax), %r9d\n\tmovq\t(%rbx), %rdi\n\tmovq\t%rdi, 40(%rsp)\n\tmovq\t24(%rbx), %rbp\n\tmovq\t32(%rbx), %rdi\n\ttestq\t%rdi, %rdi\n\tje\tLBB0_10\n\tcmpl\t$1, (%rdi)\n\tjne\tLBB0_111\nLBB0_10:\n\tmovq\t(%r13), %rdi\n\tmovq\t%rdi, 32(%rsp)\n\tmovq\t24(%r13), %rdi\n\tmovq\t%rdi, 64(%rsp)\n\tmovq\t32(%r13), %rdi\n\ttestq\t%rdi, %rdi\n\tje\tLBB0_12\n\tcmpl\t$1, (%rdi)\n\tjne\tLBB0_112\nLBB0_12:\n\tmovq\t(%r11), %rdi\n\tmovq\t%rdi, 16(%rsp)\n\tmovq\t24(%r11), %rdi\n\tmovq\t%rdi, 56(%rsp)\n\tmovq\t32(%r11), %rdi\n\ttestq\t%rdi, %rdi\n\tje\tLBB0_15\n\tcmpl\t$1, 8(%rdi)\n\tjne\tLBB0_113\n\tcmpl\t$64, (%rdi)\n\tjne\tLBB0_113\nLBB0_15:\n\tmovq\t(%r10), %rdi\n\tmovq\t%rdi, 8(%rsp)\n\tmovq\t24(%r10), %rdi\n\tmovq\t%rdi, 24(%rsp)\n\tmovq\t32(%r10), %rdi\n\ttestq\t%rdi, %rdi\n\tje\tLBB0_18\n\tcmpl\t$1, 8(%rdi)\n\tjne\tLBB0_114\n\tcmpl\t$64, (%rdi)\n\tjne\tLBB0_114\nLBB0_18:\n\tcmpl\t$13, %r15d\n\tja\tLBB0_20\n\tmovl\t$8344, %edi\n\tbtl\t%r15d, %edi\n\tjae\tLBB0_20\n\tcmpl\t$13, %edx\n\tja\tLBB0_23\n\tmovl\t$8344, %edi\n\tbtl\t%edx, %edi\n\tjae\tLBB0_23\n\tcmpl\t$13, %r12d\n\tja\tLBB0_26\n\tmovl\t$8344, %edx\n\tbtl\t%r12d, %edx\n\tjae\tLBB0_26\n\tcmpl\t$13, %ecx\n\tja\tLBB0_29\n\tmovl\t$8344, %edx\n\tbtl\t%ecx, %edx\n\tjae\tLBB0_29\n\tcmpl\t$13, %esi\n\tja\tLBB0_32\n\tmovl\t$8344, %ecx\n\tbtl\t%esi, %ecx\n\tjae\tLBB0_32\n\tcmpl\t$1, %r8d\n\tjne\tLBB0_34\n\tcmpl\t$3, 16(%rax)\n\tjne\tLBB0_36\n\tcmpb\t$2, 20(%rax)\n\tjne\tLBB0_40\n\tcmpb\t$32, 21(%rax)\n\tjne\tLBB0_40\n\tmovzwl\t22(%rax), %ecx\n\tcmpl\t$1, %ecx\n\tjne\tLBB0_40\n\tcmpl\t$13, (%r14)\n\tjne\tLBB0_42\n\tcmpl\t$16, 8(%r14)\n\tjne\tLBB0_44\n\tcmpl\t$1, 16(%r14)\n\tjne\tLBB0_46\n\tcmpq\t$0, 40(%rax)\n\tjne\tLBB0_48\n\tcmpl\t$1, 16(%rbx)\n\tjne\tLBB0_50\n\tcmpb\t$0, 20(%rbx)\n\tjne\tLBB0_54\n\tcmpb\t$32, 21(%rbx)\n\tjne\tLBB0_54\n\tmovzwl\t22(%rbx), %eax\n\tcmpl\t$1, %eax\n\tjne\tLBB0_54\n\tcmpl\t$13, (%rbp)\n\tjne\tLBB0_56\n\tcmpq\t$0, 40(%rbx)\n\tjne\tLBB0_58\n\tcmpl\t$1, 8(%rbx)\n\tjne\tLBB0_60\n\tcmpl\t12(%rbx), %r9d\n\tjne\tLBB0_62\n\tcmpl\t$1, 16(%r13)\n\tjne\tLBB0_64\n\tcmpb\t$0, 20(%r13)\n\tjne\tLBB0_68\n\tcmpb\t$32, 21(%r13)\n\tjne\tLBB0_68\n\tmovzwl\t22(%r13), %eax\n\tcmpl\t$1, %eax\n\tjne\tLBB0_68\n\tmovq\t64(%rsp), %rax\n\tcmpl\t$5, (%rax)\n\tjne\tLBB0_70\n\tcmpq\t$0, 40(%r13)\n\tjne\tLBB0_72\n\tcmpl\t$1, 8(%r13)\n\tjne\tLBB0_74\n\tcmpl\t12(%r13), %r9d\n\tjne\tLBB0_76\n\tcmpl\t$2, 16(%r11)\n\tjne\tLBB0_78\n\tcmpb\t$2, 20(%r11)\n\tjne\tLBB0_82\n\tcmpb\t$32, 21(%r11)\n\tjne\tLBB0_82\n\tmovzwl\t22(%r11), %eax\n\tcmpl\t$1, %eax\n\tjne\tLBB0_82\n\tmovq\t56(%rsp), %rax\n\tcmpl\t$1, (%rax)\n\tjne\tLBB0_84\n\tcmpl\t$64, 8(%rax)\n\tjne\tLBB0_86\n\tcmpq\t$0, 40(%r11)\n\tjne\tLBB0_88\n\tcmpl\t$1, 8(%r11)\n\tjne\tLBB0_90\n\tcmpl\t12(%r11), %r9d\n\tjne\tLBB0_92\n\tcmpl\t$2, 16(%r10)\n\tjne\tLBB0_94\n\tcmpb\t$2, 20(%r10)\n\tjne\tLBB0_98\n\tcmpb\t$32, 21(%r10)\n\tjne\tLBB0_98\n\tmovzwl\t22(%r10), %eax\n\tcmpl\t$1, %eax\n\tjne\tLBB0_98\n\tmovq\t24(%rsp), %rax\n\tcmpl\t$1, (%rax)\n\tjne\tLBB0_100\n\tcmpl\t$64, 8(%rax)\n\tjne\tLBB0_102\n\tcmpq\t$0, 40(%r10)\n\tjne\tLBB0_104\n\tcmpl\t$1, 8(%r10)\n\tjne\tLBB0_106\n\tcmpl\t12(%r10), %r9d\n\tjne\tLBB0_108\n\tmovq\t32(%rsp), %rdi\n\tmovq\t48(%rsp), %rsi\n\tmovq\t16(%rsp), %rdx\n\tmovq\t40(%rsp), %rcx\n\tmovq\t8(%rsp), %r8\n\tcallq\tl_default_function_compute_\n\txorl\t%eax, %eax\n\tjmp\tLBB0_3\nLBB0_20:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.6(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_23:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.7(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_26:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.8(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_29:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.9(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_32:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.10(%rip), %rdi\nLBB0_2:\n\tcallq\t*(%rax)\n\tmovl\t$-1, %eax\nLBB0_3:\n\taddq\t$72, %rsp\n\tpopq\t%rbx\n\tpopq\t%r12\n\tpopq\t%r13\n\tpopq\t%r14\n\tpopq\t%r15\n\tpopq\t%rbp\n\tretq\nLBB0_1:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_110:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.1(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_111:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.2(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_112:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.3(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_113:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.4(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_114:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.5(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_34:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.11(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_36:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.12(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_40:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.13(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_42:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.14(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_44:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.15(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_46:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.16(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_48:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.17(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_50:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.18(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_54:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.19(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_56:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.20(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_58:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.21(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_60:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.22(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_62:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.23(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_64:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.24(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_68:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.25(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_70:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.26(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_72:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.27(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_74:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.28(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_76:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.29(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_78:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.30(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_82:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.31(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_84:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.32(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_86:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.33(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_88:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.34(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_90:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.35(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_92:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.36(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_94:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.37(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_98:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.38(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_100:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.39(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_102:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.40(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_104:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.41(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_106:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.42(%rip), %rdi\n\tjmp\tLBB0_2\nLBB0_108:\n\tmovq\t___TVMAPISetLastError@GOTPCREL(%rip), %rax\n\tleaq\tl_.str.43(%rip), %rdi\n\tjmp\tLBB0_2\nLfunc_end0:\n\t.cfi_endproc\n\n\t.p2align\t4, 0x90\nl_default_function_compute_:\nLfunc_begin1:\n\t.cfi_startproc\n\tpushq\t%rbp\n\t.cfi_def_cfa_offset 16\n\tpushq\t%r15\n\t.cfi_def_cfa_offset 24\n\tpushq\t%r14\n\t.cfi_def_cfa_offset 32\n\tpushq\t%r13\n\t.cfi_def_cfa_offset 40\n\tpushq\t%r12\n\t.cfi_def_cfa_offset 48\n\tpushq\t%rbx\n\t.cfi_def_cfa_offset 56\n\t.cfi_offset %rbx, -56\n\t.cfi_offset %r12, -48\n\t.cfi_offset %r13, -40\n\t.cfi_offset %r14, -32\n\t.cfi_offset %r15, -24\n\t.cfi_offset %rbp, -16\n\tmovl\t(%rdi), %r11d\n\tmovl\t4(%rdi), %r9d\n\tmovl\t%r9d, %eax\n\tsubl\t%r11d, %eax\n\tvxorps\t%xmm0, %xmm0, %xmm0\n\ttestl\t%eax, %eax\n\tjle\tLBB1_1\n\tmovl\t%eax, %r14d\n\tleaq\t-1(%r14), %rax\n\tmovl\t%r14d, %r10d\n\tandl\t$3, %r10d\n\tcmpq\t$3, %rax\n\tjae\tLBB1_9\n\tvxorps\t%xmm1, %xmm1, %xmm1\n\txorl\t%r15d, %r15d\n\tvxorps\t%xmm2, %xmm2, %xmm2\n\ttestq\t%r10, %r10\n\tjne\tLBB1_5\n\tjmp\tLBB1_7\nLBB1_9:\n\tmovl\t%r11d, %ebx\n\tshll\t$4, %ebx\n\taddl\t$48, %ebx\n\tsubq\t%r10, %r14\n\tvxorps\t%xmm1, %xmm1, %xmm1\n\txorl\t%r15d, %r15d\n\tvxorps\t%xmm2, %xmm2, %xmm2\n\t.p2align\t4, 0x90\nLBB1_10:\n\tleal\t-48(%rbx), %eax\n\tmovslq\t%eax, %r12\n\tleaq\t(%r11,%r15), %rax\n\tmovslq\t%eax, %r13\n\tmovslq\t(%rcx,%r13,4), %rax\n\tvbroadcastss\t(%rdx,%rax,4), %ymm3\n\tvfmadd231ps\t(%rsi,%r12,4), %ymm3, %ymm1\n\tvfmadd231ps\t32(%rsi,%r12,4), %ymm3, %ymm2\n\tleal\t-32(%rbx), %eax\n\tcltq\n\tleal\t1(%r13), %ebp\n\tmovslq\t%ebp, %rbp\n\tmovslq\t(%rcx,%rbp,4), %rbp\n\tvbroadcastss\t(%rdx,%rbp,4), %ymm3\n\tvfmadd231ps\t32(%rsi,%rax,4), %ymm3, %ymm2\n\tvfmadd231ps\t(%rsi,%rax,4), %ymm3, %ymm1\n\tleal\t-16(%rbx), %eax\n\tcltq\n\tleal\t2(%r13), %ebp\n\tmovslq\t%ebp, %rbp\n\tmovslq\t(%rcx,%rbp,4), %rbp\n\tvbroadcastss\t(%rdx,%rbp,4), %ymm3\n\tvfmadd231ps\t(%rsi,%rax,4), %ymm3, %ymm1\n\tvfmadd231ps\t32(%rsi,%rax,4), %ymm3, %ymm2\n\tmovslq\t%ebx, %rbx\n\taddl\t$3, %r13d\n\tmovslq\t%r13d, %rax\n\tmovslq\t(%rcx,%rax,4), %rax\n\tvbroadcastss\t(%rdx,%rax,4), %ymm3\n\tvfmadd231ps\t32(%rsi,%rbx,4), %ymm3, %ymm2\n\tvfmadd231ps\t(%rsi,%rbx,4), %ymm3, %ymm1\n\taddq\t$4, %r15\n\taddl\t$64, %ebx\n\tcmpq\t%r15, %r14\n\tjne\tLBB1_10\n\ttestq\t%r10, %r10\n\tje\tLBB1_7\nLBB1_5:\n\taddl\t%r11d, %r15d\n\tmovl\t%r15d, %ebx\n\tshll\t$4, %ebx\n\t.p2align\t4, 0x90\nLBB1_6:\n\tmovslq\t%ebx, %rbx\n\tmovslq\t%r15d, %r15\n\tmovslq\t(%rcx,%r15,4), %rax\n\tvbroadcastss\t(%rdx,%rax,4), %ymm3\n\tvfmadd231ps\t32(%rsi,%rbx,4), %ymm3, %ymm2\n\tvfmadd231ps\t(%rsi,%rbx,4), %ymm3, %ymm1\n\taddl\t$1, %r15d\n\taddl\t$16, %ebx\n\taddq\t$-1, %r10\n\tjne\tLBB1_6\nLBB1_7:\n\tvmovaps\t%ymm1, (%r8)\n\tvmovaps\t%ymm2, 32(%r8)\n\tmovl\t8(%rdi), %r10d\n\tmovl\t%r10d, %eax\n\tsubl\t%r9d, %eax\n\ttestl\t%eax, %eax\n\tjle\tLBB1_8\n\tmovl\t%eax, %r14d\n\tleaq\t-1(%r14), %rax\n\tmovl\t%r14d, %r11d\n\tandl\t$3, %r11d\n\tcmpq\t$3, %rax\n\tjae\tLBB1_13\n\tvxorps\t%xmm0, %xmm0, %xmm0\n\txorl\t%r15d, %r15d\n\tvxorps\t%xmm1, %xmm1, %xmm1\n\ttestq\t%r11, %r11\n\tjne\tLBB1_16\n\tjmp\tLBB1_18\nLBB1_13:\n\tmovl\t%r9d, %ebx\n\tshll\t$4, %ebx\n\taddl\t$48, %ebx\n\tsubq\t%r11, %r14\n\tvxorps\t%xmm0, %xmm0, %xmm0\n\txorl\t%r15d, %r15d\n\tvxorps\t%xmm1, %xmm1, %xmm1\n\t.p2align\t4, 0x90\nLBB1_14:\n\tleal\t-48(%rbx), %eax\n\tmovslq\t%eax, %r12\n\tleaq\t(%r9,%r15), %rax\n\tcltq\n\tmovslq\t(%rcx,%rax,4), %rbp\n\tvbroadcastss\t(%rdx,%rbp,4), %ymm2\n\tvfmadd231ps\t(%rsi,%r12,4), %ymm2, %ymm0\n\tvfmadd231ps\t32(%rsi,%r12,4), %ymm2, %ymm1\n\tleal\t-32(%rbx), %ebp\n\tmovslq\t%ebp, %r12\n\tleal\t1(%rax), %ebp\n\tmovslq\t%ebp, %rbp\n\tmovslq\t(%rcx,%rbp,4), %rbp\n\tvbroadcastss\t(%rdx,%rbp,4), %ymm2\n\tvfmadd231ps\t32(%rsi,%r12,4), %ymm2, %ymm1\n\tvfmadd231ps\t(%rsi,%r12,4), %ymm2, %ymm0\n\tleal\t-16(%rbx), %ebp\n\tmovslq\t%ebp, %r12\n\tleal\t2(%rax), %ebp\n\tmovslq\t%ebp, %rbp\n\tmovslq\t(%rcx,%rbp,4), %rbp\n\tvbroadcastss\t(%rdx,%rbp,4), %ymm2\n\tvfmadd231ps\t(%rsi,%r12,4), %ymm2, %ymm0\n\tvfmadd231ps\t32(%rsi,%r12,4), %ymm2, %ymm1\n\tmovslq\t%ebx, %rbx\n\taddl\t$3, %eax\n\tcltq\n\tmovslq\t(%rcx,%rax,4), %rax\n\tvbroadcastss\t(%rdx,%rax,4), %ymm2\n\tvfmadd231ps\t32(%rsi,%rbx,4), %ymm2, %ymm1\n\tvfmadd231ps\t(%rsi,%rbx,4), %ymm2, %ymm0\n\taddq\t$4, %r15\n\taddl\t$64, %ebx\n\tcmpq\t%r15, %r14\n\tjne\tLBB1_14\n\ttestq\t%r11, %r11\n\tje\tLBB1_18\nLBB1_16:\n\taddl\t%r9d, %r15d\n\tmovl\t%r15d, %ebx\n\tshll\t$4, %ebx\n\t.p2align\t4, 0x90\nLBB1_17:\n\tmovslq\t%ebx, %rbx\n\tmovslq\t%r15d, %r15\n\tmovslq\t(%rcx,%r15,4), %rax\n\tvbroadcastss\t(%rdx,%rax,4), %ymm2\n\tvfmadd231ps\t32(%rsi,%rbx,4), %ymm2, %ymm1\n\tvfmadd231ps\t(%rsi,%rbx,4), %ymm2, %ymm0\n\taddl\t$1, %r15d\n\taddl\t$16, %ebx\n\taddq\t$-1, %r11\n\tjne\tLBB1_17\nLBB1_18:\n\tvmovaps\t%ymm0, 64(%r8)\n\tvmovaps\t%ymm1, 96(%r8)\n\tmovl\t12(%rdi), %r11d\n\tmovl\t%r11d, %eax\n\tsubl\t%r10d, %eax\n\tvxorps\t%xmm0, %xmm0, %xmm0\n\ttestl\t%eax, %eax\n\tjle\tLBB1_19\n\tmovl\t%eax, %r14d\n\tleaq\t-1(%r14), %rax\n\tmovl\t%r14d, %r9d\n\tandl\t$3, %r9d\n\tcmpq\t$3, %rax\n\tjae\tLBB1_22\n\tvxorps\t%xmm1, %xmm1, %xmm1\n\txorl\t%r15d, %r15d\n\tvxorps\t%xmm2, %xmm2, %xmm2\n\ttestq\t%r9, %r9\n\tjne\tLBB1_25\n\tjmp\tLBB1_27\nLBB1_22:\n\tmovl\t%r10d, %ebx\n\tshll\t$4, %ebx\n\taddl\t$48, %ebx\n\tsubq\t%r9, %r14\n\tvxorps\t%xmm1, %xmm1, %xmm1\n\txorl\t%r15d, %r15d\n\tvxorps\t%xmm2, %xmm2, %xmm2\n\t.p2align\t4, 0x90\nLBB1_23:\n\tleal\t-48(%rbx), %eax\n\tmovslq\t%eax, %r12\n\tleaq\t(%r10,%r15), %rax\n\tcltq\n\tmovslq\t(%rcx,%rax,4), %rbp\n\tvbroadcastss\t(%rdx,%rbp,4), %ymm3\n\tvfmadd231ps\t(%rsi,%r12,4), %ymm3, %ymm1\n\tvfmadd231ps\t32(%rsi,%r12,4), %ymm3, %ymm2\n\tleal\t-32(%rbx), %ebp\n\tmovslq\t%ebp, %r12\n\tleal\t1(%rax), %ebp\n\tmovslq\t%ebp, %rbp\n\tmovslq\t(%rcx,%rbp,4), %rbp\n\tvbroadcastss\t(%rdx,%rbp,4), %ymm3\n\tvfmadd231ps\t32(%rsi,%r12,4), %ymm3, %ymm2\n\tvfmadd231ps\t(%rsi,%r12,4), %ymm3, %ymm1\n\tleal\t-16(%rbx), %ebp\n\tmovslq\t%ebp, %r12\n\tleal\t2(%rax), %ebp\n\tmovslq\t%ebp, %rbp\n\tmovslq\t(%rcx,%rbp,4), %rbp\n\tvbroadcastss\t(%rdx,%rbp,4), %ymm3\n\tvfmadd231ps\t(%rsi,%r12,4), %ymm3, %ymm1\n\tvfmadd231ps\t32(%rsi,%r12,4), %ymm3, %ymm2\n\tmovslq\t%ebx, %rbx\n\taddl\t$3, %eax\n\tcltq\n\tmovslq\t(%rcx,%rax,4), %rax\n\tvbroadcastss\t(%rdx,%rax,4), %ymm3\n\tvfmadd231ps\t32(%rsi,%rbx,4), %ymm3, %ymm2\n\tvfmadd231ps\t(%rsi,%rbx,4), %ymm3, %ymm1\n\taddq\t$4, %r15\n\taddl\t$64, %ebx\n\tcmpq\t%r15, %r14\n\tjne\tLBB1_23\n\ttestq\t%r9, %r9\n\tje\tLBB1_27\nLBB1_25:\n\taddl\t%r10d, %r15d\n\tmovl\t%r15d, %ebx\n\tshll\t$4, %ebx\n\t.p2align\t4, 0x90\nLBB1_26:\n\tmovslq\t%ebx, %rbx\n\tmovslq\t%r15d, %r15\n\tmovslq\t(%rcx,%r15,4), %rax\n\tvbroadcastss\t(%rdx,%rax,4), %ymm3\n\tvfmadd231ps\t32(%rsi,%rbx,4), %ymm3, %ymm2\n\tvfmadd231ps\t(%rsi,%rbx,4), %ymm3, %ymm1\n\taddl\t$1, %r15d\n\taddl\t$16, %ebx\n\taddq\t$-1, %r9\n\tjne\tLBB1_26\nLBB1_27:\n\tvmovaps\t%ymm1, 128(%r8)\n\tvmovaps\t%ymm2, 160(%r8)\n\tmovl\t16(%rdi), %eax\n\tsubl\t%r11d, %eax\n\ttestl\t%eax, %eax\n\tjle\tLBB1_28\n\tmovl\t%eax, %r10d\n\tleaq\t-1(%r10), %rax\n\tmovl\t%r10d, %r9d\n\tandl\t$3, %r9d\n\tcmpq\t$3, %rax\n\tjae\tLBB1_31\n\tvxorps\t%xmm0, %xmm0, %xmm0\n\txorl\t%r14d, %r14d\n\tvxorps\t%xmm1, %xmm1, %xmm1\n\ttestq\t%r9, %r9\n\tjne\tLBB1_34\n\tjmp\tLBB1_36\nLBB1_31:\n\tmovl\t%r11d, %ebx\n\tshll\t$4, %ebx\n\taddl\t$48, %ebx\n\tsubq\t%r9, %r10\n\tvxorps\t%xmm0, %xmm0, %xmm0\n\txorl\t%r14d, %r14d\n\tvxorps\t%xmm1, %xmm1, %xmm1\n\t.p2align\t4, 0x90\nLBB1_32:\n\tleal\t-48(%rbx), %edi\n\tmovslq\t%edi, %rbp\n\tleaq\t(%r11,%r14), %rdi\n\tmovslq\t%edi, %rdi\n\tmovslq\t(%rcx,%rdi,4), %rax\n\tvbroadcastss\t(%rdx,%rax,4), %ymm2\n\tvfmadd231ps\t(%rsi,%rbp,4), %ymm2, %ymm0\n\tvfmadd231ps\t32(%rsi,%rbp,4), %ymm2, %ymm1\n\tleal\t-32(%rbx), %eax\n\tcltq\n\tleal\t1(%rdi), %ebp\n\tmovslq\t%ebp, %rbp\n\tmovslq\t(%rcx,%rbp,4), %rbp\n\tvbroadcastss\t(%rdx,%rbp,4), %ymm2\n\tvfmadd231ps\t32(%rsi,%rax,4), %ymm2, %ymm1\n\tvfmadd231ps\t(%rsi,%rax,4), %ymm2, %ymm0\n\tleal\t-16(%rbx), %eax\n\tcltq\n\tleal\t2(%rdi), %ebp\n\tmovslq\t%ebp, %rbp\n\tmovslq\t(%rcx,%rbp,4), %rbp\n\tvbroadcastss\t(%rdx,%rbp,4), %ymm2\n\tvfmadd231ps\t(%rsi,%rax,4), %ymm2, %ymm0\n\tvfmadd231ps\t32(%rsi,%rax,4), %ymm2, %ymm1\n\tmovslq\t%ebx, %rbx\n\taddl\t$3, %edi\n\tmovslq\t%edi, %rax\n\tmovslq\t(%rcx,%rax,4), %rax\n\tvbroadcastss\t(%rdx,%rax,4), %ymm2\n\tvfmadd231ps\t32(%rsi,%rbx,4), %ymm2, %ymm1\n\tvfmadd231ps\t(%rsi,%rbx,4), %ymm2, %ymm0\n\taddq\t$4, %r14\n\taddl\t$64, %ebx\n\tcmpq\t%r14, %r10\n\tjne\tLBB1_32\n\ttestq\t%r9, %r9\n\tje\tLBB1_36\nLBB1_34:\n\taddl\t%r11d, %r14d\n\tmovl\t%r14d, %edi\n\tshll\t$4, %edi\n\t.p2align\t4, 0x90\nLBB1_35:\n\tmovslq\t%edi, %rdi\n\tmovslq\t%r14d, %r14\n\tmovslq\t(%rcx,%r14,4), %rax\n\tvbroadcastss\t(%rdx,%rax,4), %ymm2\n\tvfmadd231ps\t32(%rsi,%rdi,4), %ymm2, %ymm1\n\tvfmadd231ps\t(%rsi,%rdi,4), %ymm2, %ymm0\n\taddl\t$1, %r14d\n\taddl\t$16, %edi\n\taddq\t$-1, %r9\n\tjne\tLBB1_35\nLBB1_36:\n\tvmovaps\t%ymm0, 192(%r8)\n\tvmovaps\t%ymm1, 224(%r8)\n\tpopq\t%rbx\n\tpopq\t%r12\n\tpopq\t%r13\n\tpopq\t%r14\n\tpopq\t%r15\n\tpopq\t%rbp\n\tvzeroupper\n\tretq\nLBB1_1:\n\tvxorps\t%xmm1, %xmm1, %xmm1\n\tvxorps\t%xmm2, %xmm2, %xmm2\n\tjmp\tLBB1_7\nLBB1_8:\n\tvxorps\t%xmm1, %xmm1, %xmm1\n\tjmp\tLBB1_18\nLBB1_19:\n\tvxorps\t%xmm1, %xmm1, %xmm1\n\tvxorps\t%xmm2, %xmm2, %xmm2\n\tjmp\tLBB1_27\nLBB1_28:\n\tvxorps\t%xmm1, %xmm1, %xmm1\n\tjmp\tLBB1_36\nLfunc_end1:\n\t.cfi_endproc\n\n\t.section\t__DATA,__data\n\t.globl\t___TVMAPISetLastError\n\t.weak_definition\t___TVMAPISetLastError\n\t.p2align\t3\n___TVMAPISetLastError:\n\t.quad\t0\n\n\t.section\t__TEXT,__const\nl_.str:\n\t.asciz\t\"Assert fail: (num_args == 5), default_function: num_args should be 5\"\n\nl_.str.1:\n\t.asciz\t\"Assert fail: (((1 == int32(arg0.strides[2])) && (1 == int32(arg0.strides[1]))) && (16 == int32(arg0.strides[0]))), arg0.strides: expected to be compact array\"\n\nl_.str.2:\n\t.asciz\t\"Assert fail: (1 == int32(arg1.strides[0])), arg1.strides: expected to be compact array\"\n\nl_.str.3:\n\t.asciz\t\"Assert fail: (1 == int32(arg2.strides[0])), arg2.strides: expected to be compact array\"\n\nl_.str.4:\n\t.asciz\t\"Assert fail: ((1 == int32(arg3.strides[1])) && (64 == int32(arg3.strides[0]))), arg3.strides: expected to be compact array\"\n\nl_.str.5:\n\t.asciz\t\"Assert fail: ((1 == int32(arg4.strides[1])) && (64 == int32(arg4.strides[0]))), arg4.strides: expected to be compact array\"\n\nl_.str.6:\n\t.asciz\t\"Assert fail: ((((arg0.code == 3) || (arg0.code == 13)) || (arg0.code == 7)) || (arg0.code == 4)), default_function: Expect arg[0] to be pointer\"\n\nl_.str.7:\n\t.asciz\t\"Assert fail: ((((arg1.code == 3) || (arg1.code == 13)) || (arg1.code == 7)) || (arg1.code == 4)), default_function: Expect arg[1] to be pointer\"\n\nl_.str.8:\n\t.asciz\t\"Assert fail: ((((arg2.code == 3) || (arg2.code == 13)) || (arg2.code == 7)) || (arg2.code == 4)), default_function: Expect arg[2] to be pointer\"\n\nl_.str.9:\n\t.asciz\t\"Assert fail: ((((arg3.code == 3) || (arg3.code == 13)) || (arg3.code == 7)) || (arg3.code == 4)), default_function: Expect arg[3] to be pointer\"\n\nl_.str.10:\n\t.asciz\t\"Assert fail: ((((arg4.code == 3) || (arg4.code == 13)) || (arg4.code == 7)) || (arg4.code == 4)), default_function: Expect arg[4] to be pointer\"\n\nl_.str.11:\n\t.asciz\t\"Assert fail: (dev_type == 1), device_type need to be 1\"\n\nl_.str.12:\n\t.asciz\t\"Assert fail: (3 == tvm_struct_get(arg0, 0, 4)), arg0.ndim is expected to equal 3\"\n\nl_.str.13:\n\t.asciz\t\"Assert fail: (((tvm_struct_get(arg0, 0, 5) == (uint8)2) && (tvm_struct_get(arg0, 0, 6) == (uint8)32)) && (tvm_struct_get(arg0, 0, 7) == (uint16)1)), arg0.dtype is expected to be float32\"\n\nl_.str.14:\n\t.asciz\t\"Assert fail: (13 == int32(arg0.shape[0])), Argument arg0.shape[0] has an unsatisfied constraint\"\n\nl_.str.15:\n\t.asciz\t\"Assert fail: (16 == int32(arg0.shape[1])), Argument arg0.shape[1] has an unsatisfied constraint\"\n\nl_.str.16:\n\t.asciz\t\"Assert fail: (1 == int32(arg0.shape[2])), Argument arg0.shape[2] has an unsatisfied constraint\"\n\nl_.str.17:\n\t.asciz\t\"Assert fail: ((uint64)0 == tvm_struct_get(arg0, 0, 8)), Argument arg0.byte_offset has an unsatisfied constraint\"\n\nl_.str.18:\n\t.asciz\t\"Assert fail: (1 == tvm_struct_get(arg1, 0, 4)), arg1.ndim is expected to equal 1\"\n\nl_.str.19:\n\t.asciz\t\"Assert fail: (((tvm_struct_get(arg1, 0, 5) == (uint8)0) && (tvm_struct_get(arg1, 0, 6) == (uint8)32)) && (tvm_struct_get(arg1, 0, 7) == (uint16)1)), arg1.dtype is expected to be int32\"\n\nl_.str.20:\n\t.asciz\t\"Assert fail: (13 == int32(arg1.shape[0])), Argument arg1.shape[0] has an unsatisfied constraint\"\n\nl_.str.21:\n\t.asciz\t\"Assert fail: ((uint64)0 == tvm_struct_get(arg1, 0, 8)), Argument arg1.byte_offset has an unsatisfied constraint\"\n\nl_.str.22:\n\t.asciz\t\"Assert fail: (1 == tvm_struct_get(arg1, 0, 10)), Argument arg1.device_type has an unsatisfied constraint\"\n\nl_.str.23:\n\t.asciz\t\"Assert fail: (dev_id == tvm_struct_get(arg1, 0, 9)), Argument arg1.device_id has an unsatisfied constraint\"\n\nl_.str.24:\n\t.asciz\t\"Assert fail: (1 == tvm_struct_get(arg2, 0, 4)), arg2.ndim is expected to equal 1\"\n\nl_.str.25:\n\t.asciz\t\"Assert fail: (((tvm_struct_get(arg2, 0, 5) == (uint8)0) && (tvm_struct_get(arg2, 0, 6) == (uint8)32)) && (tvm_struct_get(arg2, 0, 7) == (uint16)1)), arg2.dtype is expected to be int32\"\n\nl_.str.26:\n\t.asciz\t\"Assert fail: (5 == int32(arg2.shape[0])), Argument arg2.shape[0] has an unsatisfied constraint\"\n\nl_.str.27:\n\t.asciz\t\"Assert fail: ((uint64)0 == tvm_struct_get(arg2, 0, 8)), Argument arg2.byte_offset has an unsatisfied constraint\"\n\nl_.str.28:\n\t.asciz\t\"Assert fail: (1 == tvm_struct_get(arg2, 0, 10)), Argument arg2.device_type has an unsatisfied constraint\"\n\nl_.str.29:\n\t.asciz\t\"Assert fail: (dev_id == tvm_struct_get(arg2, 0, 9)), Argument arg2.device_id has an unsatisfied constraint\"\n\nl_.str.30:\n\t.asciz\t\"Assert fail: (2 == tvm_struct_get(arg3, 0, 4)), arg3.ndim is expected to equal 2\"\n\nl_.str.31:\n\t.asciz\t\"Assert fail: (((tvm_struct_get(arg3, 0, 5) == (uint8)2) && (tvm_struct_get(arg3, 0, 6) == (uint8)32)) && (tvm_struct_get(arg3, 0, 7) == (uint16)1)), arg3.dtype is expected to be float32\"\n\nl_.str.32:\n\t.asciz\t\"Assert fail: (1 == int32(arg3.shape[0])), Argument arg3.shape[0] has an unsatisfied constraint\"\n\nl_.str.33:\n\t.asciz\t\"Assert fail: (64 == int32(arg3.shape[1])), Argument arg3.shape[1] has an unsatisfied constraint\"\n\nl_.str.34:\n\t.asciz\t\"Assert fail: ((uint64)0 == tvm_struct_get(arg3, 0, 8)), Argument arg3.byte_offset has an unsatisfied constraint\"\n\nl_.str.35:\n\t.asciz\t\"Assert fail: (1 == tvm_struct_get(arg3, 0, 10)), Argument arg3.device_type has an unsatisfied constraint\"\n\nl_.str.36:\n\t.asciz\t\"Assert fail: (dev_id == tvm_struct_get(arg3, 0, 9)), Argument arg3.device_id has an unsatisfied constraint\"\n\nl_.str.37:\n\t.asciz\t\"Assert fail: (2 == tvm_struct_get(arg4, 0, 4)), arg4.ndim is expected to equal 2\"\n\nl_.str.38:\n\t.asciz\t\"Assert fail: (((tvm_struct_get(arg4, 0, 5) == (uint8)2) && (tvm_struct_get(arg4, 0, 6) == (uint8)32)) && (tvm_struct_get(arg4, 0, 7) == (uint16)1)), arg4.dtype is expected to be float32\"\n\nl_.str.39:\n\t.asciz\t\"Assert fail: (1 == int32(arg4.shape[0])), Argument arg4.shape[0] has an unsatisfied constraint\"\n\nl_.str.40:\n\t.asciz\t\"Assert fail: (64 == int32(arg4.shape[1])), Argument arg4.shape[1] has an unsatisfied constraint\"\n\nl_.str.41:\n\t.asciz\t\"Assert fail: ((uint64)0 == tvm_struct_get(arg4, 0, 8)), Argument arg4.byte_offset has an unsatisfied constraint\"\n\nl_.str.42:\n\t.asciz\t\"Assert fail: (1 == tvm_struct_get(arg4, 0, 10)), Argument arg4.device_type has an unsatisfied constraint\"\n\nl_.str.43:\n\t.asciz\t\"Assert fail: (dev_id == tvm_struct_get(arg4, 0, 9)), Argument arg4.device_id has an unsatisfied constraint\"\n\n\t.globl\t___tvm_main__\n\t.weak_definition\t___tvm_main__\n___tvm_main__:\n\t.asciz\t\"default_function\"\n\n\t.section\t__DWARF,__debug_str,regular,debug\nLinfo_string:\n\t.asciz\t\"TVM\"\n\t.asciz\t\"model.tvm\"\n\t.asciz\t\"/tmp/\"\n\t.section\t__DWARF,__debug_abbrev,regular,debug\nLsection_abbrev:\n\t.byte\t1\n\t.byte\t17\n\t.byte\t0\n\t.byte\t37\n\t.byte\t14\n\t.byte\t19\n\t.byte\t5\n\t.byte\t3\n\t.byte\t14\n\t.byte\t16\n\t.byte\t6\n\t.byte\t27\n\t.byte\t14\n\t.ascii\t\"\\261B\"\n\t.byte\t7\n\t.byte\t0\n\t.byte\t0\n\t.byte\t0\n\t.section\t__DWARF,__debug_info,regular,debug\nLsection_info:\nLcu_begin0:\n\t.long\t34\n\t.short\t2\n.set Lset0, Lsection_abbrev-Lsection_abbrev\n\t.long\tLset0\n\t.byte\t8\n\t.byte\t1\n\t.long\t0\n\t.short\t2\n\t.long\t4\n.set Lset1, Lline_table_start0-Lsection_line\n\t.long\tLset1\n\t.long\t14\n\t.quad\t1\n\t.section\t__DWARF,__debug_macinfo,regular,debug\nLdebug_macinfo:\n\t.byte\t0\n\t.section\t__DWARF,__apple_names,regular,debug\nLnames_begin:\n\t.long\t1212240712\n\t.short\t1\n\t.short\t0\n\t.long\t1\n\t.long\t0\n\t.long\t12\n\t.long\t0\n\t.long\t1\n\t.short\t1\n\t.short\t6\n\t.long\t-1\n\t.section\t__DWARF,__apple_objc,regular,debug\nLobjc_begin:\n\t.long\t1212240712\n\t.short\t1\n\t.short\t0\n\t.long\t1\n\t.long\t0\n\t.long\t12\n\t.long\t0\n\t.long\t1\n\t.short\t1\n\t.short\t6\n\t.long\t-1\n\t.section\t__DWARF,__apple_namespac,regular,debug\nLnamespac_begin:\n\t.long\t1212240712\n\t.short\t1\n\t.short\t0\n\t.long\t1\n\t.long\t0\n\t.long\t12\n\t.long\t0\n\t.long\t1\n\t.short\t1\n\t.short\t6\n\t.long\t-1\n\t.section\t__DWARF,__apple_types,regular,debug\nLtypes_begin:\n\t.long\t1212240712\n\t.short\t1\n\t.short\t0\n\t.long\t1\n\t.long\t0\n\t.long\t20\n\t.long\t0\n\t.long\t3\n\t.short\t1\n\t.short\t6\n\t.short\t3\n\t.short\t5\n\t.short\t4\n\t.short\t11\n\t.long\t-1\n\n.subsections_via_symbols\n\t.section\t__DWARF,__debug_line,regular,debug\nLsection_line:\nLline_table_start0:\n\n" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "## Plotting " | |
}, | |
{ | |
"metadata": { | |
"trusted": false | |
}, | |
"cell_type": "code", | |
"source": "def bsr_matvec_nt_nonspec(X, WS):\n N, K = WS.shape\n R, C = WS.blocksize\n assert C == 1\n indices = WS.indices\n indptr = WS.indptr\n data = WS.data\n assert data.dtype == np.float32\n assert X.dtype == np.float32\n\n import copy\n WS_tvm_ph = copy.copy(WS)\n WS_tvm_ph.data = tvm.placeholder(WS.data.shape,\n dtype=\"float32\",\n name=\"WS.data\")\n WS_tvm_ph.indices = tvm.placeholder(WS.indices.shape,\n dtype=str(WS.indices.dtype),\n name=\"WS.indices\")\n WS_tvm_ph.indptr = tvm.placeholder(WS.indptr.shape,\n dtype=str(WS.indptr.dtype),\n name=\"WS.indptr\")\n X_tvm_ph = tvm.placeholder(X.shape, dtype=str(X.dtype), name=\"X\")\n Y_tvm = sparse_dense_bsrmv(X_tvm_ph, WS_tvm_ph.data, WS_tvm_ph.indices,\n WS_tvm_ph.indptr)\n s = schedule_sparse_dense([Y_tvm])\n\n # print(tvm.lower(s, [WS_tvm_ph.data, WS_tvm_ph.indices, WS_tvm_ph.indptr, X_tvm_ph, Y_tvm], simple_mode=True))\n\n with tvm.target.create(\"llvm -mcpu=core-avx2\"):\n func = tvm.build(\n s,\n [\n WS_tvm_ph.data, WS_tvm_ph.indices, WS_tvm_ph.indptr, X_tvm_ph,\n Y_tvm\n ],\n )\n Y_tvm = tvm.ndarray.empty(Y_tvm.shape, Y_tvm.dtype)\n func(tvm.ndarray.array(WS.data), tvm.ndarray.array(WS.indices),\n tvm.ndarray.array(WS.indptr), tvm.ndarray.array(X), Y_tvm)\n\n ftimer = func.time_evaluator(func.entry_name,\n tvm.cpu(0),\n min_repeat_ms=5000,\n repeat=5)\n\n fte = lambda: ftimer(tvm.ndarray.array(\n WS.data), tvm.ndarray.array(WS.indices), tvm.ndarray.array(WS.indptr),\n tvm.ndarray.array(X), Y_tvm)\n return fte\n\n\nresults = []\nfor D in [64, 128, 256, 512, 1024, 2048]:\n for BS in [8, 16, 32]:\n break\n WS = random_bsr_matrix(D, D, BS, 1, density=density, dtype=\"float32\")\n X = np.random.randn(1, D).astype(np.float32)\n fte_nonspec = bsr_matvec_nt_nonspec(X, WS)\n result_nonspec = fte_nonspec()\n\n YZ, fte_cg, f = bsr_matvec_nt_codegen(X,\n WS,\n prefetch_interval=32,\n prefetch_length=32)\n result_cg = fte_cg()\n\n results.append((D, BS, density, result_cg, result_nonspec))\n print(results[-1])", | |
"execution_count": 13, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"trusted": false | |
}, | |
"cell_type": "code", | |
"source": "import collections\nProfileResult = collections.namedtuple('ProfileResult', ['mean', 'results'])\n\nresults = [\n (64, 8, 0.05,\n ProfileResult(mean=2.8057043991308957e-08,\n results=(2.8375043990438866e-08, 2.8122109088692635e-08,\n 2.7897528949005842e-08, 2.89378091723891e-08,\n 2.695272875601834e-08)),\n ProfileResult(mean=5.799666425548255e-08,\n results=(5.399258559832989e-08, 5.7191092064625664e-08,\n 5.7149598939101355e-08, 6.688643537771347e-08,\n 5.476360929764233e-08))),\n (64, 16, 0.05,\n ProfileResult(mean=2.4589582166053305e-08,\n results=(2.4055254278393056e-08, 2.3911940064301256e-08,\n 2.7070461461465394e-08, 2.4030305151554988e-08,\n 2.3879949874551823e-08)),\n ProfileResult(mean=4.508654073968378e-08,\n results=(4.413898297927291e-08, 4.875300609071139e-08,\n 4.408557342730776e-08, 4.4155596756312455e-08,\n 4.4299544444814376e-08))),\n (64, 32, 0.05,\n ProfileResult(mean=2.3649035803590934e-08,\n results=(2.3625186155646355e-08, 2.3560933682200313e-08,\n 2.3654738068347414e-08, 2.3672505534818304e-08,\n 2.3731815576942265e-08)),\n ProfileResult(mean=3.7793973602817224e-08,\n results=(3.7307214457196456e-08, 3.7554773974775374e-08,\n 3.7038328425086524e-08, 3.6858364718564144e-08,\n 4.0211186438463616e-08))),\n (128, 8, 0.05,\n ProfileResult(mean=7.232666099455331e-08,\n results=(7.18243720624044e-08, 7.188429454054778e-08,\n 7.15950832071703e-08, 7.214063351773944e-08,\n 7.418892164490462e-08)),\n ProfileResult(mean=1.3427689415173067e-07,\n results=(1.306551216832417e-07, 1.3002523837963322e-07,\n 1.3099189289430672e-07, 1.4855193766232413e-07,\n 1.3116028013914748e-07))),\n (128, 16, 0.05,\n ProfileResult(mean=5.7580843461463276e-08,\n results=(5.640377680314214e-08, 5.7004973157951264e-08,\n 5.615736694640355e-08, 6.229776490133481e-08,\n 5.60403354984846e-08)),\n ProfileResult(mean=9.318681910188585e-08,\n results=(1.0019170895303044e-07, 9.178403652597825e-08,\n 9.205525221755188e-08, 9.145806552756289e-08,\n 9.04450322853058e-08))),\n (128, 32, 0.05,\n ProfileResult(mean=4.386527803255476e-08,\n results=(4.3163248975770686e-08, 4.324973584024568e-08,\n 4.3987326449026605e-08, 4.43346671102114e-08,\n 4.4591411787519414e-08)),\n ProfileResult(mean=6.39557004564566e-08,\n results=(6.654276577609792e-08, 6.339571989232416e-08,\n 6.349043939521992e-08, 6.305090944365605e-08,\n 6.329866777498492e-08))),\n (256, 8, 0.05,\n ProfileResult(mean=2.6358794183875287e-07,\n results=(2.652035042819355e-07, 2.6217701536224154e-07,\n 2.5503574504121626e-07, 2.6983417719851073e-07,\n 2.656892673098604e-07)),\n ProfileResult(mean=4.5632442402579317e-07,\n results=(4.3860044093052756e-07, 4.3857506801703304e-07,\n 4.902122025828149e-07, 4.5351686865802747e-07,\n 4.607175399405631e-07))),\n (256, 16, 0.05,\n ProfileResult(mean=1.7735156154373666e-07,\n results=(1.7366399575114497e-07, 1.7206069561986062e-07,\n 1.9554694150102533e-07, 1.74160418743762e-07,\n 1.7132575610289039e-07)),\n ProfileResult(mean=3.0692775048904453e-07,\n results=(3.430244759130581e-07, 2.993624206508408e-07,\n 2.98096122183441e-07, 3.04170873966623e-07,\n 2.8998485973125975e-07))),\n (256, 32, 0.05,\n ProfileResult(mean=1.520475613255272e-07,\n results=(1.5536727338860035e-07, 1.466429414996496e-07,\n 1.7781576526340274e-07, 1.4041735184390714e-07,\n 1.399944746320763e-07)),\n ProfileResult(mean=1.7517641182511741e-07,\n results=(1.736405063551862e-07, 1.816880518958681e-07,\n 1.7384288940491851e-07, 1.7316823368213035e-07,\n 1.7354237778748388e-07))),\n (512, 8, 0.05,\n ProfileResult(mean=1.5331339219072618e-06,\n results=(1.4572572320327288e-06, 1.7761731743007142e-06,\n 1.5111525944504435e-06, 1.4597081580695153e-06,\n 1.4613784506829077e-06)),\n ProfileResult(mean=1.8953631032692353e-06,\n results=(1.8743191371249738e-06, 1.9110911916673173e-06,\n 1.891005802626425e-06, 1.9008002439624067e-06,\n 1.899599140965055e-06))),\n (512, 16, 0.05,\n ProfileResult(mean=9.55159698887546e-07,\n results=(9.211542081815214e-07, 9.130478656938943e-07,\n 1.1043859280301361e-06, 9.141259857260063e-07,\n 9.230845068061719e-07)),\n ProfileResult(mean=1.1253241561448827e-06,\n results=(1.102710450444919e-06, 1.1199403904937747e-06,\n 1.0995474809116784e-06, 1.1349924975550282e-06,\n 1.1694299613190129e-06))),\n (512, 32, 0.05,\n ProfileResult(mean=7.556054332748441e-07,\n results=(8.609060463975768e-07, 7.173943658265848e-07,\n 7.28474297257386e-07, 7.288248633045758e-07,\n 7.424275935880969e-07)),\n ProfileResult(mean=8.28100731918283e-07,\n results=(8.648035551591611e-07, 8.031128066768836e-07,\n 8.24620014975304e-07, 8.390323343116441e-07,\n 8.089349484684217e-07))),\n (1024, 8, 0.05,\n ProfileResult(mean=1.011374260554356e-05,\n results=(1.183058338111841e-05, 9.606288921175827e-06,\n 9.724502359138504e-06, 9.693133183002402e-06,\n 9.714205183282659e-06)),\n ProfileResult(mean=8.249239081307669e-06,\n results=(8.115992744185476e-06, 8.33184857370674e-06,\n 8.399380173460033e-06, 8.234962821028854e-06,\n 8.16401109415724e-06))),\n (1024, 16, 0.05,\n ProfileResult(mean=7.557000732183849e-06,\n results=(7.155535945073811e-06, 8.495820816121824e-06,\n 7.190019685191258e-06, 7.2898438522184594e-06,\n 7.653783362313889e-06)),\n ProfileResult(mean=5.195947658660286e-06,\n results=(5.2454504896759826e-06, 5.376700756806176e-06,\n 5.062725027089259e-06, 5.225027587776724e-06,\n 5.069834431953286e-06))),\n (1024, 32, 0.05,\n ProfileResult(mean=5.692024778540755e-06,\n results=(5.416114342468311e-06, 6.619133346472211e-06,\n 5.6545194448478305e-06, 5.398306086758086e-06,\n 5.372050672157334e-06)),\n ProfileResult(mean=4.124337396882822e-06,\n results=(4.286069855359008e-06, 4.226659691928009e-06,\n 4.107551189449551e-06, 4.0846163374909734e-06,\n 3.916789910186568e-06))),\n (2048, 8, 0.05,\n ProfileResult(mean=4.655855748303369e-05,\n results=(5.320585956581384e-05, 4.503298128197983e-05,\n 4.55687323032592e-05, 4.433707296292563e-05,\n 4.464814130118996e-05)),\n ProfileResult(mean=3.5955830355490764e-05,\n results=(3.5958635145364326e-05, 3.59504218228714e-05,\n 3.594030694720906e-05, 3.59609426883288e-05,\n 3.5968845173680226e-05))),\n (2048, 16, 0.05,\n ProfileResult(mean=4.010877913582572e-05,\n results=(3.7981343620087775e-05, 3.761779088749511e-05,\n 3.76555293103698e-05, 3.8201740783927454e-05,\n 4.908749107724843e-05)),\n ProfileResult(mean=2.2481758857157014e-05,\n results=(2.2578622991954424e-05, 2.1805195698544933e-05,\n 2.4099026479832444e-05, 2.1873107307341007e-05,\n 2.2052841808112277e-05))),\n (2048, 32, 0.05,\n ProfileResult(mean=3.166284582729131e-05,\n results=(3.639566350007422e-05, 3.069806377524805e-05,\n 3.086317447277377e-05, 3.051470886950366e-05,\n 2.984261851885683e-05)),\n ProfileResult(mean=2.0883348310360287e-05,\n results=(2.0875226157823936e-05, 2.084681303611048e-05,\n 2.1041677154487167e-05, 2.0640320979135e-05,\n 2.1012704224244858e-05))),\n]", | |
"execution_count": 21, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"trusted": false | |
}, | |
"cell_type": "code", | |
"source": "import matplotlib\nd = []\nfor (D, BS, density, result_cg, result_nonspec) in results:\n d.append(\n dict(D=D,\n BS=BS,\n density=density,\n GFLOPS=(2 * 1 * D * D / result_cg.mean / 10**9),\n method=\"TVM_SPECIALIZED\"))\n d.append(\n dict(D=D,\n BS=BS,\n density=density,\n GFLOPS=(2 * 1 * D * D / result_nonspec.mean / 10**9),\n method=\"TVM_GENERIC\"))", | |
"execution_count": 22, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"scrolled": false, | |
"trusted": false | |
}, | |
"cell_type": "code", | |
"source": "import pandas as pd\nimport altair as alt\nalt.renderers.enable('notebook')\nalt.themes.enable('opaque')", | |
"execution_count": 23, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": "ThemeRegistry.enable('opaque')" | |
}, | |
"execution_count": 23, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"trusted": false | |
}, | |
"cell_type": "code", | |
"source": "df = pd.DataFrame.from_records(d)\ndf['method_blocksize'] = df.apply(\n lambda x: \"{} ({}x1)\".format(x['method'], x['BS']), axis=1)\ndf", | |
"execution_count": 25, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": "<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border=\"1\" class=\"dataframe\">\n <thead>\n <tr style=\"text-align: right;\">\n <th></th>\n <th>BS</th>\n <th>D</th>\n <th>GFLOPS</th>\n <th>density</th>\n <th>method</th>\n <th>method_blocksize</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>0</th>\n <td>8</td>\n <td>64</td>\n <td>291.976589</td>\n <td>0.05</td>\n <td>TVM_SPECIALIZED</td>\n <td>TVM_SPECIALIZED (8x1)</td>\n </tr>\n <tr>\n <th>1</th>\n <td>8</td>\n <td>64</td>\n <td>141.249503</td>\n <td>0.05</td>\n <td>TVM_GENERIC</td>\n <td>TVM_GENERIC (8x1)</td>\n </tr>\n <tr>\n <th>2</th>\n <td>16</td>\n <td>64</td>\n <td>333.149215</td>\n <td>0.05</td>\n <td>TVM_SPECIALIZED</td>\n <td>TVM_SPECIALIZED (16x1)</td>\n </tr>\n <tr>\n <th>3</th>\n <td>16</td>\n <td>64</td>\n <td>181.695022</td>\n <td>0.05</td>\n <td>TVM_GENERIC</td>\n <td>TVM_GENERIC (16x1)</td>\n </tr>\n <tr>\n <th>4</th>\n <td>32</td>\n <td>64</td>\n <td>346.398900</td>\n <td>0.05</td>\n <td>TVM_SPECIALIZED</td>\n <td>TVM_SPECIALIZED (32x1)</td>\n </tr>\n <tr>\n <th>5</th>\n <td>32</td>\n <td>64</td>\n <td>216.754134</td>\n <td>0.05</td>\n <td>TVM_GENERIC</td>\n <td>TVM_GENERIC (32x1)</td>\n </tr>\n <tr>\n <th>6</th>\n <td>8</td>\n <td>128</td>\n <td>453.055617</td>\n <td>0.05</td>\n <td>TVM_SPECIALIZED</td>\n <td>TVM_SPECIALIZED (8x1)</td>\n </tr>\n <tr>\n <th>7</th>\n <td>8</td>\n <td>128</td>\n <td>244.033050</td>\n <td>0.05</td>\n <td>TVM_GENERIC</td>\n <td>TVM_GENERIC (8x1)</td>\n </tr>\n <tr>\n <th>8</th>\n <td>16</td>\n <td>128</td>\n <td>569.078152</td>\n <td>0.05</td>\n <td>TVM_SPECIALIZED</td>\n <td>TVM_SPECIALIZED (16x1)</td>\n </tr>\n <tr>\n <th>9</th>\n <td>16</td>\n <td>128</td>\n <td>351.637714</td>\n <td>0.05</td>\n <td>TVM_GENERIC</td>\n <td>TVM_GENERIC (16x1)</td>\n </tr>\n <tr>\n <th>10</th>\n <td>32</td>\n <td>128</td>\n <td>747.014529</td>\n <td>0.05</td>\n <td>TVM_SPECIALIZED</td>\n <td>TVM_SPECIALIZED (32x1)</td>\n </tr>\n <tr>\n <th>11</th>\n <td>32</td>\n <td>128</td>\n <td>512.354642</td>\n <td>0.05</td>\n <td>TVM_GENERIC</td>\n <td>TVM_GENERIC (32x1)</td>\n </tr>\n <tr>\n <th>12</th>\n <td>8</td>\n <td>256</td>\n <td>497.260987</td>\n <td>0.05</td>\n <td>TVM_SPECIALIZED</td>\n <td>TVM_SPECIALIZED (8x1)</td>\n </tr>\n <tr>\n <th>13</th>\n <td>8</td>\n <td>256</td>\n <td>287.234242</td>\n <td>0.05</td>\n <td>TVM_GENERIC</td>\n <td>TVM_GENERIC (8x1)</td>\n </tr>\n <tr>\n <th>14</th>\n <td>16</td>\n <td>256</td>\n <td>739.051852</td>\n <td>0.05</td>\n <td>TVM_SPECIALIZED</td>\n <td>TVM_SPECIALIZED (16x1)</td>\n </tr>\n <tr>\n <th>15</th>\n <td>16</td>\n <td>256</td>\n <td>427.045126</td>\n <td>0.05</td>\n <td>TVM_GENERIC</td>\n <td>TVM_GENERIC (16x1)</td>\n </tr>\n <tr>\n <th>16</th>\n <td>32</td>\n <td>256</td>\n <td>862.046052</td>\n <td>0.05</td>\n <td>TVM_SPECIALIZED</td>\n <td>TVM_SPECIALIZED (32x1)</td>\n </tr>\n <tr>\n <th>17</th>\n <td>32</td>\n <td>256</td>\n <td>748.228592</td>\n <td>0.05</td>\n <td>TVM_GENERIC</td>\n <td>TVM_GENERIC (32x1)</td>\n </tr>\n <tr>\n <th>18</th>\n <td>8</td>\n <td>512</td>\n <td>341.971430</td>\n <td>0.05</td>\n <td>TVM_SPECIALIZED</td>\n <td>TVM_SPECIALIZED (8x1)</td>\n </tr>\n <tr>\n <th>19</th>\n <td>8</td>\n <td>512</td>\n <td>276.616127</td>\n <td>0.05</td>\n <td>TVM_GENERIC</td>\n <td>TVM_GENERIC (8x1)</td>\n </tr>\n <tr>\n <th>20</th>\n <td>16</td>\n <td>512</td>\n <td>548.900881</td>\n <td>0.05</td>\n <td>TVM_SPECIALIZED</td>\n <td>TVM_SPECIALIZED (16x1)</td>\n </tr>\n <tr>\n <th>21</th>\n <td>16</td>\n <td>512</td>\n <td>465.899534</td>\n <td>0.05</td>\n <td>TVM_GENERIC</td>\n <td>TVM_GENERIC (16x1)</td>\n </tr>\n <tr>\n <th>22</th>\n <td>32</td>\n <td>512</td>\n <td>693.864783</td>\n <td>0.05</td>\n <td>TVM_SPECIALIZED</td>\n <td>TVM_SPECIALIZED (32x1)</td>\n </tr>\n <tr>\n <th>23</th>\n <td>32</td>\n <td>512</td>\n <td>633.121044</td>\n <td>0.05</td>\n <td>TVM_GENERIC</td>\n <td>TVM_GENERIC (32x1)</td>\n </tr>\n <tr>\n <th>24</th>\n <td>8</td>\n <td>1024</td>\n <td>207.356671</td>\n <td>0.05</td>\n <td>TVM_SPECIALIZED</td>\n <td>TVM_SPECIALIZED (8x1)</td>\n </tr>\n <tr>\n <th>25</th>\n <td>8</td>\n <td>1024</td>\n <td>254.223690</td>\n <td>0.05</td>\n <td>TVM_GENERIC</td>\n <td>TVM_GENERIC (8x1)</td>\n </tr>\n <tr>\n <th>26</th>\n <td>16</td>\n <td>1024</td>\n <td>277.511155</td>\n <td>0.05</td>\n <td>TVM_SPECIALIZED</td>\n <td>TVM_SPECIALIZED (16x1)</td>\n </tr>\n <tr>\n <th>27</th>\n <td>16</td>\n <td>1024</td>\n <td>403.612996</td>\n <td>0.05</td>\n <td>TVM_GENERIC</td>\n <td>TVM_GENERIC (16x1)</td>\n </tr>\n <tr>\n <th>28</th>\n <td>32</td>\n <td>1024</td>\n <td>368.436906</td>\n <td>0.05</td>\n <td>TVM_SPECIALIZED</td>\n <td>TVM_SPECIALIZED (32x1)</td>\n </tr>\n <tr>\n <th>29</th>\n <td>32</td>\n <td>1024</td>\n <td>508.482163</td>\n <td>0.05</td>\n <td>TVM_GENERIC</td>\n <td>TVM_GENERIC (32x1)</td>\n </tr>\n <tr>\n <th>30</th>\n <td>8</td>\n <td>2048</td>\n <td>180.173280</td>\n <td>0.05</td>\n <td>TVM_SPECIALIZED</td>\n <td>TVM_SPECIALIZED (8x1)</td>\n </tr>\n <tr>\n <th>31</th>\n <td>8</td>\n <td>2048</td>\n <td>233.303137</td>\n <td>0.05</td>\n <td>TVM_GENERIC</td>\n <td>TVM_GENERIC (8x1)</td>\n </tr>\n <tr>\n <th>32</th>\n <td>16</td>\n <td>2048</td>\n <td>209.146431</td>\n <td>0.05</td>\n <td>TVM_SPECIALIZED</td>\n <td>TVM_SPECIALIZED (16x1)</td>\n </tr>\n <tr>\n <th>33</th>\n <td>16</td>\n <td>2048</td>\n <td>373.129525</td>\n <td>0.05</td>\n <td>TVM_GENERIC</td>\n <td>TVM_GENERIC (16x1)</td>\n </tr>\n <tr>\n <th>34</th>\n <td>32</td>\n <td>2048</td>\n <td>264.935377</td>\n <td>0.05</td>\n <td>TVM_SPECIALIZED</td>\n <td>TVM_SPECIALIZED (32x1)</td>\n </tr>\n <tr>\n <th>35</th>\n <td>32</td>\n <td>2048</td>\n <td>401.688842</td>\n <td>0.05</td>\n <td>TVM_GENERIC</td>\n <td>TVM_GENERIC (32x1)</td>\n </tr>\n </tbody>\n</table>\n</div>", | |
"text/plain": " BS D GFLOPS density method method_blocksize\n0 8 64 291.976589 0.05 TVM_SPECIALIZED TVM_SPECIALIZED (8x1)\n1 8 64 141.249503 0.05 TVM_GENERIC TVM_GENERIC (8x1)\n2 16 64 333.149215 0.05 TVM_SPECIALIZED TVM_SPECIALIZED (16x1)\n3 16 64 181.695022 0.05 TVM_GENERIC TVM_GENERIC (16x1)\n4 32 64 346.398900 0.05 TVM_SPECIALIZED TVM_SPECIALIZED (32x1)\n5 32 64 216.754134 0.05 TVM_GENERIC TVM_GENERIC (32x1)\n6 8 128 453.055617 0.05 TVM_SPECIALIZED TVM_SPECIALIZED (8x1)\n7 8 128 244.033050 0.05 TVM_GENERIC TVM_GENERIC (8x1)\n8 16 128 569.078152 0.05 TVM_SPECIALIZED TVM_SPECIALIZED (16x1)\n9 16 128 351.637714 0.05 TVM_GENERIC TVM_GENERIC (16x1)\n10 32 128 747.014529 0.05 TVM_SPECIALIZED TVM_SPECIALIZED (32x1)\n11 32 128 512.354642 0.05 TVM_GENERIC TVM_GENERIC (32x1)\n12 8 256 497.260987 0.05 TVM_SPECIALIZED TVM_SPECIALIZED (8x1)\n13 8 256 287.234242 0.05 TVM_GENERIC TVM_GENERIC (8x1)\n14 16 256 739.051852 0.05 TVM_SPECIALIZED TVM_SPECIALIZED (16x1)\n15 16 256 427.045126 0.05 TVM_GENERIC TVM_GENERIC (16x1)\n16 32 256 862.046052 0.05 TVM_SPECIALIZED TVM_SPECIALIZED (32x1)\n17 32 256 748.228592 0.05 TVM_GENERIC TVM_GENERIC (32x1)\n18 8 512 341.971430 0.05 TVM_SPECIALIZED TVM_SPECIALIZED (8x1)\n19 8 512 276.616127 0.05 TVM_GENERIC TVM_GENERIC (8x1)\n20 16 512 548.900881 0.05 TVM_SPECIALIZED TVM_SPECIALIZED (16x1)\n21 16 512 465.899534 0.05 TVM_GENERIC TVM_GENERIC (16x1)\n22 32 512 693.864783 0.05 TVM_SPECIALIZED TVM_SPECIALIZED (32x1)\n23 32 512 633.121044 0.05 TVM_GENERIC TVM_GENERIC (32x1)\n24 8 1024 207.356671 0.05 TVM_SPECIALIZED TVM_SPECIALIZED (8x1)\n25 8 1024 254.223690 0.05 TVM_GENERIC TVM_GENERIC (8x1)\n26 16 1024 277.511155 0.05 TVM_SPECIALIZED TVM_SPECIALIZED (16x1)\n27 16 1024 403.612996 0.05 TVM_GENERIC TVM_GENERIC (16x1)\n28 32 1024 368.436906 0.05 TVM_SPECIALIZED TVM_SPECIALIZED (32x1)\n29 32 1024 508.482163 0.05 TVM_GENERIC TVM_GENERIC (32x1)\n30 8 2048 180.173280 0.05 TVM_SPECIALIZED TVM_SPECIALIZED (8x1)\n31 8 2048 233.303137 0.05 TVM_GENERIC TVM_GENERIC (8x1)\n32 16 2048 209.146431 0.05 TVM_SPECIALIZED TVM_SPECIALIZED (16x1)\n33 16 2048 373.129525 0.05 TVM_GENERIC TVM_GENERIC (16x1)\n34 32 2048 264.935377 0.05 TVM_SPECIALIZED TVM_SPECIALIZED (32x1)\n35 32 2048 401.688842 0.05 TVM_GENERIC TVM_GENERIC (32x1)" | |
}, | |
"execution_count": 25, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"trusted": false | |
}, | |
"cell_type": "code", | |
"source": "c = alt.Chart(df)\nc = c.mark_circle(size=60).encode(\n x='D', y='GFLOPS', color='method_blocksize') + c.mark_line(size=1).encode(\n x='D', y='GFLOPS', color='method_blocksize')\nc.save('bs_32.png', scale_factor=4.0)\nc", | |
"execution_count": 26, | |
"outputs": [ | |
{ | |
"data": { | |
"application/javascript": "var spec = {\"config\": {\"background\": \"white\", \"view\": {\"width\": 400, \"height\": 300}, \"mark\": {\"tooltip\": null}}, \"layer\": [{\"mark\": {\"type\": \"circle\", \"size\": 60}, \"encoding\": {\"color\": {\"type\": \"nominal\", \"field\": \"method_blocksize\"}, \"x\": {\"type\": \"quantitative\", \"field\": \"D\"}, \"y\": {\"type\": \"quantitative\", \"field\": \"GFLOPS\"}}}, {\"mark\": {\"type\": \"line\", \"size\": 1}, \"encoding\": {\"color\": {\"type\": \"nominal\", \"field\": \"method_blocksize\"}, \"x\": {\"type\": \"quantitative\", \"field\": \"D\"}, \"y\": {\"type\": \"quantitative\", \"field\": \"GFLOPS\"}}}], \"data\": {\"name\": \"data-5b6e88b46338bb3c2576065899ace7cf\"}, \"$schema\": \"https://vega.github.io/schema/vega-lite/v3.3.0.json\", \"datasets\": {\"data-5b6e88b46338bb3c2576065899ace7cf\": [{\"BS\": 8, \"D\": 64, \"GFLOPS\": 291.9765889285265, \"density\": 0.05, \"method\": \"TVM_SPECIALIZED\", \"method_blocksize\": \"TVM_SPECIALIZED (8x1)\"}, {\"BS\": 8, \"D\": 64, \"GFLOPS\": 141.24950296991594, \"density\": 0.05, \"method\": \"TVM_GENERIC\", \"method_blocksize\": \"TVM_GENERIC (8x1)\"}, {\"BS\": 16, \"D\": 64, \"GFLOPS\": 333.14921517085867, \"density\": 0.05, \"method\": \"TVM_SPECIALIZED\", \"method_blocksize\": \"TVM_SPECIALIZED (16x1)\"}, {\"BS\": 16, \"D\": 64, \"GFLOPS\": 181.6950217427006, \"density\": 0.05, \"method\": \"TVM_GENERIC\", \"method_blocksize\": \"TVM_GENERIC (16x1)\"}, {\"BS\": 32, \"D\": 64, \"GFLOPS\": 346.3989004894696, \"density\": 0.05, \"method\": \"TVM_SPECIALIZED\", \"method_blocksize\": \"TVM_SPECIALIZED (32x1)\"}, {\"BS\": 32, \"D\": 64, \"GFLOPS\": 216.75413350527808, \"density\": 0.05, \"method\": \"TVM_GENERIC\", \"method_blocksize\": \"TVM_GENERIC (32x1)\"}, {\"BS\": 8, \"D\": 128, \"GFLOPS\": 453.0556166897799, \"density\": 0.05, \"method\": \"TVM_SPECIALIZED\", \"method_blocksize\": \"TVM_SPECIALIZED (8x1)\"}, {\"BS\": 8, \"D\": 128, \"GFLOPS\": 244.03304981847958, \"density\": 0.05, \"method\": \"TVM_GENERIC\", \"method_blocksize\": \"TVM_GENERIC (8x1)\"}, {\"BS\": 16, \"D\": 128, \"GFLOPS\": 569.0781522144671, \"density\": 0.05, \"method\": \"TVM_SPECIALIZED\", \"method_blocksize\": \"TVM_SPECIALIZED (16x1)\"}, {\"BS\": 16, \"D\": 128, \"GFLOPS\": 351.63771352870293, \"density\": 0.05, \"method\": \"TVM_GENERIC\", \"method_blocksize\": \"TVM_GENERIC (16x1)\"}, {\"BS\": 32, \"D\": 128, \"GFLOPS\": 747.0145287961273, \"density\": 0.05, \"method\": \"TVM_SPECIALIZED\", \"method_blocksize\": \"TVM_SPECIALIZED (32x1)\"}, {\"BS\": 32, \"D\": 128, \"GFLOPS\": 512.3546418244557, \"density\": 0.05, \"method\": \"TVM_GENERIC\", \"method_blocksize\": \"TVM_GENERIC (32x1)\"}, {\"BS\": 8, \"D\": 256, \"GFLOPS\": 497.2609865446042, \"density\": 0.05, \"method\": \"TVM_SPECIALIZED\", \"method_blocksize\": \"TVM_SPECIALIZED (8x1)\"}, {\"BS\": 8, \"D\": 256, \"GFLOPS\": 287.23424190985514, \"density\": 0.05, \"method\": \"TVM_GENERIC\", \"method_blocksize\": \"TVM_GENERIC (8x1)\"}, {\"BS\": 16, \"D\": 256, \"GFLOPS\": 739.051851921114, \"density\": 0.05, \"method\": \"TVM_SPECIALIZED\", \"method_blocksize\": \"TVM_SPECIALIZED (16x1)\"}, {\"BS\": 16, \"D\": 256, \"GFLOPS\": 427.0451263893731, \"density\": 0.05, \"method\": \"TVM_GENERIC\", \"method_blocksize\": \"TVM_GENERIC (16x1)\"}, {\"BS\": 32, \"D\": 256, \"GFLOPS\": 862.046052283473, \"density\": 0.05, \"method\": \"TVM_SPECIALIZED\", \"method_blocksize\": \"TVM_SPECIALIZED (32x1)\"}, {\"BS\": 32, \"D\": 256, \"GFLOPS\": 748.2285921625803, \"density\": 0.05, \"method\": \"TVM_GENERIC\", \"method_blocksize\": \"TVM_GENERIC (32x1)\"}, {\"BS\": 8, \"D\": 512, \"GFLOPS\": 341.9714302242892, \"density\": 0.05, \"method\": \"TVM_SPECIALIZED\", \"method_blocksize\": \"TVM_SPECIALIZED (8x1)\"}, {\"BS\": 8, \"D\": 512, \"GFLOPS\": 276.6161265330515, \"density\": 0.05, \"method\": \"TVM_GENERIC\", \"method_blocksize\": \"TVM_GENERIC (8x1)\"}, {\"BS\": 16, \"D\": 512, \"GFLOPS\": 548.9008807748348, \"density\": 0.05, \"method\": \"TVM_SPECIALIZED\", \"method_blocksize\": \"TVM_SPECIALIZED (16x1)\"}, {\"BS\": 16, \"D\": 512, \"GFLOPS\": 465.8995340472361, \"density\": 0.05, \"method\": \"TVM_GENERIC\", \"method_blocksize\": \"TVM_GENERIC (16x1)\"}, {\"BS\": 32, \"D\": 512, \"GFLOPS\": 693.8647830094352, \"density\": 0.05, \"method\": \"TVM_SPECIALIZED\", \"method_blocksize\": \"TVM_SPECIALIZED (32x1)\"}, {\"BS\": 32, \"D\": 512, \"GFLOPS\": 633.1210440854154, \"density\": 0.05, \"method\": \"TVM_GENERIC\", \"method_blocksize\": \"TVM_GENERIC (32x1)\"}, {\"BS\": 8, \"D\": 1024, \"GFLOPS\": 207.3566711941538, \"density\": 0.05, \"method\": \"TVM_SPECIALIZED\", \"method_blocksize\": \"TVM_SPECIALIZED (8x1)\"}, {\"BS\": 8, \"D\": 1024, \"GFLOPS\": 254.2236901282245, \"density\": 0.05, \"method\": \"TVM_GENERIC\", \"method_blocksize\": \"TVM_GENERIC (8x1)\"}, {\"BS\": 16, \"D\": 1024, \"GFLOPS\": 277.5111547983081, \"density\": 0.05, \"method\": \"TVM_SPECIALIZED\", \"method_blocksize\": \"TVM_SPECIALIZED (16x1)\"}, {\"BS\": 16, \"D\": 1024, \"GFLOPS\": 403.6129956976368, \"density\": 0.05, \"method\": \"TVM_GENERIC\", \"method_blocksize\": \"TVM_GENERIC (16x1)\"}, {\"BS\": 32, \"D\": 1024, \"GFLOPS\": 368.43690630202065, \"density\": 0.05, \"method\": \"TVM_SPECIALIZED\", \"method_blocksize\": \"TVM_SPECIALIZED (32x1)\"}, {\"BS\": 32, \"D\": 1024, \"GFLOPS\": 508.48216287664275, \"density\": 0.05, \"method\": \"TVM_GENERIC\", \"method_blocksize\": \"TVM_GENERIC (32x1)\"}, {\"BS\": 8, \"D\": 2048, \"GFLOPS\": 180.1732797038842, \"density\": 0.05, \"method\": \"TVM_SPECIALIZED\", \"method_blocksize\": \"TVM_SPECIALIZED (8x1)\"}, {\"BS\": 8, \"D\": 2048, \"GFLOPS\": 233.3031365723692, \"density\": 0.05, \"method\": \"TVM_GENERIC\", \"method_blocksize\": \"TVM_GENERIC (8x1)\"}, {\"BS\": 16, \"D\": 2048, \"GFLOPS\": 209.1464307999138, \"density\": 0.05, \"method\": \"TVM_SPECIALIZED\", \"method_blocksize\": \"TVM_SPECIALIZED (16x1)\"}, {\"BS\": 16, \"D\": 2048, \"GFLOPS\": 373.12952484273745, \"density\": 0.05, \"method\": \"TVM_GENERIC\", \"method_blocksize\": \"TVM_GENERIC (16x1)\"}, {\"BS\": 32, \"D\": 2048, \"GFLOPS\": 264.93537712171053, \"density\": 0.05, \"method\": \"TVM_SPECIALIZED\", \"method_blocksize\": \"TVM_SPECIALIZED (32x1)\"}, {\"BS\": 32, \"D\": 2048, \"GFLOPS\": 401.6888420061638, \"density\": 0.05, \"method\": \"TVM_GENERIC\", \"method_blocksize\": \"TVM_GENERIC (32x1)\"}]}};\nvar opt = {};\nvar type = \"vega-lite\";\nvar id = \"f0599c53-1dc3-4fa9-9559-e780877eb6fb\";\n\nvar output_area = this;\n\nrequire([\"nbextensions/jupyter-vega/index\"], function(vega) {\n var target = document.createElement(\"div\");\n target.id = id;\n target.className = \"vega-embed\";\n\n var style = document.createElement(\"style\");\n style.textContent = [\n \".vega-embed .error p {\",\n \" color: firebrick;\",\n \" font-size: 14px;\",\n \"}\",\n ].join(\"\\\\n\");\n\n // element is a jQuery wrapped DOM element inside the output area\n // see http://ipython.readthedocs.io/en/stable/api/generated/\\\n // IPython.display.html#IPython.display.Javascript.__init__\n element[0].appendChild(target);\n element[0].appendChild(style);\n\n vega.render(\"#\" + id, spec, type, opt, output_area);\n}, function (err) {\n if (err.requireType !== \"scripterror\") {\n throw(err);\n }\n});\n", | |
"text/plain": "<vega.vegalite.VegaLite at 0x124c52cc0>" | |
}, | |
"metadata": { | |
"jupyter-vega": "#f0599c53-1dc3-4fa9-9559-e780877eb6fb" | |
}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/plain": "" | |
}, | |
"execution_count": 26, | |
"metadata": {}, | |
"output_type": "execute_result" | |
}, | |
{ | |
"data": { | |
"image/png": "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" | |
}, | |
"metadata": { | |
"jupyter-vega": "#f0599c53-1dc3-4fa9-9559-e780877eb6fb" | |
}, | |
"output_type": "display_data" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "## Experiments with BSR specialization + loops" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "### Basic implementation" | |
}, | |
{ | |
"metadata": { | |
"scrolled": false, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "# def bsr_matvec_nt(X, WS):\n# N, K = WS.shape\n# BS_R, BS_C = WS.blocksize\n\n# indices = WS.indices\n# indptr = WS.indptr\n# data = WS.data\n\n# Y = np.zeros((X.shape[0], N))\n# REGISTERS = 8\n# assert N % (REGISTERS * BS_R) == 0\n# for nb in range(0, N, BS_R * REGISTERS):\n# for register in range(REGISTERS):\n# n_idx = nb + BS_R * register\n# for jj in range(indptr[n_idx // BS_R], indptr[n_idx // BS_R + 1]):\n# j = indices[jj]\n# block_ij = data[jj]\n# for r in range(BS_R):\n# for c in range(BS_C):\n# Y[0, n_idx + r] += block_ij[r, c] * X[0, BS_C * j + c]\n\n# return Y\n\n# def bsr_matvec_nt(X, WS):\n# N, K = WS.shape\n# BS_R, BS_C = WS.blocksize\n\n# indices = WS.indices\n# indptr = WS.indptr\n# data = WS.data\n# import pprint\n \n# Y = np.zeros((X.shape[0], N))\n# REGISTERS = 8\n# assert N % (REGISTERS * BS_R) == 0\n# for nb in range(0, N, BS_R * REGISTERS):\n# items_per_register = [0 for _ in range(REGISTERS)]\n# for register in range(REGISTERS):\n# n_idx = nb + BS_R * register\n# items_for_n_idx = indptr[n_idx // BS_R + 1] - indptr[n_idx // BS_R]\n# items_per_register[register] = items_for_n_idx\n# #pprint.pprint(items_per_register)\n# accs = [[0 for _ in range(BS_R)] for _ in range(REGISTERS)]\n# for register in range(REGISTERS):\n# n_idx = nb + BS_R * register\n# for jj in range(indptr[n_idx // BS_R], indptr[n_idx // BS_R + 1]):\n# j = indices[jj]\n# block_ij = data[jj]\n# for r in range(BS_R):\n# for c in range(BS_C):\n# accs[register][r] += block_ij[r, c] * X[0, BS_C * j + c]\n# for register in range(REGISTERS):\n# n_idx = nb + BS_R * register\n# for r in range(BS_R):\n# Y[0, n_idx + r] = accs[register][r]\n\n# return Y\n\n\ndef bsr_matvec_nt(X, WS, REGISTERS=8):\n N, K = WS.shape\n BS_R, BS_C = WS.blocksize\n\n indices = WS.indices\n indptr = WS.indptr\n data = WS.data\n import pprint\n \n Y = np.zeros((X.shape[0], N))\n assert N % (REGISTERS * BS_R) == 0\n for nb in range(0, N, BS_R * REGISTERS):\n items_per_register = {register: 0 for register in range(REGISTERS)}\n for register in range(REGISTERS):\n n_idx = nb + BS_R * register\n items_for_n_idx = indptr[n_idx // BS_R + 1] - indptr[n_idx // BS_R]\n items_per_register[register] = items_for_n_idx\n # pprint.pprint(items_per_register)\n accs = [[0 for _ in range(BS_R)] for _ in range(REGISTERS)]\n # Now, we do the following.\n # Create a loop from 0 to the minimum number of active registers remaining.\n # In the loop body, do one work-item for each active register.\n # At loop finalization, flush all registers that have zero items left.\n # Repeat until there are no items_per_register.\n items_done = 0\n while max(items_per_register.values()) > 0:\n items_to_do = min(v for r, v in items_per_register.items() if v > 0)\n active_registers = [r for r, v in items_per_register.items() if v > 0]\n print(items_to_do, active_registers)\n\n for item in range(items_to_do):\n for register in active_registers:\n n_idx = nb + BS_R * register\n jj = indptr[n_idx // BS_R] + items_done + item\n j = indices[jj]\n for r in range(BS_R):\n accs[register][r] += data[jj, r, 0] * X[0, BS_C * j]\n items_done += items_to_do\n \n for register in active_registers:\n items_per_register[register] = items_per_register[register] - items_to_do\n #print(\"Register\", register, items_per_register[register])\n\n if items_per_register[register] == 0:\n n_idx = nb + BS_R * register\n for r in range(BS_R):\n Y[0, n_idx + r] = accs[register][r]\n return Y\n\nN = 1024\nK = 1024\nBS = 16\nWS = random_bsr_matrix(N, K, BS, 1, density=density, dtype=\"float32\")\nX = np.random.randn(1, K).astype(np.float32)\nW = WS.todense()\n# W = np.random.randn(N, K)\n# X = np.random.randn(1, K)\n# WS = sp.bsr_matrix(W, blocksize=(BS, 1))\n# np.testing.assert_almost_equal(WS.todense(), W)\nY = X.dot(W.T)\nYZ = bsr_matvec_nt(X, WS, REGISTERS=32)\n\nnp.testing.assert_allclose(Y, YZ, rtol=1e-5, atol=1e-5)", | |
"execution_count": 52, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": "34 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]\n6 [0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]\n3 [0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 26, 27, 28, 29, 30, 31]\n2 [0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 26, 27, 28, 29, 30, 31]\n1 [0, 1, 2, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 26, 27, 28, 29, 31]\n1 [0, 1, 2, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 21, 22, 23, 24, 26, 27, 28, 29, 31]\n1 [1, 2, 5, 7, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 21, 22, 23, 24, 26, 27, 28, 29, 31]\n1 [1, 2, 5, 7, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 21, 22, 24, 26, 27, 29, 31]\n1 [1, 2, 5, 7, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 21, 24, 26, 29, 31]\n1 [1, 2, 5, 7, 8, 11, 13, 14, 15, 16, 17, 18, 21, 24, 26, 29, 31]\n1 [1, 2, 5, 7, 11, 13, 14, 15, 16, 17, 18, 21, 24, 26, 29, 31]\n1 [1, 2, 5, 7, 11, 13, 14, 15, 17, 18, 21, 24, 26, 29, 31]\n2 [5, 7, 11, 13, 14, 15, 18, 24, 26, 29, 31]\n1 [5, 7, 13, 14, 18, 24, 26, 29, 31]\n2 [7, 13, 14, 18, 26, 29]\n1 [7, 13, 14, 26, 29]\n1 [7, 14, 26, 29]\n5 [7, 14, 26]\n1 [14, 26]\n1 [14]\n38 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]\n7 [0, 1, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]\n1 [0, 1, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 29, 30, 31]\n1 [0, 1, 5, 8, 9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 29, 30, 31]\n1 [0, 1, 5, 8, 9, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 26, 29, 30, 31]\n1 [0, 1, 9, 11, 12, 13, 15, 17, 18, 19, 20, 21, 22, 23, 24, 26, 29, 30, 31]\n1 [0, 1, 9, 11, 13, 15, 17, 18, 19, 20, 21, 22, 23, 24, 26, 29, 30, 31]\n2 [0, 1, 9, 11, 13, 15, 18, 19, 20, 21, 22, 23, 26, 30, 31]\n1 [0, 1, 9, 11, 13, 15, 18, 19, 21, 22, 23, 26, 30, 31]\n1 [0, 1, 9, 11, 13, 18, 19, 22, 23, 30, 31]\n1 [0, 1, 9, 19, 22, 23, 31]\n1 [1, 9, 19, 22, 23, 31]\n1 [1, 9, 19, 22, 31]\n1 [1, 19, 22, 31]\n1 [19, 22, 31]\n1 [19, 22]\n4 [19]\n", | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "### Arbitary register size nest" | |
}, | |
{ | |
"metadata": { | |
"trusted": false | |
}, | |
"cell_type": "code", | |
"source": "import pprint\ndef bsr_matvec_nt_codegen(X, WS, REGISTERS=8, prefetch_length=None, prefetch_interval=None):\n N, K = WS.shape\n R, C = WS.blocksize\n assert C == 1\n indices = WS.indices\n indptr = WS.indptr\n data = WS.data\n import tvm\n vecty = 'float32x{R}'.format(R=R)\n\n def tvm_bsr_codegen(ins, outs):\n ib = tvm.ir_builder.create()\n assert N % R == 0\n N_vec = (N // (R * REGISTERS)) * (R * REGISTERS)\n print(N_vec)\n for nb in range(0, N_vec, R * REGISTERS):\n items_per_register = {register: 0 for register in range(REGISTERS)}\n for register in range(REGISTERS):\n n_idx = nb + R * register\n items_for_n_idx = indptr[n_idx // R + 1] - indptr[n_idx // R]\n items_per_register[register] = items_for_n_idx\n # pprint.pprint(items_per_register)\n accs = [tvm.const(0, vecty) for _ in range(REGISTERS)]\n # Now, we do the following.\n # Create a loop from 0 to the minimum number of active registers remaining.\n # In the loop body, do one work-item for each active register.\n # At loop finalization, flush all registers that have zero items left.\n # Repeat until there are no items_per_register.\n items_done = 0\n for register in [r for r, v in items_per_register.items() if v == 0]:\n n_idx = nb + R * register\n ib.emit(outs[0].vstore([0, n_idx], accs[register]))\n \n while max(items_per_register.values()) > 0:\n # print(items_per_register)\n items_to_do = min(v for r, v in items_per_register.items() if v > 0)\n active_registers = [r for r, v in items_per_register.items() if v > 0]\n for item in range(items_to_do):\n for register in active_registers:\n n_idx = nb + R * register\n jj = indptr[n_idx // R] + items_done + item\n j = indices[jj]\n x_k = ins[0].vload([0, C * j], 'float32').astype(vecty)\n w_nk_vec = ins[1].vload([jj, 0, 0], vecty)\n accs[register] += x_k * w_nk_vec\n items_done += items_to_do\n\n for register in active_registers:\n items_per_register[register] -= items_to_do\n if items_per_register[register] == 0:\n n_idx = nb + R * register\n ib.emit(outs[0].vstore([0, n_idx], accs[register]))\n \n for nb in range(N_vec, N, R):\n acc = tvm.const(0, vecty)\n for jj in range(indptr[nb // R], indptr[nb // R + 1]):\n j = indices[jj]\n x_k = ins[0].vload([0, C * j], 'float32').astype(vecty)\n w_nk_vec = ins[1].vload([jj, 0, 0], vecty)\n acc += x_k * w_nk_vec\n ib.emit(outs[0].vstore([0, nb], acc))\n return ib.get()\n \n Xtvm = tvm.placeholder(X.shape, name=\"X\", dtype=str(X.dtype))\n WSdatatvm = tvm.placeholder(WS.data.shape,\n name=\"WS.data\",\n dtype=str(WS.data.dtype))\n Ytvm = tvm.extern((1, N), [Xtvm, WSdatatvm], tvm_bsr_codegen)\n s = tvm.create_schedule(Ytvm.op)\n f = tvm.build(s, [Xtvm, WSdatatvm, Ytvm], \"llvm -mcpu=core-avx2\")\n Xnd = tvm.nd.array(X)\n WSdata_nd = tvm.nd.array(WS.data)\n Ynd = tvm.nd.array(np.zeros((1, N)).astype(np.float32))\n f(Xnd, WSdata_nd, Ynd)\n te = f.time_evaluator(f.entry_name,\n ctx=tvm.cpu(0),\n repeat=5,\n min_repeat_ms=5000)\n fte = lambda: te(Xnd, WSdata_nd, Ynd)\n return np.array(Ynd.asnumpy()), fte, f\n\nN = 1024\nK = 1024\nBS = 16\n\nWS = random_bsr_matrix(N, K, BS, 1, density=density, dtype=\"float32\")\nX = np.random.randn(1, K).astype(np.float32)\nYS = WS.dot(X.T).T\n\nfor registers in [12]:\n YZ, fte, f = bsr_matvec_nt_codegen(X,\n WS,\n REGISTERS=registers)\n np.testing.assert_allclose(YS, YZ, atol=1e-5, rtol=1e-5)\n", | |
"execution_count": 12, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": "960\n960\n976\n992\n1008\n" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "### Variable loop only max iteration" | |
}, | |
{ | |
"metadata": { | |
"scrolled": true, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "import pprint\ndef bsr_matvec_nt_codegen(X, WS, REGISTERS=8, prefetch_length=None, prefetch_interval=None):\n N, K = WS.shape\n R, C = WS.blocksize\n assert C == 1\n indices = WS.indices\n indptr = WS.indptr\n data = WS.data\n import tvm\n vecty = 'float32x{R}'.format(R=R)\n\n indptr_reads = np.copy(indptr)\n indices_reads = np.copy(indices)\n def tvm_bsr_codegen(ins, outs):\n ib = tvm.ir_builder.create()\n assert N % (REGISTERS * R) == 0\n for nb in range(0, N, R * REGISTERS):\n items_per_register = {register: 0 for register in range(REGISTERS)}\n for register in range(REGISTERS):\n n_idx = nb + R * register\n items_for_n_idx = indptr[n_idx // R + 1] - indptr[n_idx // R]\n items_per_register[register] = items_for_n_idx\n # pprint.pprint(items_per_register)\n accs = [tvm.const(0, vecty) for _ in range(REGISTERS)]\n # Now, we do the following.\n # Create a loop from 0 to the minimum number of active registers remaining.\n # In the loop body, do one work-item for each active register.\n # At loop finalization, flush all registers that have zero items left.\n # Repeat until there are no items_per_register.\n items_done = 0\n variable_items_to_do = min(v for r, v in items_per_register.items() if v > 0)\n variable_active_registers = [r for r, v in items_per_register.items() if v > 0]\n with ib.for_range(0, variable_items_to_do) as item:\n for register in variable_active_registers:\n n_idx = nb + R * register\n # jj = indptr[n_idx // BS_R] + items_done + item\n # j = indices[jj]\n jj = indptr[n_idx // R] + items_done + item\n j = ins[2].vload([jj], 'int32')\n x_k = ins[0].vload([0, C * j], 'float32').astype(vecty)\n w_nk_vec = ins[1].vload([jj, 0, 0], vecty)\n accs[register] += x_k * w_nk_vec\n ib.emit(outs[0].vstore([0, n_idx], outs[0].vload([0, n_idx], vecty) + accs[register]))\n accs[register] = tvm.const(0, vecty)\n \n items_done += variable_items_to_do\n for register in variable_active_registers:\n items_per_register[register] -= variable_items_to_do\n \n while max(items_per_register.values()) > 0:\n # print(items_per_register)\n items_to_do = min(v for r, v in items_per_register.items() if v > 0)\n active_registers = [r for r, v in items_per_register.items() if v > 0]\n for item in range(items_to_do):\n for register in active_registers:\n n_idx = nb + R * register\n jj = indptr[n_idx // R] + items_done + item\n j = indices[jj]\n x_k = ins[0].vload([0, C * j], 'float32').astype(vecty)\n w_nk_vec = ins[1].vload([jj, 0, 0], vecty)\n accs[register] += x_k * w_nk_vec\n items_done += items_to_do\n\n for register in active_registers:\n items_per_register[register] -= items_to_do\n if items_per_register[register] == 0:\n n_idx = nb + R * register\n ib.emit(outs[0].vstore([0, n_idx], outs[0].vload([0, n_idx], vecty) + accs[register])) \n ir = ib.get()\n # print(ir)\n return ir\n\n \n Xtvm = tvm.placeholder(X.shape, name=\"X\", dtype=str(X.dtype))\n WSdatatvm = tvm.placeholder(WS.data.shape,\n name=\"WS.data\",\n dtype=str(WS.data.dtype))\n WSindicestvm = tvm.placeholder(WS.indices.shape,\n name=\"WS.indices\",\n dtype=str(WS.indices.dtype))\n \n Ytvm = tvm.extern((1, N), [Xtvm, WSdatatvm, WSindicestvm], tvm_bsr_codegen, dtype=WSdatatvm.dtype)\n s = tvm.create_schedule(Ytvm.op)\n # print(tvm.lower(s, [Xtvm, WSdatatvm, WSindicestvm, Ytvm], simple_mode=True))\n \n f = tvm.build(s, [Xtvm, WSdatatvm, WSindicestvm, Ytvm], \"llvm -mcpu=core-avx2\")\n Xnd = tvm.nd.array(X)\n WSdata_nd = tvm.nd.array(WS.data)\n WSindices_nd = tvm.nd.array(WS.indices)\n Ynd = tvm.nd.array(np.zeros((1, N)).astype(np.float32))\n f(Xnd, WSdata_nd, WSindices_nd, Ynd)\n te = f.time_evaluator(f.entry_name,\n ctx=tvm.cpu(0),\n repeat=5,\n min_repeat_ms=5000)\n fte = lambda: te(Xnd, WSdata_nd, WSindices_nd, Ynd)\n return np.array(Ynd.asnumpy()), fte, f\n\nN = 1024\nK = 1024\nBS = 16\n\nnp.random.seed(42)\nWS = random_bsr_matrix(N, K, BS, 1, density=density, dtype=\"float32\")\n# WS.data[:] = 1.0\nX = np.random.randn(1, K).astype(np.float32)\n# X[:] = 1.0\nYS = WS.dot(X.T).T\n\nfor registers in [1, 2, 3, 4, 5, 6, 7, 8]:\n YZ, fte, f = bsr_matvec_nt_codegen(X,\n WS,\n REGISTERS=registers)\n np.testing.assert_allclose(YS, YZ, atol=1e-5, rtol=1e-5)\n # print(f.get_source(\"asm\"))\n result = fte()\n print(\n \"N: {N}, K: {K}, BS: {BS}x1, Registers: {registers}, t: {t:.3}, TVM Sparsity-Spec GFLOP/s: {GFLOPs:.3}\"\n .format(N=N,\n K=K,\n BS=BS,\n registers=registers,\n t=result.mean,\n GFLOPs=2 * N * K / result.mean / 10**9))", | |
"execution_count": 92, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": "N: 1024, K: 1024, BS: 16x1, Registers: 1, t: 4.43e-06, TVM Sparsity-Spec GFLOP/s: 4.73e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 2, t: 3.97e-06, TVM Sparsity-Spec GFLOP/s: 5.29e+02\n", | |
"name": "stdout" | |
}, | |
{ | |
"output_type": "error", | |
"ename": "AssertionError", | |
"evalue": "", | |
"traceback": [ | |
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", | |
"\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)", | |
"\u001b[0;32m<ipython-input-92-d34c9400e722>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 111\u001b[0m YZ, fte, f = bsr_matvec_nt_codegen(X,\n\u001b[1;32m 112\u001b[0m \u001b[0mWS\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 113\u001b[0;31m REGISTERS=registers)\n\u001b[0m\u001b[1;32m 114\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtesting\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0massert_allclose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mYS\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mYZ\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0matol\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1e-5\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrtol\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1e-5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 115\u001b[0m \u001b[0;31m# print(f.get_source(\"asm\"))\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", | |
"\u001b[0;32m<ipython-input-92-d34c9400e722>\u001b[0m in \u001b[0;36mbsr_matvec_nt_codegen\u001b[0;34m(X, WS, REGISTERS, prefetch_length, prefetch_interval)\u001b[0m\n\u001b[1;32m 80\u001b[0m dtype=str(WS.indices.dtype))\n\u001b[1;32m 81\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 82\u001b[0;31m \u001b[0mYtvm\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtvm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mextern\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mN\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mXtvm\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mWSdatatvm\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mWSindicestvm\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtvm_bsr_codegen\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mWSdatatvm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdtype\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 83\u001b[0m \u001b[0ms\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtvm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcreate_schedule\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mYtvm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mop\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 84\u001b[0m \u001b[0;31m# print(tvm.lower(s, [Xtvm, WSdatatvm, WSindicestvm, Ytvm], simple_mode=True))\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", | |
"\u001b[0;32m~/src/tvm/python/tvm/api.py\u001b[0m in \u001b[0;36mextern\u001b[0;34m(shape, inputs, fcompute, name, dtype, in_buffers, out_buffers, tag, attrs)\u001b[0m\n\u001b[1;32m 514\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mshp\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdt\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mshape\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdtype\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 515\u001b[0m \u001b[0moutput_placeholders\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdecl_buffer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mshp\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdt\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 516\u001b[0;31m \u001b[0mbody\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfcompute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minput_placeholders\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0moutput_placeholders\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 517\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbody\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_expr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mExpr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 518\u001b[0m \u001b[0mbody\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_make\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mEvaluate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbody\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", | |
"\u001b[0;32m<ipython-input-92-d34c9400e722>\u001b[0m in \u001b[0;36mtvm_bsr_codegen\u001b[0;34m(ins, outs)\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mtvm_bsr_codegen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mins\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mouts\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0mib\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtvm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mir_builder\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcreate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 16\u001b[0;31m \u001b[0;32massert\u001b[0m \u001b[0mN\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mREGISTERS\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mR\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 17\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mnb\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mN\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mR\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mREGISTERS\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0mitems_per_register\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0mregister\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;36m0\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mregister\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mREGISTERS\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", | |
"\u001b[0;31mAssertionError\u001b[0m: " | |
] | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "### Variable loop only max iteration tests with allocate" | |
}, | |
{ | |
"metadata": { | |
"scrolled": false, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "import pprint\ndef bsr_matvec_nt_codegen(X, WS, REGISTERS=8, prefetch_length=None, prefetch_interval=None):\n N, K = WS.shape\n R, C = WS.blocksize\n assert C == 1\n indices = WS.indices\n indptr = WS.indptr\n data = WS.data\n import tvm\n vecty = 'float32x{R}'.format(R=R)\n\n indptr_reads = np.copy(indptr)\n indices_reads = np.copy(indices)\n def tvm_bsr_codegen(ins, outs):\n ib = tvm.ir_builder.create()\n assert N % R == 0\n N_vec = (N // (REGISTERS * R)) * (REGISTERS * R)\n for nb in range(0, N_vec, R * REGISTERS):\n items_per_register = {register: 0 for register in range(REGISTERS)}\n for register in range(REGISTERS):\n n_idx = nb + R * register\n items_for_n_idx = indptr[n_idx // R + 1] - indptr[n_idx // R]\n items_per_register[register] = items_for_n_idx\n # pprint.pprint(items_per_register)\n accs = ib.allocate(vecty, REGISTERS, name=\"ACCS\", scope=\"local\")\n stored = [False for _ in range(REGISTERS)]\n for register in range(REGISTERS):\n accs[register] = tvm.const(0, vecty)\n\n # Now, we do the following.\n # Create a loop from 0 to the minimum number of active registers remaining.\n # In the loop body, do one work-item for each active register.\n # At loop finalization, flush all registers that have zero items left.\n # Repeat until there are no items_per_register.\n items_done = 0\n variable_items_to_do = min(v for r, v in items_per_register.items() if v > 0)\n variable_active_registers = [r for r, v in items_per_register.items() if v > 0]\n with ib.for_range(0, variable_items_to_do) as item:\n for register in variable_active_registers:\n n_idx = nb + R * register\n jj = indptr[n_idx // R] + items_done + item\n j = ins[2].vload([jj], 'int32')\n x_k = ins[0].vload([0, C * j], 'float32').astype(vecty)\n w_nk_vec = ins[1].vload([jj, 0, 0], vecty)\n accs[register] += x_k * w_nk_vec\n \n items_done += variable_items_to_do\n for register in variable_active_registers:\n items_per_register[register] -= variable_items_to_do\n if items_per_register[register] == 0:\n n_idx = nb + R * register\n ib.emit(outs[0].vstore([0, n_idx], accs[register])) \n stored[register] = True\n while max(items_per_register.values()) > 0:\n items_to_do = min(v for r, v in items_per_register.items() if v > 0)\n active_registers = [r for r, v in items_per_register.items() if v > 0]\n for item in range(items_to_do):\n for register in active_registers:\n n_idx = nb + R * register\n jj = indptr[n_idx // R] + items_done + item\n j = indices[jj]\n x_k = ins[0].vload([0, C * j], 'float32').astype(vecty)\n w_nk_vec = ins[1].vload([jj, 0, 0], vecty)\n accs[register] += x_k * w_nk_vec\n items_done += items_to_do\n\n for register in active_registers:\n items_per_register[register] -= items_to_do\n if items_per_register[register] == 0:\n n_idx = nb + R * register\n ib.emit(outs[0].vstore([0, n_idx], accs[register])) \n stored[register] = True\n assert all(stored)\n for nb in range(N_vec, N, R):\n acc = tvm.const(0, vecty)\n for jj in range(indptr[nb // R], indptr[nb // R + 1]):\n j = indices[jj]\n x_k = ins[0].vload([0, C * j], 'float32').astype(vecty)\n w_nk_vec = ins[1].vload([jj, 0, 0], vecty)\n acc += x_k * w_nk_vec\n ib.emit(outs[0].vstore([0, nb], acc))\n\n ir = ib.get()\n # print(ir)\n return ir\n\n \n Xtvm = tvm.placeholder(X.shape, name=\"X\", dtype=str(X.dtype))\n WSdatatvm = tvm.placeholder(WS.data.shape,\n name=\"WS.data\",\n dtype=str(WS.data.dtype))\n WSindicestvm = tvm.placeholder(WS.indices.shape,\n name=\"WS.indices\",\n dtype=str(WS.indices.dtype))\n \n Ytvm = tvm.extern((1, N), [Xtvm, WSdatatvm, WSindicestvm], tvm_bsr_codegen, dtype=WSdatatvm.dtype)\n s = tvm.create_schedule(Ytvm.op)\n # print(tvm.lower(s, [Xtvm, WSdatatvm, WSindicestvm, Ytvm], simple_mode=True))\n \n f = tvm.build(s, [Xtvm, WSdatatvm, WSindicestvm, Ytvm], \"llvm -mcpu=core-avx2\")\n Xnd = tvm.nd.array(X)\n WSdata_nd = tvm.nd.array(WS.data)\n WSindices_nd = tvm.nd.array(WS.indices)\n Ynd = tvm.nd.array(np.zeros((1, N)).astype(np.float32))\n f(Xnd, WSdata_nd, WSindices_nd, Ynd)\n te = f.time_evaluator(f.entry_name,\n ctx=tvm.cpu(0),\n repeat=5,\n min_repeat_ms=5000)\n fte = lambda: te(Xnd, WSdata_nd, WSindices_nd, Ynd)\n return np.array(Ynd.asnumpy()), fte, f\n\nN = 1024\nK = 1024\nBS = 16\n\nnp.random.seed(42)\nWS = random_bsr_matrix(N, K, BS, 1, density=density, dtype=\"float32\")\n# WS.data[:] = 1.0\nX = np.random.randn(1, K).astype(np.float32)\n# X[:] = 1.0\nYS = WS.dot(X.T).T\n\n# for registers in [2]:\n# YZ, fte, f = bsr_matvec_nt_codegen(X,\n# WS,\n# REGISTERS=registers)\n# prefetch_interval = 0\n# prefetch_length = 0\n# np.testing.assert_allclose(YS, YZ, atol=1e-5, rtol=1e-5)\n# print(f.get_source(\"asm\"))\n# result = fte()\n# print(\n# \"N: {N}, K: {K}, BS: {BS}x1, Registers: {registers}, t: {t:.3}, TVM Sparsity-Spec GFLOP/s: {GFLOPs:.3}\"\n# .format(N=N,\n# K=K,\n# BS=BS,\n# registers=registers,\n# t=result.mean,\n# GFLOPs=2 * N * K / result.mean / 10**9))\n \nfor registers in [1, 2, 3, 4, 5, 6, 7, 8]:\n YZ, fte, f = bsr_matvec_nt_codegen(X,\n WS,\n REGISTERS=registers)\n prefetch_interval = 0\n prefetch_length = 0\n np.testing.assert_allclose(YS, YZ, atol=1e-5, rtol=1e-5)\n # print(f.get_source(\"asm\"))\n result = fte()\n print(\n \"N: {N}, K: {K}, BS: {BS}x1, Registers: {registers}, t: {t:.3}, TVM Sparsity-Spec GFLOP/s: {GFLOPs:.3}\"\n .format(N=N,\n K=K,\n BS=BS,\n registers=registers,\n t=result.mean,\n GFLOPs=2 * N * K / result.mean / 10**9))", | |
"execution_count": 93, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": "N: 1024, K: 1024, BS: 16x1, Registers: 1, t: 4.7e-06, TVM Sparsity-Spec GFLOP/s: 4.46e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 2, t: 4.01e-06, TVM Sparsity-Spec GFLOP/s: 5.24e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 3, t: 3.97e-06, TVM Sparsity-Spec GFLOP/s: 5.28e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 4, t: 3.81e-06, TVM Sparsity-Spec GFLOP/s: 5.51e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 5, t: 3.67e-06, TVM Sparsity-Spec GFLOP/s: 5.71e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 6, t: 3.58e-06, TVM Sparsity-Spec GFLOP/s: 5.86e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 7, t: 3.47e-06, TVM Sparsity-Spec GFLOP/s: 6.05e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 8, t: 3.95e-06, TVM Sparsity-Spec GFLOP/s: 5.31e+02\n", | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "### Variable loop, always do variable loop" | |
}, | |
{ | |
"metadata": { | |
"scrolled": true, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "import pprint\ndef bsr_matvec_nt_codegen(X, WS, REGISTERS=8, prefetch_length=None, prefetch_interval=None):\n N, K = WS.shape\n R, C = WS.blocksize\n assert C == 1\n indices = WS.indices\n indptr = WS.indptr\n data = WS.data\n import tvm\n vecty = 'float32x{R}'.format(R=R)\n\n indptr_reads = np.copy(indptr)\n indices_reads = np.copy(indices)\n def tvm_bsr_codegen(ins, outs):\n ib = tvm.ir_builder.create()\n assert N % R == 0\n N_vec = (N // (REGISTERS * R)) * (REGISTERS * R)\n for nb in range(0, N_vec, R * REGISTERS):\n items_per_register = {register: 0 for register in range(REGISTERS)}\n for register in range(REGISTERS):\n n_idx = nb + R * register\n items_for_n_idx = indptr[n_idx // R + 1] - indptr[n_idx // R]\n items_per_register[register] = items_for_n_idx\n # pprint.pprint(items_per_register)\n accs = ib.allocate(vecty, REGISTERS, name=\"ACCS\", scope=\"local\")\n stored = [False for _ in range(REGISTERS)]\n for register in range(REGISTERS):\n accs[register] = tvm.const(0, vecty)\n for register in range(REGISTERS):\n if items_per_register[register] == 0:\n n_idx = nb + R * register\n ib.emit(outs[0].vstore([0, n_idx], accs[register]))\n stored[register] = True\n\n # Now, we do the following.\n # Create a loop from 0 to the minimum number of active registers remaining.\n # In the loop body, do one work-item for each active register.\n # At loop finalization, flush all registers that have zero items left.\n # Repeat until there are no items_per_register.\n items_done = 0\n \n while max(items_per_register.values()) > 0:\n # print(items_per_register)\n items_to_do = min(v for r, v in items_per_register.items() if v > 0)\n active_registers = [r for r, v in items_per_register.items() if v > 0]\n with ib.for_range(0, items_to_do) as item:\n for register in active_registers:\n n_idx = nb + R * register\n jj = indptr[n_idx // R] + items_done + item\n j = ins[2].vload([jj], 'int32')\n x_k = ins[0].vload([0, C * j], 'float32').astype(vecty)\n w_nk_vec = ins[1].vload([jj, 0, 0], vecty)\n accs[register] += x_k * w_nk_vec\n items_done += items_to_do\n for register in active_registers:\n items_per_register[register] -= items_to_do\n if items_per_register[register] == 0:\n n_idx = nb + R * register\n ib.emit(outs[0].vstore([0, n_idx], accs[register]))\n stored[register] = True\n assert all(stored)\n for nb in range(N_vec, N, R):\n acc = tvm.const(0, vecty)\n for jj in range(indptr[nb // R], indptr[nb // R + 1]):\n j = indices[jj]\n x_k = ins[0].vload([0, C * j], 'float32').astype(vecty)\n w_nk_vec = ins[1].vload([jj, 0, 0], vecty)\n acc += x_k * w_nk_vec\n ib.emit(outs[0].vstore([0, nb], acc))\n\n ir = ib.get()\n # print(ir)\n return ir\n\n \n Xtvm = tvm.placeholder(X.shape, name=\"X\", dtype=str(X.dtype))\n WSdatatvm = tvm.placeholder(WS.data.shape,\n name=\"WS.data\",\n dtype=str(WS.data.dtype))\n WSindicestvm = tvm.placeholder(WS.indices.shape,\n name=\"WS.indices\",\n dtype=str(WS.indices.dtype))\n \n Ytvm = tvm.extern((1, N), [Xtvm, WSdatatvm, WSindicestvm], tvm_bsr_codegen, dtype=WSdatatvm.dtype)\n s = tvm.create_schedule(Ytvm.op)\n # print(tvm.lower(s, [Xtvm, WSdatatvm, WSindicestvm, Ytvm], simple_mode=True))\n \n f = tvm.build(s, [Xtvm, WSdatatvm, WSindicestvm, Ytvm], \"llvm -mcpu=core-avx2\")\n Xnd = tvm.nd.array(X)\n WSdata_nd = tvm.nd.array(WS.data)\n WSindices_nd = tvm.nd.array(WS.indices)\n Ynd = tvm.nd.array(np.zeros((1, N)).astype(np.float32))\n f(Xnd, WSdata_nd, WSindices_nd, Ynd)\n te = f.time_evaluator(f.entry_name,\n ctx=tvm.cpu(0),\n repeat=5,\n min_repeat_ms=5000)\n fte = lambda: te(Xnd, WSdata_nd, WSindices_nd, Ynd)\n return np.array(Ynd.asnumpy()), fte, f\n\nN = 1024\nK = 1024\nBS = 16\n\nnp.random.seed(42)\nWS = random_bsr_matrix(N, K, BS, 1, density=density, dtype=\"float32\")\n# WS.data[:] = 1.0\nX = np.random.randn(1, K).astype(np.float32)\n# X[:] = 1.0\nYS = WS.dot(X.T).T\n\n# for registers in [2]:\n# YZ, fte, f = bsr_matvec_nt_codegen(X,\n# WS,\n# REGISTERS=registers)\n# prefetch_interval = 0\n# prefetch_length = 0\n# np.testing.assert_allclose(YS, YZ, atol=1e-5, rtol=1e-5)\n# print(f.get_source(\"asm\"))\n# result = fte()\n# print(\n# \"N: {N}, K: {K}, BS: {BS}x1, Registers: {registers}, t: {t:.3}, TVM Sparsity-Spec GFLOP/s: {GFLOPs:.3}\"\n# .format(N=N,\n# K=K,\n# BS=BS,\n# registers=registers,\n# t=result.mean,\n# GFLOPs=2 * N * K / result.mean / 10**9))\n \n# for registers in [2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20]:\n# YZ, fte, f = bsr_matvec_nt_codegen(X,\n# WS,\n# REGISTERS=registers)\n# prefetch_interval = 0\n# prefetch_length = 0\n# np.testing.assert_allclose(YS, YZ, atol=1e-5, rtol=1e-5)\n# print(f.get_source(\"asm\"))\n# break\n \nfor registers in [1, 2, 3, 4, 5, 6, 7, 8]:\n YZ, fte, f = bsr_matvec_nt_codegen(X,\n WS,\n REGISTERS=registers)\n prefetch_interval = 0\n prefetch_length = 0\n np.testing.assert_allclose(YS, YZ, atol=1e-5, rtol=1e-5)\n #print(f.get_source(\"asm\")) \n result = fte()\n print(\n \"N: {N}, K: {K}, BS: {BS}x1, Registers: {registers}, t: {t:.3}, TVM Sparsity-Spec GFLOP/s: {GFLOPs:.3}\"\n .format(N=N,\n K=K,\n BS=BS,\n registers=registers,\n t=result.mean,\n GFLOPs=2 * N * K / result.mean / 10**9)) ", | |
"execution_count": 94, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": "N: 1024, K: 1024, BS: 16x1, Registers: 1, t: 4.63e-06, TVM Sparsity-Spec GFLOP/s: 4.52e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 2, t: 4.08e-06, TVM Sparsity-Spec GFLOP/s: 5.14e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 3, t: 3.62e-06, TVM Sparsity-Spec GFLOP/s: 5.79e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 4, t: 3.76e-06, TVM Sparsity-Spec GFLOP/s: 5.57e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 5, t: 3.73e-06, TVM Sparsity-Spec GFLOP/s: 5.62e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 6, t: 3.79e-06, TVM Sparsity-Spec GFLOP/s: 5.53e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 7, t: 3.62e-06, TVM Sparsity-Spec GFLOP/s: 5.79e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 8, t: 3.89e-06, TVM Sparsity-Spec GFLOP/s: 5.39e+02\n", | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "### Experiments with optimal partitioning" | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "import matplotlib\n%matplotlib inline \nimport matplotlib.pyplot as plt\n\ndef bsr_matvec_nt(X, WS, REGISTERS=8):\n N, K = WS.shape\n BS_R, BS_C = WS.blocksize\n\n indices = WS.indices\n indptr = WS.indptr\n data = WS.data\n import pprint\n \n Y = np.zeros((X.shape[0], N))\n assert N % (REGISTERS * BS_R) == 0\n for nb in range(0, N, BS_R * REGISTERS):\n items_per_register = {register: 0 for register in range(REGISTERS)}\n for register in range(REGISTERS):\n n_idx = nb + BS_R * register\n items_for_n_idx = indptr[n_idx // BS_R + 1] - indptr[n_idx // BS_R]\n items_per_register[register] = items_for_n_idx\n pprint.pprint(items_per_register)\n sorted_items_per_register = sorted(items_per_register.values())\n plt.bar(range(len(sorted_items_per_register)), sorted_items_per_register)\n break\n return Y\nbsr_matvec_nt(X, WS)\n\n\n# What about the following - we greedily search over theset of active regiseters at each step that maximimzes (# registers * # work done per register), and then do fully unrolled for the tail.", | |
"execution_count": 41, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": "{0: 56, 1: 37, 2: 65, 3: 47, 4: 42, 5: 51, 6: 59, 7: 45}\n", | |
"name": "stdout" | |
}, | |
{ | |
"output_type": "execute_result", | |
"execution_count": 41, | |
"data": { | |
"text/plain": "array([[0., 0., 0., ..., 0., 0., 0.]])" | |
}, | |
"metadata": {} | |
}, | |
{ | |
"output_type": "display_data", | |
"data": { | |
"text/plain": "<Figure size 432x288 with 1 Axes>", | |
"image/png": "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\n" | |
}, | |
"metadata": { | |
"needs_background": "light" | |
} | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"trusted": true, | |
"scrolled": false | |
}, | |
"cell_type": "code", | |
"source": "\n\ndef bsr_matvec_nt(X, WS, REGISTERS=8):\n N, K = WS.shape\n BS_R, BS_C = WS.blocksize\n\n indices = WS.indices\n indptr = WS.indptr\n data = WS.data\n import pprint\n \n Y = np.zeros((X.shape[0], N))\n assert N % (REGISTERS * BS_R) == 0\n for nb in range(0, N, BS_R * REGISTERS):\n items_per_register = [0 for register in range(REGISTERS)]\n for register in range(REGISTERS):\n n_idx = nb + BS_R * register\n items_for_n_idx = indptr[n_idx // BS_R + 1] - indptr[n_idx // BS_R]\n items_per_register[register] = items_for_n_idx\n # pprint.pprint(items_per_register)\n accs = [[0 for _ in range(BS_R)] for _ in range(REGISTERS)]\n # Now, we do the following.\n # Create a loop from 0 to the minimum number of active registers remaining.\n # In the loop body, do one work-item for each active register.\n # At loop finalization, flush all registers that have zero items left.\n # Repeat until there are no items_per_register.\n items_done = [0 for _ in range(REGISTERS)]\n while max(items_per_register) > 0:\n def candidate_active_set(items_per_register, register_list):\n items_to_do = min(items_per_register[r] for r in register_list)\n active_registers = register_list\n return (active_registers, items_to_do)\n\n def score(c):\n (active_registers, items_to_do) = c\n return len(active_registers) * items_to_do\n\n def get_active_set(items_per_register):\n items_per_register = np.asarray(items_per_register)\n sorted_registers_by_items = np.argsort(items_per_register)\n candidates = [candidate_active_set(items_per_register, sorted_registers_by_items[i:]) for i, _ in enumerate(items_per_register)]\n return max(candidates, key=lambda c: score(c))\n \n # Return the 'greedily' locally optimal register set, which is defined as the set that maximizes \n active_registers, items_to_do = get_active_set(items_per_register)\n print(items_to_do, active_registers)\n \n for item in range(items_to_do):\n for register in active_registers:\n n_idx = nb + BS_R * register\n jj = indptr[n_idx // BS_R] + items_done[register] + item\n j = indices[jj]\n block_ij = data[jj]\n for r in range(BS_R):\n accs[register][r] += data[jj, r, 0] * X[0, BS_C * j]\n\n \n \n for register in active_registers:\n items_done[register] += items_to_do\n items_per_register[register] -= items_to_do\n #print(\"Register\", register, items_per_register[register])\n\n if items_per_register[register] == 0:\n n_idx = nb + BS_R * register\n for r in range(BS_R):\n Y[0, n_idx + r] = accs[register][r]\n return Y\n\nN = 1024\nK = 1024\nBS = 16\nWS = random_bsr_matrix(N, K, BS, 1, density=density, dtype=\"float32\")\nX = np.random.randn(1, K).astype(np.float32)\nW = WS.todense()\n# W = np.random.randn(N, K)\n# X = np.random.randn(1, K)\n# WS = sp.bsr_matrix(W, blocksize=(BS, 1))\n# np.testing.assert_almost_equal(WS.todense(), W)\nY = X.dot(W.T)\nYZ = bsr_matvec_nt(X, WS, REGISTERS=32)\n\nnp.testing.assert_allclose(Y, YZ, rtol=1e-5, atol=1e-5)", | |
"execution_count": 77, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": "43 [31 16 22 20 30 11 19 3 12 29 23 6 8 0 9 13 25 1 7 4 28 5 2 24\n 10 21 27 18 15 17]\n7 [29 23 6 8 0 9 13 25 1 4 7 28 5 2 24 10 21 27 18 15 17 14 26]\n4 [ 7 28 19 4 12 5 3 2 24 27 21 10 18 15 17 14 26]\n24 [14 26]\n4 [21 26 27 10 18 15 17]\n1 [22 20 0 30 13 12 9 5 3 2 18 11 24 25 1 15 17]\n1 [18 24 25 11 1 15 17]\n1 [ 1 15 17]\n1 [17]\n43 [21 13 4 17 30 12 15 3 22 23 6 29 24 16 1 28 8 2 27 0 9 11 7 31\n 25 14 26 20 18 10 19]\n9 [28 1 16 8 2 27 0 9 11 7 31 25 20 14 26 18 10 19 5]\n5 [ 3 26 12 15 14 20 23 18 22 6 29 24 10 19 5]\n2 [27 29 0 9 6 11 24 7 31 30 10 25 19 5]\n25 [5]\n1 [ 2 24 23 22 31 18 17 7 8 11 25 30 10 19]\n5 [19]\n1 [25 10 30]\n", | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "WS.data[0, 0, 0]", | |
"execution_count": 60, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"execution_count": 60, | |
"data": { | |
"text/plain": "-0.65214455" | |
}, | |
"metadata": {} | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "def candidate_active_set(items_per_register, register_list):\n items_to_do = min(items_per_register[r] for r in register_list)\n active_registers = register_list\n return (active_registers, items_to_do)\n\ndef score(c):\n (active_registers, items_to_do) = c\n return len(active_registers) * items_to_do\n\ndef get_active_set(items_per_register):\n items_per_register = np.asarray(items_per_register)\n sorted_registers_by_items = np.argsort(items_per_register)\n candidates = [candidate_active_set(items_per_register, sorted_registers_by_items[i:]) for i, _ in enumerate(items_per_register)]\n print(candidates)\n return max(candidates, key=lambda c: score(c))\n\nget_active_set([1, 5, 10, 20])", | |
"execution_count": 74, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": "[(array([0, 1, 2, 3]), 1), (array([1, 2, 3]), 5), (array([2, 3]), 10), (array([3]), 20)]\n", | |
"name": "stdout" | |
}, | |
{ | |
"output_type": "execute_result", | |
"execution_count": 74, | |
"data": { | |
"text/plain": "(array([2, 3]), 10)" | |
}, | |
"metadata": {} | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "import pprint\ndef bsr_matvec_nt_codegen(X, WS, REGISTERS=8, ITEM_LOOP_THRESHOLD=2):\n N, K = WS.shape\n R, C = WS.blocksize\n assert C == 1\n indices = WS.indices\n indptr = WS.indptr\n data = WS.data\n import tvm\n vecty = 'float32x{R}'.format(R=R)\n\n indptr_reads = np.copy(indptr)\n indices_reads = np.copy(indices)\n def tvm_bsr_codegen(ins, outs):\n ib = tvm.ir_builder.create()\n assert N % R == 0\n N_vec = (N // (REGISTERS * R)) * (REGISTERS * R)\n for nb in range(0, N_vec, R * REGISTERS):\n items_per_register = [0 for register in range(REGISTERS)]\n for register in range(REGISTERS):\n n_idx = nb + R * register\n items_for_n_idx = indptr[n_idx // R + 1] - indptr[n_idx // R]\n items_per_register[register] = items_for_n_idx\n # pprint.pprint(items_per_register)\n accs = ib.allocate(vecty, REGISTERS, name=\"ACCS\", scope=\"local\")\n stored = [False for _ in range(REGISTERS)]\n for register in range(REGISTERS):\n accs[register] = tvm.const(0, vecty)\n for register in range(REGISTERS):\n if items_per_register[register] == 0:\n n_idx = nb + R * register\n ib.emit(outs[0].vstore([0, n_idx], accs[register]))\n stored[register] = True\n\n # Now, we do the following.\n # Create a loop from 0 to the minimum number of active registers remaining.\n # In the loop body, do one work-item for each active register.\n # At loop finalization, flush all registers that have zero items left.\n # Repeat until there are no items_per_register.\n items_done = [0 for register in range(REGISTERS)]\n \n while max(items_per_register) > 0:\n def candidate_active_set(items_per_register, register_list):\n items_to_do = min(items_per_register[r] for r in register_list)\n active_registers = register_list\n return (active_registers, items_to_do)\n\n def score(c):\n (active_registers, items_to_do) = c\n return len(active_registers) * items_to_do\n\n def get_active_set(items_per_register):\n items_per_register = np.asarray(items_per_register)\n sorted_registers_by_items = np.argsort(items_per_register)\n candidates = [candidate_active_set(items_per_register, sorted_registers_by_items[i:]) for i, _ in enumerate(items_per_register)]\n return max(candidates, key=lambda c: score(c))\n active_registers, items_to_do = get_active_set(items_per_register)\n #print(items_to_do, active_registers)\n if items_to_do < ITEM_LOOP_THRESHOLD:\n for item in range(items_to_do):\n for register in active_registers:\n n_idx = nb + R * register\n jj = indptr[n_idx // R] + items_done[register] + item\n j = indices[jj]\n x_k = ins[0].vload([0, C * j], 'float32').astype(vecty)\n w_nk_vec = ins[1].vload([jj, 0, 0], vecty)\n accs[register] += x_k * w_nk_vec\n else:\n with ib.for_range(0, items_to_do) as item:\n for register in active_registers:\n n_idx = nb + R * register\n jj = indptr[n_idx // R] + items_done[register] + item\n j = ins[2].vload([jj], 'int32')\n x_k = ins[0].vload([0, C * j], 'float32').astype(vecty)\n w_nk_vec = ins[1].vload([jj, 0, 0], vecty)\n accs[register] += x_k * w_nk_vec\n\n for register in active_registers:\n items_per_register[register] -= items_to_do\n items_done[register] += items_to_do\n\n if items_per_register[register] == 0:\n n_idx = nb + R * register\n ib.emit(outs[0].vstore([0, n_idx], accs[register]))\n stored[register] = True\n assert all(stored)\n for nb in range(N_vec, N, R):\n acc = tvm.const(0, vecty)\n for jj in range(indptr[nb // R], indptr[nb // R + 1]):\n j = indices[jj]\n x_k = ins[0].vload([0, C * j], 'float32').astype(vecty)\n w_nk_vec = ins[1].vload([jj, 0, 0], vecty)\n acc += x_k * w_nk_vec\n ib.emit(outs[0].vstore([0, nb], acc))\n\n ir = ib.get()\n # print(ir)\n return ir\n\n \n Xtvm = tvm.placeholder(X.shape, name=\"X\", dtype=str(X.dtype))\n WSdatatvm = tvm.placeholder(WS.data.shape,\n name=\"WS.data\",\n dtype=str(WS.data.dtype))\n WSindicestvm = tvm.placeholder(WS.indices.shape,\n name=\"WS.indices\",\n dtype=str(WS.indices.dtype))\n \n Ytvm = tvm.extern((1, N), [Xtvm, WSdatatvm, WSindicestvm], tvm_bsr_codegen, dtype=WSdatatvm.dtype)\n s = tvm.create_schedule(Ytvm.op)\n # print(tvm.lower(s, [Xtvm, WSdatatvm, WSindicestvm, Ytvm], simple_mode=True))\n \n f = tvm.build(s, [Xtvm, WSdatatvm, WSindicestvm, Ytvm], \"llvm -mcpu=core-avx2\")\n Xnd = tvm.nd.array(X)\n WSdata_nd = tvm.nd.array(WS.data)\n WSindices_nd = tvm.nd.array(WS.indices)\n Ynd = tvm.nd.array(np.zeros((1, N)).astype(np.float32))\n f(Xnd, WSdata_nd, WSindices_nd, Ynd)\n te = f.time_evaluator(f.entry_name,\n ctx=tvm.cpu(0),\n repeat=5,\n min_repeat_ms=5000)\n fte = lambda: te(Xnd, WSdata_nd, WSindices_nd, Ynd)\n return np.array(Ynd.asnumpy()), fte, f\n\nN = 1024\nK = 1024\nBS = 16\n\nnp.random.seed(42)\nWS = random_bsr_matrix(N, K, BS, 1, density=density, dtype=\"float32\")\n# WS.data[:] = 1.0\nX = np.random.randn(1, K).astype(np.float32)\n# X[:] = 1.0\nYS = WS.dot(X.T).T\n\n \nfor registers in [1, 2, 3, 4, 5, 6, 7, 8]:\n YZ, fte, f = bsr_matvec_nt_codegen(X,\n WS,\n REGISTERS=registers)\n prefetch_interval = 0\n prefetch_length = 0\n np.testing.assert_allclose(YS, YZ, atol=1e-5, rtol=1e-5)\n #print(f.get_source(\"asm\")) \n result = fte()\n print(\n \"N: {N}, K: {K}, BS: {BS}x1, Registers: {registers}, t: {t:.3}, TVM Sparsity-Spec GFLOP/s: {GFLOPs:.3}\"\n .format(N=N,\n K=K,\n BS=BS,\n registers=registers,\n t=result.mean,\n GFLOPs=2 * N * K / result.mean / 10**9)) ", | |
"execution_count": 95, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": "N: 1024, K: 1024, BS: 16x1, Registers: 1, t: 4.69e-06, TVM Sparsity-Spec GFLOP/s: 4.47e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 2, t: 3.65e-06, TVM Sparsity-Spec GFLOP/s: 5.74e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 3, t: 3.85e-06, TVM Sparsity-Spec GFLOP/s: 5.44e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 4, t: 3.96e-06, TVM Sparsity-Spec GFLOP/s: 5.3e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 5, t: 3.89e-06, TVM Sparsity-Spec GFLOP/s: 5.39e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 6, t: 3.7e-06, TVM Sparsity-Spec GFLOP/s: 5.67e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 7, t: 3.8e-06, TVM Sparsity-Spec GFLOP/s: 5.52e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 8, t: 3.96e-06, TVM Sparsity-Spec GFLOP/s: 5.3e+02\n", | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "import pprint\ndef bsr_matvec_nt_codegen(X, WS, REGISTERS=8, ITEM_LOOP_THRESHOLD=2):\n N, K = WS.shape\n R, C = WS.blocksize\n assert C == 1\n indices = WS.indices\n indptr = WS.indptr\n data = WS.data\n import tvm\n vecty = 'float32x{R}'.format(R=R)\n\n indptr_reads = np.copy(indptr)\n indices_reads = np.copy(indices)\n def tvm_bsr_codegen(ins, outs):\n ib = tvm.ir_builder.create()\n assert N % R == 0\n N_vec = (N // (REGISTERS * R)) * (REGISTERS * R)\n for nb in range(0, N_vec, R * REGISTERS):\n items_per_register = [0 for register in range(REGISTERS)]\n for register in range(REGISTERS):\n n_idx = nb + R * register\n items_for_n_idx = indptr[n_idx // R + 1] - indptr[n_idx // R]\n items_per_register[register] = items_for_n_idx\n # pprint.pprint(items_per_register)\n accs = ib.allocate(vecty, REGISTERS, name=\"ACCS\", scope=\"local\")\n stored = [False for _ in range(REGISTERS)]\n for register in range(REGISTERS):\n accs[register] = tvm.const(0, vecty)\n for register in range(REGISTERS):\n if items_per_register[register] == 0:\n n_idx = nb + R * register\n ib.emit(outs[0].vstore([0, n_idx], accs[register]))\n stored[register] = True\n\n # Now, we do the following.\n # Create a loop from 0 to the minimum number of active registers remaining.\n # In the loop body, do one work-item for each active register.\n # At loop finalization, flush all registers that have zero items left.\n # Repeat until there are no items_per_register.\n items_done = [0 for register in range(REGISTERS)]\n \n while max(items_per_register) > 0:\n def candidate_active_set(items_per_register, register_list):\n items_to_do = min(items_per_register[r] for r in register_list)\n active_registers = register_list\n return (active_registers, items_to_do)\n\n def score(c):\n (active_registers, items_to_do) = c\n return len(active_registers) * items_to_do\n\n def get_active_set(items_per_register):\n items_per_register = np.asarray(items_per_register)\n sorted_registers_by_items = np.argsort(items_per_register)\n candidates = [candidate_active_set(items_per_register, sorted_registers_by_items[i:]) for i, _ in enumerate(items_per_register)]\n return max(candidates, key=lambda c: score(c))\n active_registers, items_to_do = get_active_set(items_per_register)\n #print(items_to_do, active_registers)\n if items_to_do < ITEM_LOOP_THRESHOLD:\n for item in range(items_to_do):\n for register in active_registers:\n n_idx = nb + R * register\n jj = indptr[n_idx // R] + items_done[register] + item\n j = indices[jj]\n x_k = ins[0].vload([0, C * j], 'float32').astype(vecty)\n w_nk_vec = ins[1].vload([jj, 0, 0], vecty)\n accs[register] += x_k * w_nk_vec\n else:\n with ib.for_range(0, items_to_do) as item:\n for register in active_registers:\n n_idx = nb + R * register\n jj = indptr[n_idx // R] + items_done[register] + item\n j = ins[2].vload([jj], 'int32')\n x_k = ins[0].vload([0, C * j], 'float32').astype(vecty)\n w_nk_vec = ins[1].vload([jj, 0, 0], vecty)\n accs[register] += x_k * w_nk_vec\n\n for register in active_registers:\n items_per_register[register] -= items_to_do\n items_done[register] += items_to_do\n\n if items_per_register[register] == 0:\n n_idx = nb + R * register\n ib.emit(outs[0].vstore([0, n_idx], accs[register]))\n stored[register] = True\n assert all(stored)\n for nb in range(N_vec, N, R):\n acc = tvm.const(0, vecty)\n for jj in range(indptr[nb // R], indptr[nb // R + 1]):\n j = indices[jj]\n x_k = ins[0].vload([0, C * j], 'float32').astype(vecty)\n w_nk_vec = ins[1].vload([jj, 0, 0], vecty)\n acc += x_k * w_nk_vec\n ib.emit(outs[0].vstore([0, nb], acc))\n\n ir = ib.get()\n # print(ir)\n return ir\n\n \n Xtvm = tvm.placeholder(X.shape, name=\"X\", dtype=str(X.dtype))\n WSdatatvm = tvm.placeholder(WS.data.shape,\n name=\"WS.data\",\n dtype=str(WS.data.dtype))\n WSindicestvm = tvm.placeholder(WS.indices.shape,\n name=\"WS.indices\",\n dtype=str(WS.indices.dtype))\n \n Ytvm = tvm.extern((1, N), [Xtvm, WSdatatvm, WSindicestvm], tvm_bsr_codegen, dtype=WSdatatvm.dtype)\n s = tvm.create_schedule(Ytvm.op)\n # print(tvm.lower(s, [Xtvm, WSdatatvm, WSindicestvm, Ytvm], simple_mode=True))\n \n f = tvm.build(s, [Xtvm, WSdatatvm, WSindicestvm, Ytvm], \"llvm -mcpu=core-avx2\")\n Xnd = tvm.nd.array(X)\n WSdata_nd = tvm.nd.array(WS.data)\n WSindices_nd = tvm.nd.array(WS.indices)\n Ynd = tvm.nd.array(np.zeros((1, N)).astype(np.float32))\n f(Xnd, WSdata_nd, WSindices_nd, Ynd)\n te = f.time_evaluator(f.entry_name,\n ctx=tvm.cpu(0),\n repeat=5,\n min_repeat_ms=5000)\n fte = lambda: te(Xnd, WSdata_nd, WSindices_nd, Ynd)\n return np.array(Ynd.asnumpy()), fte, f\n\nN = 1024\nK = 1024\nBS = 16\n\nnp.random.seed(42)\nWS = random_bsr_matrix(N, K, BS, 1, density=density, dtype=\"float32\")\n# WS.data[:] = 1.0\nX = np.random.randn(1, K).astype(np.float32)\n# X[:] = 1.0\nYS = WS.dot(X.T).T\n\n \nfor registers in [1, 2, 3, 4, 5, 6, 7, 8]:\n for item_loop_threshold in [1, 2, 4, 6]:\n YZ, fte, f = bsr_matvec_nt_codegen(X,\n WS,\n REGISTERS=registers,\n ITEM_LOOP_THRESHOLD=item_loop_threshold)\n prefetch_interval = 0\n prefetch_length = 0\n np.testing.assert_allclose(YS, YZ, atol=1e-5, rtol=1e-5)\n #print(f.get_source(\"asm\")) \n result = fte()\n print(\n \"N: {N}, K: {K}, BS: {BS}x1, Registers: {registers}, Item Loop Threshold: {item_loop_threshold}, t: {t:.3}, TVM Sparsity-Spec GFLOP/s: {GFLOPs:.3}\"\n .format(N=N,\n K=K,\n BS=BS,\n registers=registers,\n item_loop_threshold=item_loop_threshold,\n t=result.mean,\n GFLOPs=2 * N * K / result.mean / 10**9)) ", | |
"execution_count": 97, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": "N: 1024, K: 1024, BS: 16x1, Registers: 1, Item Loop Threshold: 1, t: 4.79e-06, TVM Sparsity-Spec GFLOP/s: 4.37e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 1, Item Loop Threshold: 2, t: 4.67e-06, TVM Sparsity-Spec GFLOP/s: 4.49e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 1, Item Loop Threshold: 4, t: 4.82e-06, TVM Sparsity-Spec GFLOP/s: 4.35e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 1, Item Loop Threshold: 6, t: 4.48e-06, TVM Sparsity-Spec GFLOP/s: 4.68e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 2, Item Loop Threshold: 1, t: 3.86e-06, TVM Sparsity-Spec GFLOP/s: 5.44e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 2, Item Loop Threshold: 2, t: 3.92e-06, TVM Sparsity-Spec GFLOP/s: 5.34e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 2, Item Loop Threshold: 4, t: 3.68e-06, TVM Sparsity-Spec GFLOP/s: 5.7e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 2, Item Loop Threshold: 6, t: 3.95e-06, TVM Sparsity-Spec GFLOP/s: 5.31e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 3, Item Loop Threshold: 1, t: 3.82e-06, TVM Sparsity-Spec GFLOP/s: 5.49e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 3, Item Loop Threshold: 2, t: 4.15e-06, TVM Sparsity-Spec GFLOP/s: 5.05e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 3, Item Loop Threshold: 4, t: 3.79e-06, TVM Sparsity-Spec GFLOP/s: 5.54e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 3, Item Loop Threshold: 6, t: 3.84e-06, TVM Sparsity-Spec GFLOP/s: 5.46e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 4, Item Loop Threshold: 1, t: 3.54e-06, TVM Sparsity-Spec GFLOP/s: 5.92e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 4, Item Loop Threshold: 2, t: 3.45e-06, TVM Sparsity-Spec GFLOP/s: 6.08e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 4, Item Loop Threshold: 4, t: 3.83e-06, TVM Sparsity-Spec GFLOP/s: 5.48e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 4, Item Loop Threshold: 6, t: 3.55e-06, TVM Sparsity-Spec GFLOP/s: 5.9e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 5, Item Loop Threshold: 1, t: 3.72e-06, TVM Sparsity-Spec GFLOP/s: 5.63e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 5, Item Loop Threshold: 2, t: 3.83e-06, TVM Sparsity-Spec GFLOP/s: 5.48e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 5, Item Loop Threshold: 4, t: 3.54e-06, TVM Sparsity-Spec GFLOP/s: 5.92e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 5, Item Loop Threshold: 6, t: 3.51e-06, TVM Sparsity-Spec GFLOP/s: 5.97e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 6, Item Loop Threshold: 1, t: 3.63e-06, TVM Sparsity-Spec GFLOP/s: 5.78e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 6, Item Loop Threshold: 2, t: 3.44e-06, TVM Sparsity-Spec GFLOP/s: 6.1e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 6, Item Loop Threshold: 4, t: 3.44e-06, TVM Sparsity-Spec GFLOP/s: 6.09e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 6, Item Loop Threshold: 6, t: 3.59e-06, TVM Sparsity-Spec GFLOP/s: 5.84e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 7, Item Loop Threshold: 1, t: 3.52e-06, TVM Sparsity-Spec GFLOP/s: 5.96e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 7, Item Loop Threshold: 2, t: 3.83e-06, TVM Sparsity-Spec GFLOP/s: 5.47e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 7, Item Loop Threshold: 4, t: 3.82e-06, TVM Sparsity-Spec GFLOP/s: 5.49e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 7, Item Loop Threshold: 6, t: 3.58e-06, TVM Sparsity-Spec GFLOP/s: 5.85e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 8, Item Loop Threshold: 1, t: 3.7e-06, TVM Sparsity-Spec GFLOP/s: 5.67e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 8, Item Loop Threshold: 2, t: 3.81e-06, TVM Sparsity-Spec GFLOP/s: 5.51e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 8, Item Loop Threshold: 4, t: 3.78e-06, TVM Sparsity-Spec GFLOP/s: 5.55e+02\nN: 1024, K: 1024, BS: 16x1, Registers: 8, Item Loop Threshold: 6, t: 3.87e-06, TVM Sparsity-Spec GFLOP/s: 5.41e+02\n", | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "### Plotting" | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "def bsr_matvec_nt_codegen_unroll(X, WS, prefetch_length=None, prefetch_interval=None):\n N, K = WS.shape\n R, C = WS.blocksize\n assert C == 1\n indices = WS.indices\n indptr = WS.indptr\n data = WS.data\n import tvm\n vecty = 'float32x{R}'.format(R=R)\n\n def tvm_bsr_codegen(ins, outs):\n ib = tvm.ir_builder.create()\n load_count = 0\n\n for nb in range(0, N, R):\n acc = tvm.const(0, vecty)\n for jj in range(indptr[nb // R], indptr[nb // R + 1]):\n if prefetch_length and prefetch_interval and load_count % prefetch_interval == 0:\n jj_prefetch = jj + prefetch_length\n load = ins[1].vload([jj_prefetch, 0, 0], vecty)\n emit_prefetch(ib, load)\n\n j = indices[jj]\n x_k = ins[0].vload([0, C * j], 'float32').astype(vecty)\n w_nk_vec = ins[1].vload([jj, 0, 0], vecty)\n acc += x_k * w_nk_vec\n load_count += 1\n\n ib.emit(outs[0].vstore([0, nb], acc))\n return ib.get()\n\n Xtvm = tvm.placeholder(X.shape, name=\"X\", dtype=str(X.dtype))\n WSdatatvm = tvm.placeholder(WS.data.shape,\n name=\"WS.data\",\n dtype=str(WS.data.dtype))\n Ytvm = tvm.extern((1, N), [Xtvm, WSdatatvm], tvm_bsr_codegen)\n s = tvm.create_schedule(Ytvm.op)\n f = tvm.build(s, [Xtvm, WSdatatvm, Ytvm], \"llvm -mcpu=core-avx2\")\n Xnd = tvm.nd.array(X)\n WSdata_nd = tvm.nd.array(WS.data)\n Ynd = tvm.nd.array(np.zeros((1, N)).astype(np.float32))\n f(Xnd, WSdata_nd, Ynd)\n te = f.time_evaluator(f.entry_name,\n ctx=tvm.cpu(0),\n repeat=5,\n min_repeat_ms=5000)\n fte = lambda: te(Xnd, WSdata_nd, Ynd)\n return np.array(Ynd.asnumpy()), fte, f\n\ndef bsr_matvec_nt_nonspec(X, WS):\n N, K = WS.shape\n R, C = WS.blocksize\n assert C == 1\n indices = WS.indices\n indptr = WS.indptr\n data = WS.data\n assert data.dtype == np.float32\n assert X.dtype == np.float32\n\n import copy\n WS_tvm_ph = copy.copy(WS)\n WS_tvm_ph.data = tvm.placeholder(WS.data.shape,\n dtype=\"float32\",\n name=\"WS.data\")\n WS_tvm_ph.indices = tvm.placeholder(WS.indices.shape,\n dtype=str(WS.indices.dtype),\n name=\"WS.indices\")\n WS_tvm_ph.indptr = tvm.placeholder(WS.indptr.shape,\n dtype=str(WS.indptr.dtype),\n name=\"WS.indptr\")\n X_tvm_ph = tvm.placeholder(X.shape, dtype=str(X.dtype), name=\"X\")\n Y_tvm = sparse_dense_bsrmv(X_tvm_ph, WS_tvm_ph.data, WS_tvm_ph.indices,\n WS_tvm_ph.indptr)\n s = schedule_sparse_dense([Y_tvm])\n\n # print(tvm.lower(s, [WS_tvm_ph.data, WS_tvm_ph.indices, WS_tvm_ph.indptr, X_tvm_ph, Y_tvm], simple_mode=True))\n\n with tvm.target.create(\"llvm -mcpu=core-avx2\"):\n func = tvm.build(\n s,\n [\n WS_tvm_ph.data, WS_tvm_ph.indices, WS_tvm_ph.indptr, X_tvm_ph,\n Y_tvm\n ],\n )\n Y_tvm = tvm.ndarray.empty(Y_tvm.shape, Y_tvm.dtype)\n func(tvm.ndarray.array(WS.data), tvm.ndarray.array(WS.indices),\n tvm.ndarray.array(WS.indptr), tvm.ndarray.array(X), Y_tvm)\n\n ftimer = func.time_evaluator(func.entry_name,\n tvm.cpu(0),\n min_repeat_ms=5000,\n repeat=5)\n\n fte = lambda: ftimer(tvm.ndarray.array(\n WS.data), tvm.ndarray.array(WS.indices), tvm.ndarray.array(WS.indptr),\n tvm.ndarray.array(X), Y_tvm)\n return fte\n\ndef bsr_matvec_nt_codegen_loops(X, WS, REGISTERS=8, ITEM_LOOP_THRESHOLD=2):\n N, K = WS.shape\n R, C = WS.blocksize\n assert C == 1\n indices = WS.indices\n indptr = WS.indptr\n data = WS.data\n import tvm\n vecty = 'float32x{R}'.format(R=R)\n\n indptr_reads = np.copy(indptr)\n indices_reads = np.copy(indices)\n def tvm_bsr_codegen(ins, outs):\n ib = tvm.ir_builder.create()\n assert N % R == 0\n N_vec = (N // (REGISTERS * R)) * (REGISTERS * R)\n for nb in range(0, N_vec, R * REGISTERS):\n items_per_register = [0 for register in range(REGISTERS)]\n for register in range(REGISTERS):\n n_idx = nb + R * register\n items_for_n_idx = indptr[n_idx // R + 1] - indptr[n_idx // R]\n items_per_register[register] = items_for_n_idx\n # pprint.pprint(items_per_register)\n accs = ib.allocate(vecty, REGISTERS, name=\"ACCS\", scope=\"local\")\n stored = [False for _ in range(REGISTERS)]\n for register in range(REGISTERS):\n accs[register] = tvm.const(0, vecty)\n for register in range(REGISTERS):\n if items_per_register[register] == 0:\n n_idx = nb + R * register\n ib.emit(outs[0].vstore([0, n_idx], accs[register]))\n stored[register] = True\n\n # Now, we do the following.\n # Create a loop from 0 to the minimum number of active registers remaining.\n # In the loop body, do one work-item for each active register.\n # At loop finalization, flush all registers that have zero items left.\n # Repeat until there are no items_per_register.\n items_done = [0 for register in range(REGISTERS)]\n \n while max(items_per_register) > 0:\n def candidate_active_set(items_per_register, register_list):\n items_to_do = min(items_per_register[r] for r in register_list)\n active_registers = register_list\n return (active_registers, items_to_do)\n\n def score(c):\n (active_registers, items_to_do) = c\n return len(active_registers) * items_to_do\n\n def get_active_set(items_per_register):\n items_per_register = np.asarray(items_per_register)\n sorted_registers_by_items = np.argsort(items_per_register)\n candidates = [candidate_active_set(items_per_register, sorted_registers_by_items[i:]) for i, _ in enumerate(items_per_register)]\n return max(candidates, key=lambda c: score(c))\n active_registers, items_to_do = get_active_set(items_per_register)\n #print(items_to_do, active_registers)\n if items_to_do < ITEM_LOOP_THRESHOLD:\n for item in range(items_to_do):\n for register in active_registers:\n n_idx = nb + R * register\n jj = indptr[n_idx // R] + items_done[register] + item\n j = indices[jj]\n x_k = ins[0].vload([0, C * j], 'float32').astype(vecty)\n w_nk_vec = ins[1].vload([jj, 0, 0], vecty)\n accs[register] += x_k * w_nk_vec\n else:\n with ib.for_range(0, items_to_do) as item:\n for register in active_registers:\n n_idx = nb + R * register\n jj = indptr[n_idx // R] + items_done[register] + item\n j = ins[2].vload([jj], 'int32')\n x_k = ins[0].vload([0, C * j], 'float32').astype(vecty)\n w_nk_vec = ins[1].vload([jj, 0, 0], vecty)\n accs[register] += x_k * w_nk_vec\n\n for register in active_registers:\n items_per_register[register] -= items_to_do\n items_done[register] += items_to_do\n\n if items_per_register[register] == 0:\n n_idx = nb + R * register\n ib.emit(outs[0].vstore([0, n_idx], accs[register]))\n stored[register] = True\n assert all(stored)\n for nb in range(N_vec, N, R):\n acc = tvm.const(0, vecty)\n for jj in range(indptr[nb // R], indptr[nb // R + 1]):\n j = indices[jj]\n x_k = ins[0].vload([0, C * j], 'float32').astype(vecty)\n w_nk_vec = ins[1].vload([jj, 0, 0], vecty)\n acc += x_k * w_nk_vec\n ib.emit(outs[0].vstore([0, nb], acc))\n\n ir = ib.get()\n # print(ir)\n return ir\n\n \n Xtvm = tvm.placeholder(X.shape, name=\"X\", dtype=str(X.dtype))\n WSdatatvm = tvm.placeholder(WS.data.shape,\n name=\"WS.data\",\n dtype=str(WS.data.dtype))\n WSindicestvm = tvm.placeholder(WS.indices.shape,\n name=\"WS.indices\",\n dtype=str(WS.indices.dtype))\n \n Ytvm = tvm.extern((1, N), [Xtvm, WSdatatvm, WSindicestvm], tvm_bsr_codegen, dtype=WSdatatvm.dtype)\n s = tvm.create_schedule(Ytvm.op)\n # print(tvm.lower(s, [Xtvm, WSdatatvm, WSindicestvm, Ytvm], simple_mode=True))\n \n f = tvm.build(s, [Xtvm, WSdatatvm, WSindicestvm, Ytvm], \"llvm -mcpu=core-avx2\")\n Xnd = tvm.nd.array(X)\n WSdata_nd = tvm.nd.array(WS.data)\n WSindices_nd = tvm.nd.array(WS.indices)\n Ynd = tvm.nd.array(np.zeros((1, N)).astype(np.float32))\n f(Xnd, WSdata_nd, WSindices_nd, Ynd)\n te = f.time_evaluator(f.entry_name,\n ctx=tvm.cpu(0),\n repeat=5,\n min_repeat_ms=5000)\n fte = lambda: te(Xnd, WSdata_nd, WSindices_nd, Ynd)\n return np.array(Ynd.asnumpy()), fte, f\n\n\n#results = []\n# for D in []:\nfor D in [64, 128, 256, 512, 1024, 2048]:\n \n for BS in [8, 16, 32]:\n WS = random_bsr_matrix(D, D, BS, 1, density=density, dtype=\"float32\")\n X = np.random.randn(1, D).astype(np.float32)\n fte_nonspec = bsr_matvec_nt_nonspec(X, WS)\n result_nonspec = fte_nonspec()\n\n YZ, fte_cg, f = bsr_matvec_nt_codegen_unroll(X,\n WS,\n prefetch_interval=0,\n prefetch_length=0)\n result_cg = fte_cg()\n\n result_loops = []\n for registers in [2, 4, 6]:\n YZ, fte_cg_loop, f = bsr_matvec_nt_codegen_loops(\n X, WS, REGISTERS=registers)\n result_loops.append(fte_cg_loop())\n \n results.append(dict(D=D, BS=BS, density=density, t=min(result_loop.mean for result_loop in result_loops), method=\"Specialized, Loops\"))\n results.append(dict(D=D, BS=BS, density=density, t=result_cg.mean, method=\"Specialized, Unrolled\"))\n results.append(dict(D=D, BS=BS, density=density, t=result_nonspec.mean, method=\"No Specialization\"))\n", | |
"execution_count": 114, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "import pickle\npickle.dump(results, open(\"results_times2.pkl\", \"wb\"))\nprint(results)", | |
"execution_count": 115, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"text": "[{'D': 256, 'BS': 8, 'density': 0.05, 't': 2.133080444232235e-07, 'method': 'Specialized, Loops'}, {'D': 256, 'BS': 8, 'density': 0.05, 't': 2.2811299492588102e-07, 'method': 'Specialized, Unrolled'}, {'D': 256, 'BS': 8, 'density': 0.05, 't': 4.962891530128627e-07, 'method': 'No Specialization'}, {'D': 256, 'BS': 16, 'density': 0.05, 't': 1.4883442442141122e-07, 'method': 'Specialized, Loops'}, {'D': 256, 'BS': 16, 'density': 0.05, 't': 1.5730738922431397e-07, 'method': 'Specialized, Unrolled'}, {'D': 256, 'BS': 16, 'density': 0.05, 't': 2.674741590035752e-07, 'method': 'No Specialization'}, {'D': 256, 'BS': 32, 'density': 0.05, 't': 1.1262190911761503e-07, 'method': 'Specialized, Loops'}, {'D': 256, 'BS': 32, 'density': 0.05, 't': 1.3268527663833858e-07, 'method': 'Specialized, Unrolled'}, {'D': 256, 'BS': 32, 'density': 0.05, 't': 1.6964427439659223e-07, 'method': 'No Specialization'}, {'D': 512, 'BS': 8, 'density': 0.05, 't': 1.0182937986194024e-06, 'method': 'Specialized, Loops'}, {'D': 512, 'BS': 8, 'density': 0.05, 't': 1.2528012924079856e-06, 'method': 'Specialized, Unrolled'}, {'D': 512, 'BS': 8, 'density': 0.05, 't': 1.7982164931033507e-06, 'method': 'No Specialization'}, {'D': 512, 'BS': 16, 'density': 0.05, 't': 8.364706990745376e-07, 'method': 'Specialized, Loops'}, {'D': 512, 'BS': 16, 'density': 0.05, 't': 8.538161681356788e-07, 'method': 'Specialized, Unrolled'}, {'D': 512, 'BS': 16, 'density': 0.05, 't': 1.0687262674734708e-06, 'method': 'No Specialization'}, {'D': 512, 'BS': 32, 'density': 0.05, 't': 7.191038118587013e-07, 'method': 'Specialized, Loops'}, {'D': 512, 'BS': 32, 'density': 0.05, 't': 6.627856745876913e-07, 'method': 'Specialized, Unrolled'}, {'D': 512, 'BS': 32, 'density': 0.05, 't': 7.713198417005155e-07, 'method': 'No Specialization'}, {'D': 1024, 'BS': 8, 'density': 0.05, 't': 4.5071949971043275e-06, 'method': 'Specialized, Loops'}, {'D': 1024, 'BS': 8, 'density': 0.05, 't': 8.434779331746014e-06, 'method': 'Specialized, Unrolled'}, {'D': 1024, 'BS': 8, 'density': 0.05, 't': 8.019421588942178e-06, 'method': 'No Specialization'}, {'D': 1024, 'BS': 16, 'density': 0.05, 't': 3.452362588503161e-06, 'method': 'Specialized, Loops'}, {'D': 1024, 'BS': 16, 'density': 0.05, 't': 6.616857037213309e-06, 'method': 'Specialized, Unrolled'}, {'D': 1024, 'BS': 16, 'density': 0.05, 't': 5.15709326392636e-06, 'method': 'No Specialization'}, {'D': 1024, 'BS': 32, 'density': 0.05, 't': 3.145059278836992e-06, 'method': 'Specialized, Loops'}, {'D': 1024, 'BS': 32, 'density': 0.05, 't': 5.367362047141053e-06, 'method': 'Specialized, Unrolled'}, {'D': 1024, 'BS': 32, 'density': 0.05, 't': 3.580151541295226e-06, 'method': 'No Specialization'}, {'D': 2048, 'BS': 8, 'density': 0.05, 't': 2.2163306666377298e-05, 'method': 'Specialized, Loops'}, {'D': 2048, 'BS': 8, 'density': 0.05, 't': 3.946106533344352e-05, 'method': 'Specialized, Unrolled'}, {'D': 2048, 'BS': 8, 'density': 0.05, 't': 3.502421081737321e-05, 'method': 'No Specialization'}, {'D': 2048, 'BS': 16, 'density': 0.05, 't': 2.0289652145720762e-05, 'method': 'Specialized, Loops'}, {'D': 2048, 'BS': 16, 'density': 0.05, 't': 3.395735933636384e-05, 'method': 'Specialized, Unrolled'}, {'D': 2048, 'BS': 16, 'density': 0.05, 't': 2.1451794697433912e-05, 'method': 'No Specialization'}, {'D': 2048, 'BS': 32, 'density': 0.05, 't': 1.9290948226997196e-05, 'method': 'Specialized, Loops'}, {'D': 2048, 'BS': 32, 'density': 0.05, 't': 2.8791332106136455e-05, 'method': 'Specialized, Unrolled'}, {'D': 2048, 'BS': 32, 'density': 0.05, 't': 2.0987920887955505e-05, 'method': 'No Specialization'}, {'D': 64, 'BS': 8, 'density': 0.05, 't': 3.154390080610991e-08, 'method': 'Specialized, Loops'}, {'D': 64, 'BS': 8, 'density': 0.05, 't': 2.767048794674572e-08, 'method': 'Specialized, Unrolled'}, {'D': 64, 'BS': 8, 'density': 0.05, 't': 6.248712447240978e-08, 'method': 'No Specialization'}, {'D': 64, 'BS': 16, 'density': 0.05, 't': 2.980773931489213e-08, 'method': 'Specialized, Loops'}, {'D': 64, 'BS': 16, 'density': 0.05, 't': 2.4216455074084598e-08, 'method': 'Specialized, Unrolled'}, {'D': 64, 'BS': 16, 'density': 0.05, 't': 4.22478435951169e-08, 'method': 'No Specialization'}, {'D': 64, 'BS': 32, 'density': 0.05, 't': 2.6256341478357466e-08, 'method': 'Specialized, Loops'}, {'D': 64, 'BS': 32, 'density': 0.05, 't': 2.456149920264534e-08, 'method': 'Specialized, Unrolled'}, {'D': 64, 'BS': 32, 'density': 0.05, 't': 3.698586238818649e-08, 'method': 'No Specialization'}, {'D': 128, 'BS': 8, 'density': 0.05, 't': 7.37431961467401e-08, 'method': 'Specialized, Loops'}, {'D': 128, 'BS': 8, 'density': 0.05, 't': 5.8178382727694875e-08, 'method': 'Specialized, Unrolled'}, {'D': 128, 'BS': 8, 'density': 0.05, 't': 1.3339175007545698e-07, 'method': 'No Specialization'}, {'D': 128, 'BS': 16, 'density': 0.05, 't': 5.427601393897902e-08, 'method': 'Specialized, Loops'}, {'D': 128, 'BS': 16, 'density': 0.05, 't': 4.900419371118465e-08, 'method': 'Specialized, Unrolled'}, {'D': 128, 'BS': 16, 'density': 0.05, 't': 8.925716243573886e-08, 'method': 'No Specialization'}, {'D': 128, 'BS': 32, 'density': 0.05, 't': 4.519596953431281e-08, 'method': 'Specialized, Loops'}, {'D': 128, 'BS': 32, 'density': 0.05, 't': 4.219336584707194e-08, 'method': 'Specialized, Unrolled'}, {'D': 128, 'BS': 32, 'density': 0.05, 't': 6.354837736085245e-08, 'method': 'No Specialization'}]\n", | |
"name": "stdout" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "import pandas as pd\nimport altair as alt\nalt.renderers.enable('notebook')\nalt.themes.enable('opaque')\n\ndf = pd.DataFrame.from_records(results)\ndf['method_blocksize'] = df.apply(\n lambda x: \"{} ({}x1)\".format(x['method'], x['BS']), axis=1)\ndf['GFLOPS'] = df.apply(\n lambda x: 2 * x['D'] * x['D'] / x['t'] / 1.0e9, axis=1)\ndf\n\nc = alt.Chart(df)\nc = c.mark_circle(size=60).encode(\n x='D', y='GFLOPS', color='method_blocksize') + c.mark_line(size=1).encode(\n x='D', y='GFLOPS', color='method_blocksize')\nc.save('bs_32_n.png', scale_factor=4.0)\nc", | |
"execution_count": 116, | |
"outputs": [ | |
{ | |
"output_type": "display_data", | |
"data": { | |
"application/javascript": "var spec = {\"config\": {\"background\": \"white\", \"view\": {\"width\": 400, \"height\": 300}, \"mark\": {\"tooltip\": null}}, \"layer\": [{\"mark\": {\"type\": \"circle\", \"size\": 60}, \"encoding\": {\"color\": {\"type\": \"nominal\", \"field\": \"method_blocksize\"}, \"x\": {\"type\": \"quantitative\", \"field\": \"D\"}, \"y\": {\"type\": \"quantitative\", \"field\": \"GFLOPS\"}}}, {\"mark\": {\"type\": \"line\", \"size\": 1}, \"encoding\": {\"color\": {\"type\": \"nominal\", \"field\": \"method_blocksize\"}, \"x\": {\"type\": \"quantitative\", \"field\": \"D\"}, \"y\": {\"type\": \"quantitative\", \"field\": \"GFLOPS\"}}}], \"data\": {\"name\": \"data-24f3db4dce0427a028c9ef8b1b98de17\"}, \"$schema\": \"https://vega.github.io/schema/vega-lite/v3.3.0.json\", \"datasets\": {\"data-24f3db4dce0427a028c9ef8b1b98de17\": [{\"BS\": 8, \"D\": 256, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 2.133080444232235e-07, \"method_blocksize\": \"Specialized, Loops (8x1)\", \"GFLOPS\": 614.472840695781}, {\"BS\": 8, \"D\": 256, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 2.2811299492588102e-07, \"method_blocksize\": \"Specialized, Unrolled (8x1)\", \"GFLOPS\": 574.5924296973445}, {\"BS\": 8, \"D\": 256, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 4.962891530128627e-07, \"method_blocksize\": \"No Specialization (8x1)\", \"GFLOPS\": 264.10409980611223}, {\"BS\": 16, \"D\": 256, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 1.4883442442141122e-07, \"method_blocksize\": \"Specialized, Loops (16x1)\", \"GFLOPS\": 880.6564778917106}, {\"BS\": 16, \"D\": 256, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 1.5730738922431397e-07, \"method_blocksize\": \"Specialized, Unrolled (16x1)\", \"GFLOPS\": 833.2221432592505}, {\"BS\": 16, \"D\": 256, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 2.674741590035752e-07, \"method_blocksize\": \"No Specialization (16x1)\", \"GFLOPS\": 490.0361234456597}, {\"BS\": 32, \"D\": 256, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 1.1262190911761503e-07, \"method_blocksize\": \"Specialized, Loops (32x1)\", \"GFLOPS\": 1163.8232829379308}, {\"BS\": 32, \"D\": 256, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 1.3268527663833858e-07, \"method_blocksize\": \"Specialized, Unrolled (32x1)\", \"GFLOPS\": 987.8413289009005}, {\"BS\": 32, \"D\": 256, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 1.6964427439659223e-07, \"method_blocksize\": \"No Specialization (32x1)\", \"GFLOPS\": 772.628492568995}, {\"BS\": 8, \"D\": 512, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 1.0182937986194024e-06, \"method_blocksize\": \"Specialized, Loops (8x1)\", \"GFLOPS\": 514.8690885781953}, {\"BS\": 8, \"D\": 512, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 1.2528012924079856e-06, \"method_blocksize\": \"Specialized, Unrolled (8x1)\", \"GFLOPS\": 418.4925440109308}, {\"BS\": 8, \"D\": 512, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 1.7982164931033507e-06, \"method_blocksize\": \"No Specialization (8x1)\", \"GFLOPS\": 291.5599995944799}, {\"BS\": 16, \"D\": 512, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 8.364706990745376e-07, \"method_blocksize\": \"Specialized, Loops (16x1)\", \"GFLOPS\": 626.7858522481023}, {\"BS\": 16, \"D\": 512, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 8.538161681356788e-07, \"method_blocksize\": \"Specialized, Unrolled (16x1)\", \"GFLOPS\": 614.0525555340456}, {\"BS\": 16, \"D\": 512, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 1.0687262674734708e-06, \"method_blocksize\": \"No Specialization (16x1)\", \"GFLOPS\": 490.5727649414348}, {\"BS\": 32, \"D\": 512, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 7.191038118587013e-07, \"method_blocksize\": \"Specialized, Loops (32x1)\", \"GFLOPS\": 729.0852744123943}, {\"BS\": 32, \"D\": 512, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 6.627856745876913e-07, \"method_blocksize\": \"Specialized, Unrolled (32x1)\", \"GFLOPS\": 791.0370125699404}, {\"BS\": 32, \"D\": 512, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 7.713198417005155e-07, \"method_blocksize\": \"No Specialization (32x1)\", \"GFLOPS\": 679.7283975530971}, {\"BS\": 8, \"D\": 1024, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 4.5071949971043275e-06, \"method_blocksize\": \"Specialized, Loops (8x1)\", \"GFLOPS\": 465.28983133574803}, {\"BS\": 8, \"D\": 1024, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 8.434779331746014e-06, \"method_blocksize\": \"Specialized, Unrolled (8x1)\", \"GFLOPS\": 248.6315192748363}, {\"BS\": 8, \"D\": 1024, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 8.019421588942178e-06, \"method_blocksize\": \"No Specialization (8x1)\", \"GFLOPS\": 261.50913463531106}, {\"BS\": 16, \"D\": 1024, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 3.452362588503161e-06, \"method_blocksize\": \"Specialized, Loops (16x1)\", \"GFLOPS\": 607.4541553033283}, {\"BS\": 16, \"D\": 1024, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 6.616857037213309e-06, \"method_blocksize\": \"Specialized, Unrolled (16x1)\", \"GFLOPS\": 316.9408056129343}, {\"BS\": 16, \"D\": 1024, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 5.15709326392636e-06, \"method_blocksize\": \"No Specialization (16x1)\", \"GFLOPS\": 406.6538828509241}, {\"BS\": 32, \"D\": 1024, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 3.145059278836992e-06, \"method_blocksize\": \"Specialized, Loops (32x1)\", \"GFLOPS\": 666.8084172885617}, {\"BS\": 32, \"D\": 1024, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 5.367362047141053e-06, \"method_blocksize\": \"Specialized, Unrolled (32x1)\", \"GFLOPS\": 390.7230370489087}, {\"BS\": 32, \"D\": 1024, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 3.580151541295226e-06, \"method_blocksize\": \"No Specialization (32x1)\", \"GFLOPS\": 585.7718523393266}, {\"BS\": 8, \"D\": 2048, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 2.2163306666377298e-05, \"method_blocksize\": \"Specialized, Loops (8x1)\", \"GFLOPS\": 378.4908148532675}, {\"BS\": 8, \"D\": 2048, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 3.946106533344352e-05, \"method_blocksize\": \"Specialized, Unrolled (8x1)\", \"GFLOPS\": 212.57935965784986}, {\"BS\": 8, \"D\": 2048, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 3.502421081737321e-05, \"method_blocksize\": \"No Specialization (8x1)\", \"GFLOPS\": 239.50883700822638}, {\"BS\": 16, \"D\": 2048, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 2.0289652145720762e-05, \"method_blocksize\": \"Specialized, Loops (16x1)\", \"GFLOPS\": 413.44267214404755}, {\"BS\": 16, \"D\": 2048, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 3.395735933636384e-05, \"method_blocksize\": \"Specialized, Unrolled (16x1)\", \"GFLOPS\": 247.0335786981207}, {\"BS\": 16, \"D\": 2048, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 2.1451794697433912e-05, \"method_blocksize\": \"No Specialization (16x1)\", \"GFLOPS\": 391.0445777762107}, {\"BS\": 32, \"D\": 2048, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 1.9290948226997196e-05, \"method_blocksize\": \"Specialized, Loops (32x1)\", \"GFLOPS\": 434.84684637017244}, {\"BS\": 32, \"D\": 2048, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 2.8791332106136455e-05, \"method_blocksize\": \"Specialized, Unrolled (32x1)\", \"GFLOPS\": 291.3588009431523}, {\"BS\": 32, \"D\": 2048, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 2.0987920887955505e-05, \"method_blocksize\": \"No Specialization (32x1)\", \"GFLOPS\": 399.6874223408205}, {\"BS\": 8, \"D\": 64, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 3.154390080610991e-08, \"method_blocksize\": \"Specialized, Loops (8x1)\", \"GFLOPS\": 259.7015521432672}, {\"BS\": 8, \"D\": 64, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 2.767048794674572e-08, \"method_blocksize\": \"Specialized, Unrolled (8x1)\", \"GFLOPS\": 296.05549478441515}, {\"BS\": 8, \"D\": 64, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 6.248712447240978e-08, \"method_blocksize\": \"No Specialization (8x1)\", \"GFLOPS\": 131.09900750221033}, {\"BS\": 16, \"D\": 64, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 2.980773931489213e-08, \"method_blocksize\": \"Specialized, Loops (16x1)\", \"GFLOPS\": 274.8279536887665}, {\"BS\": 16, \"D\": 64, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 2.4216455074084598e-08, \"method_blocksize\": \"Specialized, Unrolled (16x1)\", \"GFLOPS\": 338.28237762044387}, {\"BS\": 16, \"D\": 64, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 4.22478435951169e-08, \"method_blocksize\": \"No Specialization (16x1)\", \"GFLOPS\": 193.90338779200675}, {\"BS\": 32, \"D\": 64, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 2.6256341478357466e-08, \"method_blocksize\": \"Specialized, Loops (32x1)\", \"GFLOPS\": 312.0008172788462}, {\"BS\": 32, \"D\": 64, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 2.456149920264534e-08, \"method_blocksize\": \"Specialized, Unrolled (32x1)\", \"GFLOPS\": 333.53012910212334}, {\"BS\": 32, \"D\": 64, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 3.698586238818649e-08, \"method_blocksize\": \"No Specialization (32x1)\", \"GFLOPS\": 221.4900362203417}, {\"BS\": 8, \"D\": 128, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 7.37431961467401e-08, \"method_blocksize\": \"Specialized, Loops (8x1)\", \"GFLOPS\": 444.3528584629776}, {\"BS\": 8, \"D\": 128, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 5.8178382727694875e-08, \"method_blocksize\": \"Specialized, Unrolled (8x1)\", \"GFLOPS\": 563.2332571596447}, {\"BS\": 8, \"D\": 128, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 1.3339175007545698e-07, \"method_blocksize\": \"No Specialization (8x1)\", \"GFLOPS\": 245.65237341487622}, {\"BS\": 16, \"D\": 128, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 5.427601393897902e-08, \"method_blocksize\": \"Specialized, Loops (16x1)\", \"GFLOPS\": 603.7289333155551}, {\"BS\": 16, \"D\": 128, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 4.900419371118465e-08, \"method_blocksize\": \"Specialized, Unrolled (16x1)\", \"GFLOPS\": 668.6774644864951}, {\"BS\": 16, \"D\": 128, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 8.925716243573886e-08, \"method_blocksize\": \"No Specialization (16x1)\", \"GFLOPS\": 367.1189975772699}, {\"BS\": 32, \"D\": 128, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 4.519596953431281e-08, \"method_blocksize\": \"Specialized, Loops (32x1)\", \"GFLOPS\": 725.0204019878922}, {\"BS\": 32, \"D\": 128, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 4.219336584707194e-08, \"method_blocksize\": \"Specialized, Unrolled (32x1)\", \"GFLOPS\": 776.6149806290929}, {\"BS\": 32, \"D\": 128, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 6.354837736085245e-08, \"method_blocksize\": \"No Specialization (32x1)\", \"GFLOPS\": 515.6386576785513}]}};\nvar opt = {};\nvar type = \"vega-lite\";\nvar id = \"58323ae6-7c93-4d94-9386-4e12f4950f93\";\n\nvar output_area = this;\n\nrequire([\"nbextensions/jupyter-vega/index\"], function(vega) {\n var target = document.createElement(\"div\");\n target.id = id;\n target.className = \"vega-embed\";\n\n var style = document.createElement(\"style\");\n style.textContent = [\n \".vega-embed .error p {\",\n \" color: firebrick;\",\n \" font-size: 14px;\",\n \"}\",\n ].join(\"\\\\n\");\n\n // element is a jQuery wrapped DOM element inside the output area\n // see http://ipython.readthedocs.io/en/stable/api/generated/\\\n // IPython.display.html#IPython.display.Javascript.__init__\n element[0].appendChild(target);\n element[0].appendChild(style);\n\n vega.render(\"#\" + id, spec, type, opt, output_area);\n}, function (err) {\n if (err.requireType !== \"scripterror\") {\n throw(err);\n }\n});\n", | |
"text/plain": "<vega.vegalite.VegaLite at 0x11f0aca90>" | |
}, | |
"metadata": { | |
"jupyter-vega": "#58323ae6-7c93-4d94-9386-4e12f4950f93" | |
} | |
}, | |
{ | |
"output_type": "execute_result", | |
"execution_count": 116, | |
"data": { | |
"text/plain": "" | |
}, | |
"metadata": {} | |
}, | |
{ | |
"data": { | |
"image/png": "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CBAChAAhQAgQAr2GABG1XoOaLkQIEAKEACFACBAChIB/CBBR8w8vmk0IEAKEACFACBAChECvIUBErdegpgsRAn2HACutwfop2kdRURGvrcdqo9FOxb7TC12ZECAECAEpBIioSSFE7xMCUYDAPffcg3/961+80GhGRgYvympvGs7eo0EIEAKEACEgJgJE1MTUC0lFCIQUgZ///Of44IMPwGqisUb0rBn4xRdfzAtCHz16lFeNp0EIEAKEACEgHgJE1MTTCUlECIQcATtRY10G7N0WPL0W8gvTCQkBQoAQIASCQoCIWlDw0cGEQGQgQEQtMvREUhIChAAh4I4AETWyCUIgBhBwD322tbXxvpRarRbHjh2Lqd6lMaBuukVCgBCIIgSIqEWRMulWCAFvCNg3Ezz77LO8b+czzzyDw4cP45VXXsFtt91GwBEChAAhQAgIigARNUEVQ2IRAqFEwL08R2pqKidp1113XSgvQ+ciBAgBQoAQCDECRNRCDCidjhAgBAgBQoAQIAQIgVAhQEQtVEjSeQgBQoAQIAQIAUKAEAgxAkTUQgwonY4QIAQIAUKAECAECIFQIUBELVRI0nkIAUKAECAECAFCgBAIMQJE1EIMKJ2OECAECAFCgBAgBAiBUCFARC1USNJ5CAFCgBAgBAgBQoAQCDEC/w8KehS5tpnlowAAAABJRU5ErkJggg==" | |
}, | |
"metadata": { | |
"jupyter-vega": "#58323ae6-7c93-4d94-9386-4e12f4950f93" | |
}, | |
"output_type": "display_data" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "\nc = alt.Chart(df[df.BS == 16])\nc = c.mark_circle(size=60).encode(\n x='D', y='GFLOPS', color='method_blocksize') + c.mark_line(size=1).encode(\n x='D', y='GFLOPS', color='method_blocksize')\nc.save('bs_32_n_bs16.png', scale_factor=4.0)\nc", | |
"execution_count": 117, | |
"outputs": [ | |
{ | |
"output_type": "display_data", | |
"data": { | |
"application/javascript": "var spec = {\"config\": {\"background\": \"white\", \"view\": {\"width\": 400, \"height\": 300}, \"mark\": {\"tooltip\": null}}, \"layer\": [{\"mark\": {\"type\": \"circle\", \"size\": 60}, \"encoding\": {\"color\": {\"type\": \"nominal\", \"field\": \"method_blocksize\"}, \"x\": {\"type\": \"quantitative\", \"field\": \"D\"}, \"y\": {\"type\": \"quantitative\", \"field\": \"GFLOPS\"}}}, {\"mark\": {\"type\": \"line\", \"size\": 1}, \"encoding\": {\"color\": {\"type\": \"nominal\", \"field\": \"method_blocksize\"}, \"x\": {\"type\": \"quantitative\", \"field\": \"D\"}, \"y\": {\"type\": \"quantitative\", \"field\": \"GFLOPS\"}}}], \"data\": {\"name\": \"data-33c76566c8a3565f36671ddbc7f1ce56\"}, \"$schema\": \"https://vega.github.io/schema/vega-lite/v3.3.0.json\", \"datasets\": {\"data-33c76566c8a3565f36671ddbc7f1ce56\": [{\"BS\": 16, \"D\": 256, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 1.4883442442141122e-07, \"method_blocksize\": \"Specialized, Loops (16x1)\", \"GFLOPS\": 880.6564778917106}, {\"BS\": 16, \"D\": 256, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 1.5730738922431397e-07, \"method_blocksize\": \"Specialized, Unrolled (16x1)\", \"GFLOPS\": 833.2221432592505}, {\"BS\": 16, \"D\": 256, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 2.674741590035752e-07, \"method_blocksize\": \"No Specialization (16x1)\", \"GFLOPS\": 490.0361234456597}, {\"BS\": 16, \"D\": 512, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 8.364706990745376e-07, \"method_blocksize\": \"Specialized, Loops (16x1)\", \"GFLOPS\": 626.7858522481023}, {\"BS\": 16, \"D\": 512, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 8.538161681356788e-07, \"method_blocksize\": \"Specialized, Unrolled (16x1)\", \"GFLOPS\": 614.0525555340456}, {\"BS\": 16, \"D\": 512, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 1.0687262674734708e-06, \"method_blocksize\": \"No Specialization (16x1)\", \"GFLOPS\": 490.5727649414348}, {\"BS\": 16, \"D\": 1024, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 3.452362588503161e-06, \"method_blocksize\": \"Specialized, Loops (16x1)\", \"GFLOPS\": 607.4541553033283}, {\"BS\": 16, \"D\": 1024, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 6.616857037213309e-06, \"method_blocksize\": \"Specialized, Unrolled (16x1)\", \"GFLOPS\": 316.9408056129343}, {\"BS\": 16, \"D\": 1024, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 5.15709326392636e-06, \"method_blocksize\": \"No Specialization (16x1)\", \"GFLOPS\": 406.6538828509241}, {\"BS\": 16, \"D\": 2048, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 2.0289652145720762e-05, \"method_blocksize\": \"Specialized, Loops (16x1)\", \"GFLOPS\": 413.44267214404755}, {\"BS\": 16, \"D\": 2048, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 3.395735933636384e-05, \"method_blocksize\": \"Specialized, Unrolled (16x1)\", \"GFLOPS\": 247.0335786981207}, {\"BS\": 16, \"D\": 2048, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 2.1451794697433912e-05, \"method_blocksize\": \"No Specialization (16x1)\", \"GFLOPS\": 391.0445777762107}, {\"BS\": 16, \"D\": 64, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 2.980773931489213e-08, \"method_blocksize\": \"Specialized, Loops (16x1)\", \"GFLOPS\": 274.8279536887665}, {\"BS\": 16, \"D\": 64, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 2.4216455074084598e-08, \"method_blocksize\": \"Specialized, Unrolled (16x1)\", \"GFLOPS\": 338.28237762044387}, {\"BS\": 16, \"D\": 64, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 4.22478435951169e-08, \"method_blocksize\": \"No Specialization (16x1)\", \"GFLOPS\": 193.90338779200675}, {\"BS\": 16, \"D\": 128, \"density\": 0.05, \"method\": \"Specialized, Loops\", \"t\": 5.427601393897902e-08, \"method_blocksize\": \"Specialized, Loops (16x1)\", \"GFLOPS\": 603.7289333155551}, {\"BS\": 16, \"D\": 128, \"density\": 0.05, \"method\": \"Specialized, Unrolled\", \"t\": 4.900419371118465e-08, \"method_blocksize\": \"Specialized, Unrolled (16x1)\", \"GFLOPS\": 668.6774644864951}, {\"BS\": 16, \"D\": 128, \"density\": 0.05, \"method\": \"No Specialization\", \"t\": 8.925716243573886e-08, \"method_blocksize\": \"No Specialization (16x1)\", \"GFLOPS\": 367.1189975772699}]}};\nvar opt = {};\nvar type = \"vega-lite\";\nvar id = \"4225eac2-2d50-4be8-b8bb-7393ef60b036\";\n\nvar output_area = this;\n\nrequire([\"nbextensions/jupyter-vega/index\"], function(vega) {\n var target = document.createElement(\"div\");\n target.id = id;\n target.className = \"vega-embed\";\n\n var style = document.createElement(\"style\");\n style.textContent = [\n \".vega-embed .error p {\",\n \" color: firebrick;\",\n \" font-size: 14px;\",\n \"}\",\n ].join(\"\\\\n\");\n\n // element is a jQuery wrapped DOM element inside the output area\n // see http://ipython.readthedocs.io/en/stable/api/generated/\\\n // IPython.display.html#IPython.display.Javascript.__init__\n element[0].appendChild(target);\n element[0].appendChild(style);\n\n vega.render(\"#\" + id, spec, type, opt, output_area);\n}, function (err) {\n if (err.requireType !== \"scripterror\") {\n throw(err);\n }\n});\n", | |
"text/plain": "<vega.vegalite.VegaLite at 0x11ee7a1d0>" | |
}, | |
"metadata": { | |
"jupyter-vega": "#4225eac2-2d50-4be8-b8bb-7393ef60b036" | |
} | |
}, | |
{ | |
"output_type": "execute_result", | |
"execution_count": 117, | |
"data": { | |
"text/plain": "" | |
}, | |
"metadata": {} | |
}, | |
{ | |
"data": { | |
"image/png": "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" | |
}, | |
"metadata": { | |
"jupyter-vega": "#4225eac2-2d50-4be8-b8bb-7393ef60b036" | |
}, | |
"output_type": "display_data" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "", | |
"execution_count": null, | |
"outputs": [] | |
} | |
], | |
"metadata": { | |
"_draft": { | |
"nbviewer_url": "https://gist.github.com/439191f566cfe433ac7e1d7cea627368" | |
}, | |
"gist": { | |
"id": "439191f566cfe433ac7e1d7cea627368", | |
"data": { | |
"description": "Block-Sparse GEMM.ipynb", | |
"public": true | |
} | |
}, | |
"kernelspec": { | |
"name": "python3", | |
"display_name": "Python 3", | |
"language": "python" | |
}, | |
"language_info": { | |
"name": "python", | |
"version": "3.6.8", | |
"mimetype": "text/x-python", | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"pygments_lexer": "ipython3", | |
"nbconvert_exporter": "python", | |
"file_extension": ".py" | |
}, | |
"toc": { | |
"nav_menu": {}, | |
"number_sections": true, | |
"sideBar": true, | |
"skip_h1_title": false, | |
"base_numbering": 1, | |
"title_cell": "Table of Contents", | |
"title_sidebar": "Contents", | |
"toc_cell": true, | |
"toc_position": { | |
"height": "calc(100% - 180px)", | |
"width": "222px", | |
"left": "10px", | |
"top": "150px" | |
}, | |
"toc_section_display": true, | |
"toc_window_display": true | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment