Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@
</ItemGroup>

<ItemGroup Condition="'$(TargetArchitecture)' == 'arm64' Or '$(TargetArchitecture)' == 'arm'">
<NativeAssemblyReference Remove="MklImports"/>
<!-- MklImports (arm shim) and SymSgdNative are built on arm/arm64, so keep
them here so they are copied next to the managed assemblies. -->
<NativeAssemblyReference Remove="CpuMathNative"/>
<NativeAssemblyReference Remove="FastTreeNative"/>
<NativeAssemblyReference Remove="SymSgdNative"/>
<NativeAssemblyReference Remove="MklProxyNative"/>
<NativeAssemblyReference Remove="libiomp5md"/>
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -834,8 +834,8 @@ private static unsafe class Native

[DllImport(NativePath), SuppressUnmanagedCodeSecurity]
private static extern void LearnAll(int totalNumInstances, int* instSizes, int** instIndices,
float** instValues, float* labels, bool tuneLR, ref float lr, float l2Const, float piw, float* weightVector, ref float bias,
int numFeatres, int numPasses, int numThreads, bool tuneNumLocIter, ref int numLocIter, float tolerance, bool needShuffle, bool shouldInitialize,
float** instValues, float* labels, [MarshalAs(UnmanagedType.I1)] bool tuneLR, ref float lr, float l2Const, float piw, float* weightVector, ref float bias,
int numFeatres, int numPasses, int numThreads, [MarshalAs(UnmanagedType.I1)] bool tuneNumLocIter, ref int numLocIter, float tolerance, [MarshalAs(UnmanagedType.I1)] bool needShuffle, [MarshalAs(UnmanagedType.I1)] bool shouldInitialize,
State* state, ChannelCallBack info);

/// <summary>
Expand Down
6 changes: 4 additions & 2 deletions src/Native/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -265,9 +265,11 @@ if(NOT ${ARCHITECTURE} MATCHES "arm.*")
add_subdirectory(CpuMathNative)
add_subdirectory(FastTreeNative)
add_subdirectory(MklProxyNative)
# TODO: once we fix the 4 intel MKL methods, SymSgdNative will need to go back in.
add_subdirectory(SymSgdNative)
endif()
else()
add_subdirectory(MklImportsArm)
add_subdirectory(SymSgdNative)
endif()

if(${ARCHITECTURE} MATCHES "[xX].*64")
add_subdirectory(OneDalNative)
Expand Down
23 changes: 23 additions & 0 deletions src/Native/MklImportsArm/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
project(MklImportsArm)

# On ARM platforms, Intel MKL is not available. This target provides a small,
# self-contained libMklImports covering exactly the symbols SymSGD needs
# (dense/sparse level-1 CBLAS) plus DFTI stubs, with NO external BLAS
# dependency. This is required because the CI cross-compilation sysroots do
# not ship OpenBLAS or any system BLAS.

set(SOURCES
MklImportsArm.c
)

if(NOT WIN32)
list(APPEND SOURCES ${VERSION_FILE_PATH})
SET(CMAKE_SKIP_BUILD_RPATH FALSE)
SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
SET(CMAKE_INSTALL_RPATH "$ORIGIN/")
endif()

add_library(MklImports SHARED ${SOURCES} ${RESOURCES})

install_library_and_symbols(MklImports)
118 changes: 118 additions & 0 deletions src/Native/MklImportsArm/MklImportsArm.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

// ARM replacement for Intel MKL (libMklImports.so).
//
// This provides a small, self-contained libMklImports for arm/arm64 that
// covers exactly the symbols SymSGD needs, with no external BLAS dependency.
// That is important because the cross-compilation sysroots used in CI do not
// ship OpenBLAS (or any system BLAS), so linking against one is not an option.
//
// SymSGD uses only four CBLAS routines:
// * cblas_sdot / cblas_saxpy - dense single-precision dot and AXPY,
// * cblas_sdoti / cblas_saxpyi - their sparse counterparts (MKL extensions).
// All four are implemented below as plain C loops. With -O3 the compiler
// autovectorizes the dense paths to NEON, matching hand-written BLAS closely.
//
// MKL DFTI (FFT) functions are stubbed — they are referenced by the managed
// MKL Components initializer but not used by SymSGD. The stubs return error
// codes so any actual FFT call fails cleanly rather than crashing.

// The native build is compiled with -fvisibility=hidden, so every symbol that
// must be visible to SymSgdNative (the CBLAS routines) or to the managed
// P/Invoke layer (DftiErrorMessage) has to be exported explicitly.
#if defined(_WIN32)
#define MKLIMPORTS_EXPORT __declspec(dllexport)
#else
#define MKLIMPORTS_EXPORT __attribute__((visibility("default")))
#endif

// --- Dense BLAS (CBLAS, level 1) ---

MKLIMPORTS_EXPORT float cblas_sdot(const int n, const float *x, const int incx,
const float *y, const int incy)
{
float result = 0.0f;
if (incx == 1 && incy == 1)
{
for (int i = 0; i < n; i++)
result += x[i] * y[i];
}
else
{
int ix = incx < 0 ? (1 - n) * incx : 0;
int iy = incy < 0 ? (1 - n) * incy : 0;
for (int i = 0; i < n; i++, ix += incx, iy += incy)
result += x[ix] * y[iy];
}
return result;
}

