# Exposing Metal acceleration libraries on MacOS for Fortran

**URL:** <https://fortran-lang.discourse.group/t/exposing-metal-acceleration-libraries-on-macos-for-fortran/5030>\
**Category:** Uncategorized\
**Created:** [January 14, 2023, 4:41pm UTC](https://fortran-lang.discourse.group/t/exposing-metal-acceleration-libraries-on-macos-for-fortran/5030 "2023-01-14T16:41:27Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Walt](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/walt/32/2465_2.png) [@Walt](https://fortran-lang.discourse.group/u/Walt)\
**Post date:** [January 14, 2023, 4:41pm UTC](https://fortran-lang.discourse.group/t/exposing-metal-acceleration-libraries-on-macos-for-fortran/5030/1 "2023-01-14T16:41:27Z")

</div>

I was tinkering with interfacing Metal performance shaders to Fortran the other night and I was curious if this would be a useful addition to the Fortran community.

According to this poll about 33% of Fortran users are on MacOS so it may be of use.

> [@Fortran Developer's Poll - 2022](https://fortran-lang.discourse.group/t/fortran-developers-poll-2022/3877):
>
> As a continuation of an older post about Code Editors used to write Fortran: [Code Editors/IDEs used for Fortran](https://fortran-lang.discourse.group/t/code-editors-ides-used-for-fortran/3139). I thought it would be interesting to get some statistics about the OSs and processors people use to develop Fortran on/for, as well as the type of parallelism, if any, folks use. If the survey has a good turn out maybe it can become an annual poll to help us identify areas that are of high demand and are in need of support. Feel free to share this survey with your teams/research gro…

To me the lack of CUDA support and deprecation of OpenCL is a real problem for MacOS users looking for GPU acceleration, the only supported way to access GPU acceleration is through Metal at this point. Unfortunately it’s obfuscated in an Objective C interface which requires a good deal of ObjC-\>C-\>Fortran hacking work that most people aren’t willing to take on.

Looking at the Metal Performance Shaders documentation ([Metal Performance Shaders | Apple Developer Documentation](https://developer.apple.com/documentation/metalperformanceshaders)) there are a few interfaces I would be interested, the Matrix and Neural network interfaces are of interest to me but maybe its just me… my question is what would be of interest in the MPS library to the MacOS Fortran community? I could expose Metal compute shaders also, which MPS are based on…

Would it be worth posting on GitHub and leave it open to community support? There are only a few cases I would want but the community may be interested in exposing more functionality.

Thanks

Walt

---

<div class="post-metadata">

**Author:** ![ivanpribec](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/ivanpribec/32/3290_2.png) [@ivanpribec](https://fortran-lang.discourse.group/u/ivanpribec)\
**Post date:** [January 14, 2023, 8:08pm UTC](https://fortran-lang.discourse.group/t/exposing-metal-acceleration-libraries-on-macos-for-fortran/5030/2 "2023-01-14T20:08:06Z")

</div>

I’ve been tinkering with some graphical applications in Fortran and was looking at what is available on MacOS. While compiling a test OpenGL application, a number of deprecation warnings popped up, suggesting I use Metal. I found there is a Metal C++ interface ([Getting started with Metal-cpp - Metal - Apple Developer](https://developer.apple.com/metal/cpp/)), but due to the complexity of it, and the need for double bridging C++ → C → Fortran, I gave up immediately.

IMHO, without an automatic interface generation tool, it’s only feasible on an as needed basis. In other words, you should only expose the minimum set of functionality needed to get the job done.

> [@Walt](#):
>
> Would it be worth posting on GitHub and leave it open to community support?

Absolutely, even if the full interface is not covered, at least we can follow your example and potentially expose more of the interface over time.

It’s also a shame there seems to be no SYCL support for Mac GPU devices. This would be an easier path for those that already use oneAPI on other devices. It might be possible (in the future) by translating [SPIR-V](https://en.wikipedia.org/wiki/Standard_Portable_Intermediate_Representation) to MSL, as @JeffH suggested [here](https://stackoverflow.com/questions/62110463/using-sycl-1-2-in-macos). The [SPIRV-Cross](https://github.com/KhronosGroup/SPIRV-Cross) tool has seen some progress in disassembling/lifting SPIR-V to MSL.

---

<div class="post-metadata">

**Author:** ![FedericoPerini](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/federicoperini/32/1750_2.png) [@FedericoPerini](https://fortran-lang.discourse.group/u/FedericoPerini)\
**Post date:** [January 14, 2023, 8:55pm UTC](https://fortran-lang.discourse.group/t/exposing-metal-acceleration-libraries-on-macos-for-fortran/5030/3 "2023-01-14T20:55:09Z")

</div>

> [@Walt](#):
>
> Would it be worth posting on GitHub and leave it open to community support?

As an avid Fortran and Mac user I’d be eager to see at least an example online, I think it would be great for the Fortran community to strengthen the fact that Fortran is not a closed ecosystem. Thank you if you’re willing to do that!

On a broader perspective, I think most Fortran applications/codes target scientific computing, so they’re hardly Mac-centric. Mac OS may cover some fraction (10%? 30%?) of the total “OS share”, but that probably sees Unix and MS Windows come first. In this scenario, using cross-platform libraries makes a lot more sense IMHO because it maximizes coding productivity: OpenCL is my choice for GPU programming.

It’s true Apple is trying to move people to use Metal but I doubt support for OpenCL is going to be ditched anytime soon, since so many Mac apps still use it.

---

<div class="post-metadata">

**Author:** ![FedericoPerini](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/federicoperini/32/1750_2.png) [@FedericoPerini](https://fortran-lang.discourse.group/u/FedericoPerini)\
**Post date:** [January 14, 2023, 9:04pm UTC](https://fortran-lang.discourse.group/t/exposing-metal-acceleration-libraries-on-macos-for-fortran/5030/4 "2023-01-14T21:04:24Z")

</div>

> [@ivanpribec](#):
>
> and the need for double bridging C++ → C → Fortran

[this repo](https://github.com/recp/cmt) provides a C interface already, looks like it could be a good starting point for a Fortran interface.

---

<div class="post-metadata">

**Author:** ![Walt](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/walt/32/2465_2.png) [@Walt](https://fortran-lang.discourse.group/u/Walt)\
**Post date:** [January 15, 2023, 4:18pm UTC](https://fortran-lang.discourse.group/t/exposing-metal-acceleration-libraries-on-macos-for-fortran/5030/5 "2023-01-15T16:18:35Z")

</div>

I don’t think the Apple cpu’s support OpenCL, I checked a few months ago and it appeared that only a CPU device was available… I wrote a OpenGL 4.6 interface on top of Metal (MGL) last year and it works well but the feedback from the Fortran community on OpenGL was the data post processed and displayed using other interface language.

I worked at Apple and on OpenCL 1.0 in 2008… and I can say it hasn’t changed much, the last supported version was 1.2 and thats where Apple dropped support for OpenCL.

But I do know that the some of the same people that worked on OpenCL 1.0 also work in the Accelerate group and they optimized the MPS so we can expect MPS to use optimized kernels.

The problem I have with Metal is that it doesn’t support doubles, but thats what we get… Metal is targeted at IOS devices and there’s no need for double support on IOS.

---

<div class="post-metadata">

**Author:** ![ivanpribec](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/ivanpribec/32/3290_2.png) [@ivanpribec](https://fortran-lang.discourse.group/u/ivanpribec)\
**Post date:** [February 8, 2023, 9:49pm UTC](https://fortran-lang.discourse.group/t/exposing-metal-acceleration-libraries-on-macos-for-fortran/5030/6 "2023-02-08T21:49:18Z")

</div>

You could try to emulate FP64 with a pair of floats (not the same range as IEEE double). Here are a few threads in this direction:

- [c++ - Emulate "double" using 2 "float"s - Stack Overflow](https://stackoverflow.com/questions/6769881/emulate-double-using-2-floats)
- [floating point - Emulating FP64 with 2 FP32 on a GPU - Stack Overflow](https://stackoverflow.com/questions/29344800/emulating-fp64-with-2-fp32-on-a-gpu)
- [Apple GPUs don't support FP64 at all - and Apple only provides Metal as a (suppo... | Hacker News](https://news.ycombinator.com/item?id=31199437)

Taking the code snippet in the second link above, multiplying two `dblefloats` could be achieved with something like this:

```cpp
#include <metal_common> // fma

typedef float2 dblfloat; // .y = head, .x = tail

dblfloat mul_dblfloat (dblfloat x, dblfloat y)
{
    dblfloat t, z;
    float sum;
    t.y = x.y * y.y;
    t.x = fma (x.y, y.y, -t.y);
    t.x = fma (x.x, y.x, t.x);
    t.x = fma (x.y, y.x, t.x);
    t.x = fma (x.x, y.y, t.x);
    /* normalize result */
    sum = t.y + t.x;
    z.x = (t.y - sum) + t.x;
    z.y = sum;
    return z;
}

```

I’m guessing however you’d have to implement the kernels yourself, and wouldn’t be able to reuse the existing shaders.

---

<div class="post-metadata">

**Author:** ![Walt](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/walt/32/2465_2.png) [@Walt](https://fortran-lang.discourse.group/u/Walt)\
**Post date:** [February 8, 2023, 10:29pm UTC](https://fortran-lang.discourse.group/t/exposing-metal-acceleration-libraries-on-macos-for-fortran/5030/7 "2023-02-08T22:29:57Z")

</div>

I am in the process of finishing an initial pass right now, I was going to release something in a couple of days.

I abstracted the interface to something akin to OpenGL to avoid passing pointers around and keeping the pointers inside ObjectiveC land which preserves the reference counts.

There are a lot of kernel types, I just don’t want to be supporting every call entry so I am abstracting kernel generation to a target which indicates which type of MPS kernel it is.

This way you get a generic interface driven by enums and named objects which simplifies all the pointer handling.

Here is a quick look at the C header for the interface, right now I support mpsMatrixMult / mpsMatrixSum and Metal shaders… **its not complete as I have just switched over to kernel types**

**typedef**  **enum** {

kInvalid = 0,

kFloat32,

kFloat16,

kInt64,

kInt32,

kInt16,

kInt8,

kUint64,

kUInt32,

kUInt16,

kUInt8,

kBool,

kComplexFloat32,

kComplextFloat16,

kData,

kMaxMPSType

} DataType;

**typedef**  **enum** {

kShader = 0,

kMatrixMult,

kMatrixSum

} KernelTarget;

**typedef**  **enum** {

kAlpha,

kBeta,

kTranspose\_A,

kTranspose\_B,

kMaxOption

} MatrixOption;

// enable functions, mps kernels can take optional values

**void** mpsEnable(KernelTarget target, MatrixOption option);

**void** mpsDisable(KernelTarget target, MatrixOption option);

// set options value, mps kernels

**void** mpsSetOptionValuef(KernelTarget target, MatrixOption option, **float** value);

**void** mpsSetOptionValued(KernelTarget target, MatrixOption option, **double** value);

// gen type functions

**unsigned** mpsGenBuffer(size\_t size, **void** \*data);

**unsigned** mpsGenVector( **unsigned** len, **void** \*data, DataType type);

**unsigned** mpsGenMatrix( **unsigned** rows, **unsigned** cols, **void** \*data, DataType type);

// gen kernel functions

**unsigned** mpsGenMatrixMultKernel( **unsigned** rows, **unsigned** cols);

**unsigned** mpsGenMatrixSumKernel( **unsigned** rows, **unsigned** cols);

// data functions

**void** mpsSubBuffer( **unsigned** name, **void** \*src, size\_t size, size\_t offset);

**void** mpsSubBufferVector( **unsigned** name, **void** \*src, size\_t len, size\_t offset, DataType type);

**void** mpsSubBufferMat( **unsigned** name, **void** \*src, **unsigned** row, **unsigned** col, **unsigned** width, **unsigned** height, DataType type);

// synchronize data from GPU to CPU after a GPU operation modifies the contents

**void** mpsSyncBuffer( **unsigned** name);

**void** mpsSyncVector( **unsigned** name);

**void** mpsSyncMatrix( **unsigned** name);

// copy metals copy of the data to a dst pointer in the same format it was submitted in

**void** mpsGetBuffer( **unsigned** name, **void** \*dst);

**void** mpsGetVector( **unsigned** name, **void** \*dst);

**void** mpsGetMatrix( **unsigned** name, **void** \*dst);

// command buffer flush routines

**void** mpsFlush( **void** );

**void** mpsFinish( **void** );

This is a simple test of MatrixMult, again it’s missing the kernel target support but it’s getting there.

matA = mpsGenMatrix(rows, cols, dataA, kFloat32);

matB = mpsGenMatrix(rows, cols, dataB, kFloat32);

result = mpsGenMatrix(rows, cols, dataResult, kFloat32);

mpsEnable(kMatrixMult, kAlpha);

mpsSetOptionValued(kMatrixMult, kAlpha, 0.5);

mpsMatrixMult(matA, matB, result);

mpsSyncMatrix(result);

mpsFinish();

mpsGetMatrix(result, dataResult);

I will check it in github for review if people are interested in expanding on the work.

Walt
