зеркало из https://github.com/microsoft/hat.git
Add macOS to CI pipeline, restore HATPackage to accept a file path instead of a directory path (#13)
* restored hat package definition as a hat file + library * [nfc] pydoc update * update test condition * replace test data files with runtime generation * install test dependencies * update whl test * fix folder name * export run_benchmark, support spaces in strings * fixed add_functions to expect a single hat file Co-authored-by: Lisa Ong <onglisa@microsoft.com>
This commit is contained in:
Родитель
d2b3132a24
Коммит
9146a032b1
|
@ -12,12 +12,7 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ["3.7", "3.8", "3.9"]
|
||||
os: ['windows-latest', 'ubuntu-latest']
|
||||
include:
|
||||
- os: 'windows-latest'
|
||||
test-dir: 'tools/test/data/windows'
|
||||
- os: 'ubuntu-latest'
|
||||
test-dir: 'tools/test/data/linux'
|
||||
os: ['windows-latest', 'ubuntu-latest', 'macos-latest']
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
|
@ -35,7 +30,10 @@ jobs:
|
|||
python -m pip install --upgrade pip
|
||||
python -m pip install -r tools/requirements.txt
|
||||
- name: Unittest
|
||||
run: python -m unittest discover tools/test
|
||||
run: |
|
||||
python -m pip install -r tools/test/requirements.txt
|
||||
python -m pip uninstall hatlib
|
||||
python -m unittest discover tools/test
|
||||
- name: Build whl
|
||||
run: |
|
||||
python -m pip install build
|
||||
|
@ -44,8 +42,8 @@ jobs:
|
|||
shell: pwsh
|
||||
run: |
|
||||
$WHL = Get-ChildItem -Path dist -Filter "*.whl" | %{$_.FullName}
|
||||
python -m pip install $WHL
|
||||
python -m pip install --force-reinstall $WHL
|
||||
- name: Test whl
|
||||
run: |
|
||||
cd ${{ matrix.test-dir }}
|
||||
hatlib.hat_to_dynamic optimized_matmul.hat optimized_matmul.test.hat
|
||||
cd test_acccgen
|
||||
hatlib.benchmark_hat BenchmarkHATPackage_test_benchmark.hat
|
||||
|
|
|
@ -356,9 +356,9 @@ MigrationBackup/
|
|||
build/
|
||||
results.csv
|
||||
|
||||
# test data
|
||||
tools/test/data/
|
||||
|
||||
# setuptools
|
||||
dist/
|
||||
*.egg-info/
|
||||
*.egg-info/
|
||||
|
||||
# test
|
||||
test_acccgen
|
|
@ -2,3 +2,4 @@ from .hat import *
|
|||
from .hat_file import *
|
||||
from .hat_package import *
|
||||
from .hat_to_dynamic import *
|
||||
from .benchmark_hat_package import run_benchmark
|
||||
|
|
|
@ -8,30 +8,23 @@ import os
|
|||
|
||||
class HATPackage:
|
||||
def __init__(self, hat_file_path):
|
||||
"""A HAT Package is defined to be a HAT file and corresponding object file, located in the same directory.
|
||||
The object file is specified in the HAT file's link_target attribute.
|
||||
The same object file can be referenced by many HAT files.
|
||||
"""A HAT Package is defined to be a HAT file and corresponding binary file, located in the same directory.
|
||||
The binary file is specified in the HAT file's link_target attribute.
|
||||
The same binary file can be referenced by many HAT files.
|
||||
Many HAT packages can exist in the same directory.
|
||||
An instance of HATPackage is created by giving HATPackage the file path to the .hat file."""
|
||||
self.path = Path(dirpath).resolve()
|
||||
assert self.path.is_dir()
|
||||
self.name = os.path.basename(hat_file_path)
|
||||
self.hat_file_path = hat_file_path
|
||||
self.hat_file = HATFile.Deserialize(hat_file_path)
|
||||
|
||||
self.name = self.path.name
|
||||
self.hat_files = [HATFile.Deserialize(hat_file_path) for hat_file_path in self.path.glob("*.hat")]
|
||||
|
||||
# Find all referenced link targets and ensure they are also part of the package
|
||||
self.link_targets = []
|
||||
for hat_file in self.hat_files:
|
||||
link_target_path = self.path / hat_file.dependencies.link_target
|
||||
if not os.path.isfile(link_target_path):
|
||||
raise ValueError(f"HAT file {hat_file.path} references link_target {hat_file.dependencies.link_target} which is not part of the HAT package at {self.path}")
|
||||
self.link_targets.append(link_target_path)
|
||||
self.link_target = self.hat_file.dependencies.link_target
|
||||
self.link_target_path = os.path.join(os.path.split(self.hat_file_path)[0], self.hat_file.dependencies.link_target)
|
||||
if not os.path.isfile(self.link_target_path):
|
||||
raise ValueError(f"HAT file {self.hat_file_path} references link_target {self.hat_file.dependencies.link_target} which is not found in same directory as HAT file (expecting it to be in {os.path.split(self.hat_file_path)[0]}")
|
||||
self.functions = self.hat_file.functions
|
||||
|
||||
def get_functions(self):
|
||||
functions = []
|
||||
for hat_file in self.hat_files:
|
||||
functions += hat_file.functions
|
||||
return functions
|
||||
return self.hat_file.functions
|
||||
|
||||
def get_functions_for_target(self, os: str, arch: str, required_extensions:list = []):
|
||||
all_functions = self.get_functions()
|
||||
|
|
|
@ -47,7 +47,7 @@ def linux_create_dynamic_package(input_hat_binary_path, output_hat_path, hat_des
|
|||
# create new HAT binary
|
||||
prefix, _ = os.path.splitext(output_hat_path)
|
||||
output_hat_binary_path = prefix + ".so"
|
||||
os.system(f"g++ -shared -fPIC -o {output_hat_binary_path} {input_hat_binary_path}")
|
||||
os.system(f'g++ -shared -fPIC -o "{output_hat_binary_path}" "{input_hat_binary_path}"')
|
||||
|
||||
# create new HAT file
|
||||
hat_description["dependencies"]["link_target"] = os.path.basename(output_hat_binary_path)
|
||||
|
@ -100,7 +100,7 @@ def windows_create_dynamic_package(input_hat_binary_path, output_hat_path, hat_d
|
|||
|
||||
function_descriptions = hat_description["functions"]
|
||||
function_names = list(function_descriptions.keys())
|
||||
linker_command_line = "link.exe -dll -FORCE:MULTIPLE -EXPORT:{} -out:out.dll dllmain.obj {}".format(" -EXPORT:".join(function_names), input_hat_binary_path)
|
||||
linker_command_line = 'link.exe -dll -FORCE:MULTIPLE -EXPORT:{} -out:out.dll dllmain.obj "{}"'.format(' -EXPORT:'.join(function_names), input_hat_binary_path)
|
||||
os.system(linker_command_line)
|
||||
shutil.copyfile("out.dll", output_hat_binary_path)
|
||||
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
|
||||
#ifndef __optimized_matmul__
|
||||
#define __optimized_matmul__
|
||||
|
||||
#ifdef TOML
|
||||
[description]
|
||||
author = ""
|
||||
version = ""
|
||||
license_url = ""
|
||||
|
||||
[functions]
|
||||
[functions.optimized_matmul_py_66985f63]
|
||||
name = 'optimized_matmul_py_66985f63'
|
||||
description = ''
|
||||
calling_convention = "cdecl"
|
||||
arguments = [{name = '', description = '', logical_type = "affine_array", declared_type = 'float*', element_type = 'float', usage = "input_output", shape = [ 784, 128 ], affine_map = [ 128, 1 ], affine_offset = 0}, {name = '', description = '', logical_type = "affine_array", declared_type = 'float*', element_type = 'float', usage = "input_output", shape = [ 128, 512 ], affine_map = [ 512, 1 ], affine_offset = 0}, {name = '', description = '', logical_type = "affine_array", declared_type = 'float*', element_type = 'float', usage = "input_output", shape = [ 784, 512 ], affine_map = [ 512, 1 ], affine_offset = 0}]
|
||||
return = {name = '', description = '', logical_type = "void", declared_type = 'void', element_type = 'void', usage = "output"}
|
||||
|
||||
[target]
|
||||
[target.required]
|
||||
os = "linux"
|
||||
|
||||
[target.required.CPU]
|
||||
architecture = "x86_64"
|
||||
extensions = ["+sse2", "-tsxldtrk", "+cx16", "+sahf", "-tbm", "-avx512ifma", "-sha", "-gfni", "-fma4", "-vpclmulqdq", "+prfchw", "+bmi2", "-cldemote", "+fsgsbase", "-ptwrite", "-amx-tile", "-uintr", "+popcnt", "-widekl", "+aes", "-avx512bitalg", "-movdiri", "+xsaves", "-avx512er", "-avxvnni", "-avx512vnni", "-amx-bf16", "-avx512vpopcntdq", "-pconfig", "+clwb", "+avx512f", "+xsavec", "-clzero", "-pku", "+mmx", "-lwp", "-rdpid", "-xop", "+rdseed", "-waitpkg", "-kl", "-movdir64b", "-sse4a", "+avx512bw", "+clflushopt", "+xsave", "-avx512vbmi2", "+64bit", "+avx512vl", "-serialize", "-hreset", "+invpcid", "+avx512cd", "+avx", "-vaes", "-avx512bf16", "+cx8", "+fma", "+rtm", "+bmi", "-enqcmd", "+rdrnd", "-mwaitx", "+sse4.1", "+sse4.2", "+avx2", "+fxsr", "-wbnoinvd", "+sse", "+lzcnt", "+pclmul", "-prefetchwt1", "+f16c", "+ssse3", "-sgx", "-shstk", "+cmov", "-avx512vbmi", "-amx-int8", "+movbe", "-avx512vp2intersect", "+xsaveopt", "+avx512dq", "+adx", "-avx512pf", "+sse"]
|
||||
|
||||
[dependencies]
|
||||
link_target = "optimized_matmul.o"
|
||||
deploy_files = []
|
||||
dynamic = []
|
||||
|
||||
[compiled_with]
|
||||
compiler = ''
|
||||
flags = ''
|
||||
crt = ''
|
||||
libraries = []
|
||||
|
||||
[declaration]
|
||||
code = '''
|
||||
#endif // __TOML__
|
||||
//
|
||||
// Header for Accera library optimized_matmul
|
||||
//
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C"
|
||||
{
|
||||
#endif // defined(__cplusplus)
|
||||
|
||||
//
|
||||
// Functions
|
||||
//
|
||||
|
||||
void optimized_matmul_py_66985f63(float*, float*, float*);
|
||||
|
||||
#ifndef __optimized_matmul_py_DEFINED__
|
||||
#define __optimized_matmul_py_DEFINED__
|
||||
void (*optimized_matmul_py)(float*, float*, float*) = optimized_matmul_py_66985f63;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} // extern "C"
|
||||
#endif // defined(__cplusplus)
|
||||
|
||||
#ifdef __TOML__
|
||||
'''
|
||||
|
||||
#endif // TOML
|
||||
|
||||
#endif // __optimized_matmul__
|
Двоичные данные
tools/test/data/linux/optimized_matmul.o
Двоичные данные
tools/test/data/linux/optimized_matmul.o
Двоичный файл не отображается.
|
@ -1,74 +0,0 @@
|
|||
|
||||
#ifndef __optimized_matmul__
|
||||
#define __optimized_matmul__
|
||||
|
||||
#ifdef TOML
|
||||
[description]
|
||||
author = ""
|
||||
version = ""
|
||||
license_url = ""
|
||||
|
||||
[functions]
|
||||
[functions.optimized_matmul_py_5a7abfb9]
|
||||
name = 'optimized_matmul_py_5a7abfb9'
|
||||
description = ''
|
||||
calling_convention = "cdecl"
|
||||
arguments = [{name = '', description = '', logical_type = "affine_array", declared_type = 'float*', element_type = 'float', usage = "input_output", shape = [ 784, 128 ], affine_map = [ 128, 1 ], affine_offset = 0}, {name = '', description = '', logical_type = "affine_array", declared_type = 'float*', element_type = 'float', usage = "input_output", shape = [ 128, 512 ], affine_map = [ 512, 1 ], affine_offset = 0}, {name = '', description = '', logical_type = "affine_array", declared_type = 'float*', element_type = 'float', usage = "input_output", shape = [ 784, 512 ], affine_map = [ 512, 1 ], affine_offset = 0}]
|
||||
return = {name = '', description = '', logical_type = "void", declared_type = 'void', element_type = 'void', usage = "output"}
|
||||
|
||||
[target]
|
||||
[target.required]
|
||||
os = "windows"
|
||||
|
||||
[target.required.CPU]
|
||||
architecture = "x86_64"
|
||||
extensions = ["+sse2", "-tsxldtrk", "+cx16", "+sahf", "-tbm", "-avx512ifma", "-sha", "-gfni", "-fma4", "-vpclmulqdq", "+prfchw", "+bmi2", "-cldemote", "+fsgsbase", "-ptwrite", "-amx-tile", "-uintr", "+popcnt", "-widekl", "+aes", "-avx512bitalg", "-movdiri", "+xsaves", "-avx512er", "-avxvnni", "-avx512vnni", "-amx-bf16", "-avx512vpopcntdq", "-pconfig", "+clwb", "+avx512f", "+xsavec", "-clzero", "-pku", "+mmx", "-lwp", "-rdpid", "-xop", "+rdseed", "-waitpkg", "-kl", "-movdir64b", "-sse4a", "+avx512bw", "+clflushopt", "+xsave", "-avx512vbmi2", "+64bit", "+avx512vl", "-serialize", "-hreset", "+invpcid", "+avx512cd", "+avx", "-vaes", "-avx512bf16", "+cx8", "+fma", "+rtm", "+bmi", "-enqcmd", "+rdrnd", "-mwaitx", "+sse4.1", "+sse4.2", "+avx2", "+fxsr", "-wbnoinvd", "+sse", "+lzcnt", "+pclmul", "-prefetchwt1", "+f16c", "+ssse3", "-sgx", "-shstk", "+cmov", "-avx512vbmi", "-amx-int8", "+movbe", "-avx512vp2intersect", "+xsaveopt", "+avx512dq", "+adx", "-avx512pf", "+sse"]
|
||||
|
||||
[dependencies]
|
||||
link_target = "optimized_matmul.obj"
|
||||
deploy_files = []
|
||||
dynamic = []
|
||||
|
||||
[compiled_with]
|
||||
compiler = ''
|
||||
flags = ''
|
||||
crt = ''
|
||||
libraries = []
|
||||
|
||||
[declaration]
|
||||
code = '''
|
||||
#endif // __TOML__
|
||||
//
|
||||
// Header for Accera library optimized_matmul
|
||||
//
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C"
|
||||
{
|
||||
#endif // defined(__cplusplus)
|
||||
|
||||
//
|
||||
// Functions
|
||||
//
|
||||
|
||||
void optimized_matmul_py_5a7abfb9(float*, float*, float*);
|
||||
|
||||
#ifndef __optimized_matmul_py_DEFINED__
|
||||
#define __optimized_matmul_py_DEFINED__
|
||||
void (*optimized_matmul_py)(float*, float*, float*) = optimized_matmul_py_5a7abfb9;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} // extern "C"
|
||||
#endif // defined(__cplusplus)
|
||||
|
||||
#ifdef __TOML__
|
||||
'''
|
||||
|
||||
#endif // TOML
|
||||
|
||||
#endif // __optimized_matmul__
|
Двоичные данные
tools/test/data/windows/optimized_matmul.obj
Двоичные данные
tools/test/data/windows/optimized_matmul.obj
Двоичный файл не отображается.
|
@ -0,0 +1 @@
|
|||
accera
|
|
@ -2,7 +2,7 @@
|
|||
#!/usr/bin/env python3
|
||||
import unittest
|
||||
import sys, os
|
||||
from pathlib import Path
|
||||
import accera as acc
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
|
||||
|
||||
|
@ -10,12 +10,23 @@ from benchmark_hat_package import run_benchmark
|
|||
from hat_to_dynamic import get_platform
|
||||
|
||||
class BenchmarkHATPackage_test(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.hatfile_path = Path(os.path.dirname(__file__)) / "data" / get_platform().lower() / "optimized_matmul.hat"
|
||||
|
||||
@unittest.skipUnless(get_platform().lower() == "windows", "Flaky on non-windows")
|
||||
def test_benchmark(self):
|
||||
run_benchmark(self.hatfile_path, store_in_hat=False, batch_size=2, min_time_in_sec=1, input_sets_minimum_size_MB=1)
|
||||
A = acc.Array(role=acc.Array.Role.INPUT, shape=(256, 256))
|
||||
B = acc.Array(role=acc.Array.Role.INPUT, shape=(256, 256))
|
||||
C = acc.Array(role=acc.Array.Role.INPUT_OUTPUT, shape=(256, 256))
|
||||
|
||||
nest = acc.Nest(shape=(256, 256, 256))
|
||||
i, j, k = nest.get_indices()
|
||||
|
||||
@nest.iteration_logic
|
||||
def _():
|
||||
C[i, j] += A[i, k] * B[k, j]
|
||||
|
||||
package = acc.Package()
|
||||
package.add_function(nest, args=(A, B, C), base_name="test_function")
|
||||
package.build(name="BenchmarkHATPackage_test_benchmark", output_dir="test_acccgen")
|
||||
|
||||
run_benchmark("test_acccgen/BenchmarkHATPackage_test_benchmark.hat", store_in_hat=False, batch_size=2, min_time_in_sec=1, input_sets_minimum_size_MB=1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
import unittest
|
||||
import sys, os
|
||||
from pathlib import Path
|
||||
import accera as acc
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
|
||||
|
||||
|
@ -9,30 +9,40 @@ from hat import load
|
|||
from hat_to_dynamic import get_platform, create_dynamic_package
|
||||
|
||||
class HAT_test(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.hatfile_path = Path(os.path.dirname(__file__)) / "data" / get_platform().lower() / "optimized_matmul.hat"
|
||||
self.dyn_hatfile_path = Path(os.path.dirname(__file__)) / "data" / get_platform().lower() / "optimized_matmul.HAT_test.hat"
|
||||
|
||||
@unittest.skipUnless(get_platform().lower() == "windows", "Flaky on non-windows")
|
||||
def test_load(self):
|
||||
import numpy as np
|
||||
import accera as acc
|
||||
|
||||
create_dynamic_package(self.hatfile_path, self.dyn_hatfile_path)
|
||||
package = load(self.dyn_hatfile_path)
|
||||
# Generate a HAT package
|
||||
A = acc.Array(role=acc.Array.Role.INPUT, shape=(16, 16))
|
||||
B = acc.Array(role=acc.Array.Role.INPUT_OUTPUT, shape=(16, 16))
|
||||
|
||||
nest = acc.Nest(shape=(16, 16))
|
||||
i, j = nest.get_indices()
|
||||
|
||||
@nest.iteration_logic
|
||||
def _():
|
||||
B[i, j] += A[i, j]
|
||||
|
||||
package = acc.Package()
|
||||
package.add_function(nest, args=(A, B), base_name="test_function")
|
||||
package.build(name="HAT_test_load", output_dir="test_acccgen")
|
||||
|
||||
create_dynamic_package("test_acccgen/HAT_test_load.hat", "test_acccgen/HAT_test_load.dyn.hat")
|
||||
package = load("test_acccgen/HAT_test_load.dyn.hat")
|
||||
|
||||
for name in package.names:
|
||||
print(name)
|
||||
|
||||
# create numpy arguments with the correct shape and dtype
|
||||
A = np.random.rand(784, 128).astype(np.float32)
|
||||
B = np.random.rand(128, 512).astype(np.float32)
|
||||
C = np.random.rand(784, 512).astype(np.float32)
|
||||
C_ref = C + A @ B
|
||||
A = np.random.rand(16, 16).astype(np.float32)
|
||||
B = np.random.rand(16, 16).astype(np.float32)
|
||||
B_ref = B + A
|
||||
|
||||
# call the function
|
||||
name = package.names[0]
|
||||
optimized_matmul = package[name]
|
||||
optimized_matmul(A, B, C)
|
||||
test_function = package[name]
|
||||
test_function(A, B)
|
||||
|
||||
# check for correctness
|
||||
np.testing.assert_allclose(C, C_ref)
|
||||
np.testing.assert_allclose(B, B_ref)
|
Загрузка…
Ссылка в новой задаче