MKLIMPORTS_EXPORT void cblas_saxpy(const int n, const float a, const float *x, const int incx,
float *y, const int incy)
{
if (a == 0.0f)
return;
if (incx == 1 && incy == 1)
{
for (int i = 0; i < n; i++)
y[i] += a * x[i];
}
else
{
int ix = incx < 0 ? (1 - n) * incx : 0;
int iy = incy < 0 ? (1 - n) * incy : 0;
for (int i = 0; i < n; i++, ix += incx, iy += incy)
y[iy] += a * x[ix];
}
}

// --- Sparse BLAS (MKL extensions, not in standard BLAS) ---

MKLIMPORTS_EXPORT void cblas_saxpyi(const int nz, const float a,
const float *x, const int *indx, float *y)
{
for (int i = 0; i < nz; i++)
y[indx[i]] += a * x[i];
}

MKLIMPORTS_EXPORT float cblas_sdoti(const int nz, const float *x,
const int *indx, const float *y)
{
float result = 0.0f;
for (int i = 0; i < nz; i++)
result += x[i] * y[indx[i]];
return result;
}

// --- DFTI (FFT) stubs ---

MKLIMPORTS_EXPORT const char* DftiErrorMessage(long status)
{
return "DFTI not available (arm64 MKL shim build)";
}

MKLIMPORTS_EXPORT long DftiCreateDescriptor(void **h, int precision, int domain, int dim, ...)
{
*h = (void*)0;
return -1;
}

MKLIMPORTS_EXPORT long DftiSetValue(void *h, int param, ...)
{
return -1;
}

MKLIMPORTS_EXPORT long DftiCommitDescriptor(void *h) { return -1; }
MKLIMPORTS_EXPORT long DftiComputeForward(void *h, ...) { return -1; }
MKLIMPORTS_EXPORT long DftiComputeBackward(void *h, ...) { return -1; }
MKLIMPORTS_EXPORT long DftiFreeDescriptor(void **h)
{
// Match MKL's contract: clear the caller's handle after freeing so callers
// that rely on the descriptor being nulled out (e.g. the managed
// FreeDescriptor(ref IntPtr) P/Invoke) behave correctly.
if (h != (void*)0)
*h = (void*)0;
return 0;
}
7 changes: 6 additions & 1 deletion src/Native/SymSgdNative/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ else()
endif()
endif()

if(NOT ${ARCHITECTURE} MATCHES "arm.*")
if(${ARCHITECTURE} MATCHES "arm.*")
# On ARM, MklImports is built from MklImportsArm, a self-contained CBLAS
# shim implemented as plain C loops (no external BLAS dependency).
# Link against the CMake target directly.
set(MKL_LIBRARY MklImports)
else()
find_library(MKL_LIBRARY MklImports HINTS ${MKL_LIB_PATH})
endif()

Expand Down
18 changes: 12 additions & 6 deletions src/Native/SymSgdNative/SparseBLAS.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,23 @@
#pragma once
#include "../Stdafx.h"

extern "C" float __cdecl cblas_sdot(const int vecSize, const float* denseVecX, const int incX, const float* denseVecY, const int incY);
extern "C" float __cdecl cblas_sdoti(const int sparseVecSize, const float* sparseVecValues, const int* sparseVecIndices, float* denseVec);
extern "C" void __cdecl cblas_saxpy(const int vecSize, const float coef, const float* denseVecX, const int incX, float* denseVecY, const int incY);
extern "C" void __cdecl cblas_saxpyi(const int sparseVecSize, const float coef, const float* sparseVecValues, const int* sparseVecIndices, float* denseVec);
#ifdef _WIN32
#define CBLAS_CALLING_CONV __cdecl
#else
#define CBLAS_CALLING_CONV
#endif

extern "C" float CBLAS_CALLING_CONV cblas_sdot(const int vecSize, const float* denseVecX, const int incX, const float* denseVecY, const int incY);
extern "C" float CBLAS_CALLING_CONV cblas_sdoti(const int sparseVecSize, const float* sparseVecValues, const int* sparseVecIndices, const float* denseVec);
extern "C" void CBLAS_CALLING_CONV cblas_saxpy(const int vecSize, const float coef, const float* denseVecX, const int incX, float* denseVecY, const int incY);
Comment thread
vladimir-aubrecht marked this conversation as resolved.
extern "C" void CBLAS_CALLING_CONV cblas_saxpyi(const int sparseVecSize, const float coef, const float* sparseVecValues, const int* sparseVecIndices, float* denseVec);

float SDOT(const int vecSize, const float* denseVecX, const float* denseVecY)
{
return cblas_sdot(vecSize, denseVecX, 1, denseVecY, 1);
}

float SDOTI(const int sparseVecSize, const int* sparseVecIndices, const float* sparseVecValues, float* denseVec)
float SDOTI(const int sparseVecSize, const int* sparseVecIndices, const float* sparseVecValues, const float* denseVec)
{
return cblas_sdoti(sparseVecSize, sparseVecValues, sparseVecIndices, denseVec);
}
Expand All @@ -28,4 +34,4 @@ void SAXPY(const int vecSize, const float* denseVecX, float* denseVecY, float co
void SAXPYI(const int sparseVecSize, const int* sparseVecIndices, const float* sparseVecValues, float* denseVec, float coef)
{
cblas_saxpyi(sparseVecSize, coef, sparseVecValues, sparseVecIndices, denseVec);
}
}
Loading