public class MPSNNGraph extends MPSKernel implements NSCopying, NSSecureCoding
Optimized representation of a graph of MPSNNImageNodes and MPSNNFilterNodes
Once you have prepared a graph of MPSNNImageNodes and MPSNNFilterNodes (and if needed MPSNNStateNodes), you may initialize a MPSNNGraph using the MPSNNImageNode that you wish to appear as the result. The MPSNNGraph object will introspect the graph representation and determine which nodes are needed for inputs, and which nodes are produced as output state (if any). Nodes which are not needed to calculate the result image node are ignored. Some nodes may be internally concatenated with other nodes for better performance.
Note: the MPSNNImageNode that you choose as the result node may be interior to a graph. This feature is provided as a means to examine intermediate computations in the full graph for debugging purposes.
During MPSNNGraph construction, the graph attached to the result node will be parsed and reduced to an optimized representation. This representation may be saved using the NSSecureCoding protocol for later recall.
When decoding a MPSNNGraph using a NSCoder, it will be created against
the system default MTLDevice. If you would like to set the MTLDevice,
your NSCoder should conform to the
You may find it helpful to set MPSKernelOptionsVerbose on the graph when
debugging. To turn this on during MPSKernel initialization (including
MPSNNGraph initialization) set the MPS_LOG_INFO environment variable.
There is a lot of information about what optimizations are done to your
graph, including some information on why certain optimizations were not
made.
| Modifier and Type | Class and Description |
|---|---|
static interface |
MPSNNGraph.Block_executeAsyncWithSourceImagesCompletionHandler |
NSObject.Function_instanceMethodForSelector_ret, NSObject.Function_methodForSelector_ret| Modifier | Constructor and Description |
|---|---|
protected |
MPSNNGraph(org.moe.natj.general.Pointer peer) |
| Modifier and Type | Method and Description |
|---|---|
boolean |
_supportsSecureCoding()
This property must return YES on all classes that allow secure coding.
|
static boolean |
accessInstanceVariablesDirectly() |
static MPSNNGraph |
alloc() |
static MPSNNGraph |
allocWithZone(org.moe.natj.general.ptr.VoidPtr zone) |
static boolean |
automaticallyNotifiesObserversForKey(java.lang.String key) |
static void |
cancelPreviousPerformRequestsWithTarget(java.lang.Object aTarget) |
static void |
cancelPreviousPerformRequestsWithTargetSelectorObject(java.lang.Object aTarget,
org.moe.natj.objc.SEL aSelector,
java.lang.Object anArgument) |
static NSArray<java.lang.String> |
classFallbacksForKeyedArchiver() |
static org.moe.natj.objc.Class |
classForKeyedUnarchiver() |
java.lang.Object |
copyWithZone(org.moe.natj.general.ptr.VoidPtr zone) |
static java.lang.String |
debugDescription_static() |
static java.lang.String |
description_static() |
MPSImageAllocator |
destinationImageAllocator()
Method to allocate the result image from -encodeToCommandBuffer...
|
MPSImage |
encodeToCommandBufferSourceImages(MTLCommandBuffer commandBuffer,
NSArray<? extends MPSImage> sourceImages)
Encode the graph to a MTLCommandBuffer
|
MPSImage |
encodeToCommandBufferSourceImagesSourceStatesIntermediateImagesDestinationStates(MTLCommandBuffer commandBuffer,
NSArray<? extends MPSImage> sourceImages,
NSArray<? extends MPSState> sourceStates,
NSMutableArray<MPSImage> intermediateImages,
NSMutableArray<MPSState> destinationStates)
Encode the graph to a MTLCommandBuffer
|
void |
encodeWithCoder(NSCoder coder) |
MPSImage |
executeAsyncWithSourceImagesCompletionHandler(NSArray<? extends MPSImage> sourceImages,
MPSNNGraph.Block_executeAsyncWithSourceImagesCompletionHandler handler)
Convenience method to execute a graph without having to manage many Metal details
|
long |
format()
The default storage format used for graph intermediate images
|
static MPSNNGraph |
graphWithDeviceResultImage(MTLDevice device,
MPSNNImageNode resultImage) |
static MPSNNGraph |
graphWithDeviceResultImageResultImageIsNeeded(MTLDevice device,
MPSNNImageNode resultImage,
boolean resultIsNeeded) |
static MPSNNGraph |
graphWithDeviceResultImagesResultsAreNeeded(MTLDevice device,
NSArray<? extends MPSNNImageNode> resultImages,
org.moe.natj.general.ptr.BoolPtr areResultsNeeded) |
static long |
hash_static() |
MPSNNGraph |
init() |
MPSNNGraph |
initWithCoder(NSCoder coder)
NS_DESIGNATED_INITIALIZER
|
MPSNNGraph |
initWithCoderDevice(NSCoder aDecoder,
java.lang.Object device)
NSSecureCoding compatability
|
MPSNNGraph |
initWithDevice(java.lang.Object device)
Standard init with default properties per filter type
|
MPSNNGraph |
initWithDeviceResultImage(MTLDevice device,
MPSNNImageNode resultImage) |
MPSNNGraph |
initWithDeviceResultImageResultImageIsNeeded(MTLDevice device,
MPSNNImageNode resultImage,
boolean resultIsNeeded)
Initialize a MPSNNGraph object on a device starting with resultImage working backward
|
MPSNNGraph |
initWithDeviceResultImagesResultsAreNeeded(MTLDevice device,
NSArray<? extends MPSNNImageNode> resultImages,
org.moe.natj.general.ptr.BoolPtr areResultsNeeded)
Initialize a MPSNNGraph object on a device starting with resultImage working backward
|
static NSObject.Function_instanceMethodForSelector_ret |
instanceMethodForSelector(org.moe.natj.objc.SEL aSelector) |
static NSMethodSignature |
instanceMethodSignatureForSelector(org.moe.natj.objc.SEL aSelector) |
static boolean |
instancesRespondToSelector(org.moe.natj.objc.SEL aSelector) |
NSArray<?> |
intermediateImageHandles()
Get a list of identifiers for intermediate images objects produced by the graph
|
static boolean |
isSubclassOfClass(org.moe.natj.objc.Class aClass) |
static NSSet<java.lang.String> |
keyPathsForValuesAffectingValueForKey(java.lang.String key) |
static MPSNNGraph |
new_objc() |
boolean |
outputStateIsTemporary()
Should MPSState objects produced by -encodeToCommandBuffer...
|
long |
readCountForSourceImageAtIndex(long index)
Find the number of times a image will be read by the graph *
|
long |
readCountForSourceStateAtIndex(long index)
Find the number of times a state will be read by the graph *
|
void |
reloadFromDataSources()
Reinitialize all graph nodes from data sources
|
static boolean |
resolveClassMethod(org.moe.natj.objc.SEL sel) |
static boolean |
resolveInstanceMethod(org.moe.natj.objc.SEL sel) |
MPSHandle |
resultHandle()
Get a handle for the graph result image
|
boolean |
resultImageIsNeeded()
Set at -init time.
|
NSArray<?> |
resultStateHandles()
Get a list of identifiers for result state objects produced by the graph
|
void |
setDestinationImageAllocator(MPSImageAllocator value)
Method to allocate the result image from -encodeToCommandBuffer...
|
void |
setFormat(long value)
The default storage format used for graph intermediate images
|
void |
setOutputStateIsTemporary(boolean value)
Should MPSState objects produced by -encodeToCommandBuffer...
|
static void |
setVersion_static(long aVersion) |
NSArray<?> |
sourceImageHandles()
Get a list of identifiers for source images needed to calculate the result image
|
NSArray<?> |
sourceStateHandles()
Get a list of identifiers for source state objects needed to calculate the result image
|
static org.moe.natj.objc.Class |
superclass_static() |
static boolean |
supportsSecureCoding() |
static long |
version_static() |
copyWithZoneDevice, device, label, options, setLabel, setOptionsaccessibilityActivate, accessibilityActivationPoint, accessibilityAssistiveTechnologyFocusedIdentifiers, accessibilityAttributedHint, accessibilityAttributedLabel, accessibilityAttributedUserInputLabels, accessibilityAttributedValue, accessibilityContainerType, accessibilityCustomActions, accessibilityCustomRotors, accessibilityDecrement, accessibilityDragSourceDescriptors, accessibilityDropPointDescriptors, accessibilityElementAtIndex, accessibilityElementCount, accessibilityElementDidBecomeFocused, accessibilityElementDidLoseFocus, accessibilityElementIsFocused, accessibilityElements, accessibilityElementsHidden, accessibilityFrame, accessibilityHint, accessibilityIncrement, accessibilityLabel, accessibilityLanguage, accessibilityNavigationStyle, accessibilityPath, accessibilityPerformEscape, accessibilityPerformMagicTap, accessibilityRespondsToUserInteraction, accessibilityScroll, accessibilityTextualContext, accessibilityTraits, accessibilityUserInputLabels, accessibilityValue, accessibilityViewIsModal, addObserverForKeyPathOptionsContext, attemptRecoveryFromErrorOptionIndex, attemptRecoveryFromErrorOptionIndexDelegateDidRecoverSelectorContextInfo, autoContentAccessingProxy, awakeAfterUsingCoder, awakeFromNib, class_objc, classForCoder, classForKeyedArchiver, copy, dealloc, debugDescription, description, dictionaryWithValuesForKeys, didChangeValueForKey, didChangeValueForKeyWithSetMutationUsingObjects, didChangeValuesAtIndexesForKey, doesNotRecognizeSelector, fileManagerShouldProceedAfterError, fileManagerWillProcessPath, finalize_objc, forwardingTargetForSelector, forwardInvocation, hash, indexOfAccessibilityElement, isAccessibilityElement, isEqual, isKindOfClass, isMemberOfClass, isProxy, methodForSelector, methodSignatureForSelector, mutableArrayValueForKey, mutableArrayValueForKeyPath, mutableCopy, mutableOrderedSetValueForKey, mutableOrderedSetValueForKeyPath, mutableSetValueForKey, mutableSetValueForKeyPath, observationInfo, observeValueForKeyPathOfObjectChangeContext, performSelector, performSelectorInBackgroundWithObject, performSelectorOnMainThreadWithObjectWaitUntilDone, performSelectorOnMainThreadWithObjectWaitUntilDoneModes, performSelectorOnThreadWithObjectWaitUntilDone, performSelectorOnThreadWithObjectWaitUntilDoneModes, performSelectorWithObject, performSelectorWithObjectAfterDelay, performSelectorWithObjectAfterDelayInModes, performSelectorWithObjectWithObject, prepareForInterfaceBuilder, provideImageDataBytesPerRowOrigin_Size_UserInfo, removeObserverForKeyPath, removeObserverForKeyPathContext, replacementObjectForCoder, replacementObjectForKeyedArchiver, respondsToSelector, self, setAccessibilityActivationPoint, setAccessibilityAttributedHint, setAccessibilityAttributedLabel, setAccessibilityAttributedUserInputLabels, setAccessibilityAttributedValue, setAccessibilityContainerType, setAccessibilityCustomActions, setAccessibilityCustomRotors, setAccessibilityDragSourceDescriptors, setAccessibilityDropPointDescriptors, setAccessibilityElements, setAccessibilityElementsHidden, setAccessibilityFrame, setAccessibilityHint, setAccessibilityLabel, setAccessibilityLanguage, setAccessibilityNavigationStyle, setAccessibilityPath, setAccessibilityRespondsToUserInteraction, setAccessibilityTextualContext, setAccessibilityTraits, setAccessibilityUserInputLabels, setAccessibilityValue, setAccessibilityViewIsModal, setIsAccessibilityElement, setNilValueForKey, setObservationInfo, setShouldGroupAccessibilityChildren, setValueForKey, setValueForKeyPath, setValueForUndefinedKey, setValuesForKeysWithDictionary, shouldGroupAccessibilityChildren, superclass, validateValueForKeyError, validateValueForKeyPathError, valueForKey, valueForKeyPath, valueForUndefinedKey, willChangeValueForKey, willChangeValueForKeyWithSetMutationUsingObjects, willChangeValuesAtIndexesForKeypublic static boolean accessInstanceVariablesDirectly()
public static MPSNNGraph alloc()
public static MPSNNGraph allocWithZone(org.moe.natj.general.ptr.VoidPtr zone)
public static boolean automaticallyNotifiesObserversForKey(java.lang.String key)
public static void cancelPreviousPerformRequestsWithTarget(java.lang.Object aTarget)
public static void cancelPreviousPerformRequestsWithTargetSelectorObject(java.lang.Object aTarget,
org.moe.natj.objc.SEL aSelector,
java.lang.Object anArgument)
public static NSArray<java.lang.String> classFallbacksForKeyedArchiver()
public static org.moe.natj.objc.Class classForKeyedUnarchiver()
public java.lang.Object copyWithZone(org.moe.natj.general.ptr.VoidPtr zone)
copyWithZone in interface NSCopyingcopyWithZone in class MPSKernelpublic static java.lang.String debugDescription_static()
public static java.lang.String description_static()
public MPSImageAllocator destinationImageAllocator()
This property overrides the allocator for the final result image in the graph. Default: MPSImage.defaultAllocator
public MPSImage encodeToCommandBufferSourceImages(MTLCommandBuffer commandBuffer, NSArray<? extends MPSImage> sourceImages)
IMPORTANT: Please use [MTLCommandBuffer addCompletedHandler:] to determine when this work is done. Use CPU time that would have been spent waiting for the GPU to encode the next command buffer and commit it too. That way, the work for the next command buffer is ready to go the moment the GPU is done. This will keep the GPU busy and running at top speed.
Those who ignore this advice and use [MTLCommandBuffer waitUntilCompleted] instead will likely cause their code to slow down by a factor of two or more. The CPU clock spins down while it waits for the GPU. When the GPU completes, the CPU runs slowly for a while until it spins up. The GPU has to wait for the CPU to encode more work (at low clock), giving it plenty of time to spin its own clock down. In typical CNN graph usage, neither may ever reach maximum clock frequency, causing slow down far beyond what otherwise would be expected from simple failure to schedule CPU and GPU work concurrently. Regrattably, it is probable that every performance benchmark you see on the net will be based on [MTLCommandBuffer waitUntilCompleted].
commandBuffer - The command buffer. If the command buffer is a MPSCommandBuffer,
the work will be committed to Metal in small pieces so that
the CPU-side latency is much reduced.sourceImages - A list of MPSImages to use as the source images for the graph.
These should be in the same order as the list returned from MPSNNGraph.sourceImageHandles.public MPSImage encodeToCommandBufferSourceImagesSourceStatesIntermediateImagesDestinationStates(MTLCommandBuffer commandBuffer, NSArray<? extends MPSImage> sourceImages, NSArray<? extends MPSState> sourceStates, NSMutableArray<MPSImage> intermediateImages, NSMutableArray<MPSState> destinationStates)
commandBuffer - The command buffer. If the command buffer is a MPSCommandBuffer,
the work will be committed to Metal in small pieces so that
the CPU-side latency is much reduced.sourceImages - A list of MPSImages to use as the source images for the graph.
These should be in the same order as the list returned from MPSNNGraph.sourceImageHandles.
The images may be image arrays. Typically, this is only one or two images
such as a .JPG decoded into a MPSImage*. If the sourceImages are MPSTemporaryImages,
the graph will decrement the readCount by 1, even if the graph actually
reads an image multiple times.sourceStates - A list of MPSState objects to use as state for a graph.
These should be in the same order as the list returned from MPSNNGraph.sourceStateHandles.
May be nil, if there is no source state. If the sourceStates are temporary,
the graph will decrement the readCount by 1, even if the graph actually
reads the state multiple times.intermediateImages - An optional NSMutableArray to receive any MPSImage objects exported as part of its operation.
These are only the images that were tagged with MPSNNImageNode.exportFromGraph = YES. The
identity of the states is given by -resultStateHandles. If temporary, each intermediateImage
will have a readCount of 1. If the result was tagged exportFromGraph = YES, it will be here
too, with a readCount of 2. To be able to access the images from outside the graph on the CPU,
your application must also set MPSNNImageNode.synchronizeResource = YES,
and MPSNNImageNode.imageAllocator = [MPSImage defaultAllocator]; The defaultAllocator creates
a permanent image that can be read with readBytes.destinationStates - An optional NSMutableArray to receive any MPSState objects created as part of its operation.
The identity of the states is given by -resultStateHandles.public void encodeWithCoder(NSCoder coder)
encodeWithCoder in interface NSCodingencodeWithCoder in class MPSKernelpublic MPSImage executeAsyncWithSourceImagesCompletionHandler(NSArray<? extends MPSImage> sourceImages, MPSNNGraph.Block_executeAsyncWithSourceImagesCompletionHandler handler)
This function will synchronously encode the graph on a private command buffer, commit it to a MPS internal command queue and return. The GPU will start working. When the GPU is done, the completion handler will be called. You should use the intervening time to encode other work for execution on the GPU, so that the GPU stays busy and doesn't clock down.
The work will be performed on the MTLDevice that hosts the source images.
This is a convenience API. There are a few situations it does not handle optimally. These may be better handled using [encodeToCommandBuffer:sourceImages:]. Specifically: [@code] o If the graph needs to be run multiple times for different images, it would be better to encode the graph multiple times on the same command buffer using [encodeToCommandBuffer:sourceImages:] This will allow the multiple graphs to share memory for intermediate storage, dramatically reducing memory usage.
o If preprocessing or post-processing of the MPSImage is required, such as resizing or normalization outside of a convolution, it would be better to encode those things on the same command buffer. Memory may be saved here too for intermediate storage. (MPSTemporaryImage lifetime does not span multiple command buffers.) [@endcode]
sourceImages - A list of MPSImages to use as the source images for the graph.
These should be in the same order as the list returned from
MPSNNGraph.sourceImageHandles. They should be allocated against
the same MTLDevice. There must be at least one source image.
Note: this array is intended to handle the case where multiple
input images are required to generate a single graph result.
That is, the graph itself has multiple inputs. If you need to
execute the graph multiple times, then call this API multiple
times, or (faster) make use of MPSImageBatches using
-executeBatchToCommandBuffer:sourceImages:sourceStates:...
(See discussion)handler - A block to receive any errors generated. This block may run
on any thread and may be called before this method returns.
The image, if any, passed to this callback is the same image
as that returned from the left hand side.public static long hash_static()
public MPSNNGraph init()
public MPSNNGraph initWithCoder(NSCoder coder)
NSCodinginitWithCoder in interface NSCodinginitWithCoder in class MPSKernelpublic MPSNNGraph initWithCoderDevice(NSCoder aDecoder, java.lang.Object device)
While the standard NSSecureCoding/NSCoding method -initWithCoder: should work, since the file can't know which device your data is allocated on, we have to guess and may guess incorrectly. To avoid that problem, use initWithCoder:device instead.
initWithCoderDevice in class MPSKernelaDecoder - The NSCoder subclass with your serialized MPSKerneldevice - The MTLDevice on which to make the MPSKernelpublic MPSNNGraph initWithDevice(java.lang.Object device)
MPSKernelinitWithDevice in class MPSKerneldevice - The device that the filter will be used on. May not be NULL.public MPSNNGraph initWithDeviceResultImage(MTLDevice device, MPSNNImageNode resultImage)
public static NSObject.Function_instanceMethodForSelector_ret instanceMethodForSelector(org.moe.natj.objc.SEL aSelector)
public static NSMethodSignature instanceMethodSignatureForSelector(org.moe.natj.objc.SEL aSelector)
public static boolean instancesRespondToSelector(org.moe.natj.objc.SEL aSelector)
public NSArray<?> intermediateImageHandles()
public static boolean isSubclassOfClass(org.moe.natj.objc.Class aClass)
public static NSSet<java.lang.String> keyPathsForValuesAffectingValueForKey(java.lang.String key)
public static MPSNNGraph new_objc()
public boolean outputStateIsTemporary()
See MPSState description. Default: NO
public static boolean resolveClassMethod(org.moe.natj.objc.SEL sel)
public static boolean resolveInstanceMethod(org.moe.natj.objc.SEL sel)
public MPSHandle resultHandle()
public NSArray<?> resultStateHandles()
Not guaranteed to be in the same order as sourceStateHandles
public void setDestinationImageAllocator(MPSImageAllocator value)
This property overrides the allocator for the final result image in the graph. Default: MPSImage.defaultAllocator
public void setOutputStateIsTemporary(boolean value)
See MPSState description. Default: NO
public static void setVersion_static(long aVersion)
public NSArray<?> sourceImageHandles()
public NSArray<?> sourceStateHandles()
Not guaranteed to be in the same order as resultStateHandles
public static org.moe.natj.objc.Class superclass_static()
public static boolean supportsSecureCoding()
public boolean _supportsSecureCoding()
NSSecureCoding_supportsSecureCoding in interface NSSecureCoding_supportsSecureCoding in class MPSKernelpublic static long version_static()
public long format()
This doesn't affect how data is stored in buffers in states. Nor does it affect the storage format for weights such as convolution weights stored by individual filters. Default: MPSImageFeatureChannelFormatFloat16
public static MPSNNGraph graphWithDeviceResultImage(MTLDevice device, MPSNNImageNode resultImage)
public static MPSNNGraph graphWithDeviceResultImageResultImageIsNeeded(MTLDevice device, MPSNNImageNode resultImage, boolean resultIsNeeded)
public static MPSNNGraph graphWithDeviceResultImagesResultsAreNeeded(MTLDevice device, NSArray<? extends MPSNNImageNode> resultImages, org.moe.natj.general.ptr.BoolPtr areResultsNeeded)
public MPSNNGraph initWithDeviceResultImageResultImageIsNeeded(MTLDevice device, MPSNNImageNode resultImage, boolean resultIsNeeded)
The MPSNNGraph constructor will start with the indicated result image, and look to see what MPSNNFilterNode produced it, then look to its dependencies and so forth to reveal the subsection of the graph necessary to compute the image.
device - The MTLDevice on which to run the graphresultImage - The MPSNNImageNode corresponding to the last image in the graph.
This is the image that will be returned. Note: the imageAllocator
for this node is ignored and the MPSNNGraph.destinationImageAllocator
is used for this node instead.resultIsNeeded - Commonly, when training a graph, the last MPSImage out of the
graph is not used. The final gradient filter is run solely to update
some weights. If resultIsNeeded is set to NO, nil will
be returned from the left hand side of the -encode call instead,
and computation to produce the last image may be pruned away.public MPSNNGraph initWithDeviceResultImagesResultsAreNeeded(MTLDevice device, NSArray<? extends MPSNNImageNode> resultImages, org.moe.natj.general.ptr.BoolPtr areResultsNeeded)
The MPSNNGraph constructor will start with the indicated result images, and look to see what MPSNNFilterNode produced them, then look to its dependencies and so forth to reveal the subsection of the graph necessary to compute the image. This variant is provided to support graphs and subgraphs with multiple image outputs.
device - The MTLDevice on which to run the graphresultImages - The MPSNNImageNodes corresponding to the last images in the graph.
The first image in the array will be returned from the -encode method
LHS. The rest will be included in the list of intermediate images.areResultsNeeded - An array of BOOL values with count equal to resultImages.count.
If NO is passed for a given image, the image itself is marked unneeded
and might be skipped. The graph will prune this branch back to the
first requred filter. A filter is required if it generates a needed
result image, or is needed to update training parameters.public long readCountForSourceImageAtIndex(long index)
From the set of images (or image batches) passed in to the graph, find the number of times the graph will read an image. This may be needed by your application to correctly set the MPSImage.readCount property.
index - The index of the image. The index of the image matches the index of the image in the array returned
by the sourceImageHandles property.public long readCountForSourceStateAtIndex(long index)
From the set of state (or state batches) passed in to the graph, find the number of times the graph will read a state. This may be needed by your application to correctly set the MPSState.readCount property.
index - The index of the state. The index of the state matches the index of the state in the array returned
by the sourceStateHandles property.public void reloadFromDataSources()
A number of the nodes that make up a graph have a data source associated with them, for example a MPSCNNConvolutionDataSource or a MPSCNNBatchNormalizationDataSource. Generally, the data is read from these once at graph initialization time and then not looked at again, except during the weight / parameter update phase of the corresponding gradient nodes and then only if CPU updates are requested. Otherwise, update occurs on the GPU, and the data in the data source is thereafter ignored.
It can happen, though, that your application has determined the graph should load a new set of weights from the data source. When this method is called, the graph will find all nodes that support reloading and direct them to reinitialize themselves based on their data source.
This process occurs immediately. Your application will need to make sure any GPU work being done by the graph is complete to ensure data coherency. Most nodes do not have a data source and will not be modified. Nodes that are not used by the graph will not be updated.
public boolean resultImageIsNeeded()
If NO, nil will be returned from -encode calls and some computation may be omitted.
public void setFormat(long value)
This doesn't affect how data is stored in buffers in states. Nor does it affect the storage format for weights such as convolution weights stored by individual filters. Default: MPSImageFeatureChannelFormatFloat16