Skip to content

Commit

Permalink
benchmarking for article
Browse files Browse the repository at this point in the history
  • Loading branch information
jph00 committed Jan 10, 2019
1 parent b7ab38c commit acea01c
Show file tree
Hide file tree
Showing 15 changed files with 1,419 additions and 556 deletions.
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ ifeq ($(mode),)
mode := debug
endif

link_args := -Xswiftc -Ounchecked $(addprefix -Xcc ,-ffast-math -O3 -march=native)
link_args := -Xswiftc -Ounchecked $(addprefix -Xcc ,-O2 -ffast-math -ffp-contract=fast -march=native)

gybs := $(shell find Sources Tests -type f -name '*.gyb')
conv_gybs := $(patsubst %.gyb,%,$(gybs))
Expand Down Expand Up @@ -38,7 +38,8 @@ gyb: $(sources)
%.h: %.h.gyb
gyb --line-directive '' -o $@ $<

Sources/BaseMath/BaseMath.swift Sources/BaseMath/BaseVector.swift: Sources/BaseMath/mathfuncs.py
Sources/BaseMath/BaseMath.swift Sources/BaseMath/BaseVector.swift Sources/CBaseMath/CBaseMath.c: mathfuncs.py
Sources/CBaseMath/include/CBaseMath.h: Sources/CBaseMath/CBaseMath.c

.PHONY: clean
clean:
Expand Down
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ let package = Package(
.target( name: "CBaseMath"),
.target( name: "BaseMath", dependencies: ["CBaseMath"]),
.testTarget( name: "BaseMathTests", dependencies: ["BaseMath"]),
.target( name: "bench", dependencies: ["BaseMath"]),
]
)

12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
# BaseMath

Basic math functions for float and double arrays in Swift, with no dependencies. Generally around 3-5x faster than standard swift loops or maps. These functions are provided (all also have a version suffixed with `_` and a version prefixed with `sum` - see below of details):
Basic math functions for float and double arrays in Swift, with no dependencies, via the `BaseVector` protocol. They are generally around 3-5x faster than standard swift loops or maps, since they use pointers, which avoids the overhead of Swift's copy-on-write checking. The following functions are provided (all also have a version suffixed with `_` and a version prefixed with `sum` - see below of details):

- Binary functions,: `min`, `max`, `pow`, `atan2`, `copysign`, `fdim`, `fmax`, `fmin`, `hypot`, `nextafter`, `add`, `sub`, `mul`, `div`, `subRev`, `divRev`
- Binary functions,: `sqr`, `abs`, `min`, `max`, `pow`, `atan2`, `copysign`, `fdim`, `fmax`, `fmin`, `hypot`, `nextafter`, `add`, `sub`, `mul`, `div`, `subRev`, `divRev`
- Unary functions,: `acos`, `acosh`, `asin`, `asinh`, `atan`, `atanh`, `cbrt`, `cos`, `cosh`, `erf`, `erfc`, `exp`, `exp2`, `expm1`, `log`, `log10`, `log1p`, `log2`, `logb`, `nearbyint`, `rint`, `sin`, `sinh`, `tan`, `tanh`, `tgamma`

This library is used by [SwiftyMKL](https://github.com/jph00/SwiftyMKL), which adds more optimized versions of the functions from Intel's Performance Libraries, along with various linear algebra and statistical functions.

Math functions from `Foundation` (which in turn uses functions in `math.h`) are used, except for `sum()`, which is written in C, since reductions in Swift are currently not vectorized. The standard math operators are also provided, including optimzed assignment versions. Functions with `_` suffix are in-place.
Math functions from `Foundation` (which in turn provides the functions available in `math.h`) are used, except for `sum()` (and `sum${f}()`, where `${f}` is a function name from `math.h`), which are written in C, since reductions in Swift are currently not vectorized. The standard math operators are also provided, including optimized assignment versions. Functions with `_` suffix are in-place. In addition the `sqr` function is provided. Note that `abs` is called `fabs` in C versions since that's what it's called in `math.h`.

Note that because the library uses pointers, Swift's copy-on-write and `let` immutability are bypassed. Use the provided `copy()` method to get a real copy of an `Array`.
Because the library uses pointers, Swift's copy-on-write and `let` immutability are bypassed. Use the provided `copy()` method to get a real copy of an `Array`.

To avoid surprises, you might prefer to use the provided `AlignedStorage` struct, which supports much of the same functionality as `Array`, but doesn't use copy-on-write, and aligns memory for (sometimes) better performance.
To avoid surprises, you might prefer to use the provided `AlignedStorage` struct, which supports much of the same functionality as `Array`, but doesn't use copy-on-write, and aligns memory for (sometimes) better performance. In addition, `UnsafeMutableBufferPointer` is extended to conform to `BaseVector` so it also gets the same functionality.

After `import BaseVector` you'll find that all the standard unary and binary math functions have been added to `Array` for floats and doubles, along with reduction versions of each which start with `sum` (e.g `sumabs`, `sumcos`, etc).

See the test suite for examples of use.
See the test suite for examples of use. For more information on background and implementation details, see [this article] \(TBA).

113 changes: 113 additions & 0 deletions Sources/BaseMath/BaseMath.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ import Foundation
import CBaseMath


extension SupportsBasicMath {
public func string(_ digits:Int) -> String {
let fmt = NumberFormatter()
fmt.minimumFractionDigits = digits
fmt.maximumFractionDigits = digits
return fmt.string(from: self.nsNumber) ?? "\(self)"
}
}

precedencegroup ExponentiationPrecedence { associativity: right higherThan: MultiplicationPrecedence }
infix operator ^^: ExponentiationPrecedence

Expand All @@ -12,6 +21,8 @@ public protocol SupportsBasicMath:BinaryFloatingPoint {
init(_ value: Self)
init()

var nsNumber:NSNumber {get}

func add(_ b: Self) -> Self
func sub(_ b: Self) -> Self
func mul(_ b: Self) -> Self
Expand All @@ -28,6 +39,7 @@ public protocol SupportsBasicMath:BinaryFloatingPoint {
func fmin(_ b: Self) -> Self
func hypot(_ b: Self) -> Self
func nextafter(_ b: Self) -> Self
func sqrt() -> Self
func acos() -> Self
func acosh() -> Self
func asin() -> Self
Expand Down Expand Up @@ -55,21 +67,58 @@ public protocol SupportsBasicMath:BinaryFloatingPoint {
func tanh() -> Self
func tgamma() -> Self

static func sqr(_ a:Self) -> Self
func sqr() -> Self
func abs() -> Self
static func sum(_ a:PtrT, _ n:Int32) -> Element
static func sumsqr(_ a:PtrT, _ n:Int32)->Element
static func sumfabs(_ a:PtrT, _ n:Int32)->Element
static func sumsqrt(_ a:PtrT, _ n:Int32)->Element
static func sumacos(_ a:PtrT, _ n:Int32)->Element
static func sumacosh(_ a:PtrT, _ n:Int32)->Element
static func sumasin(_ a:PtrT, _ n:Int32)->Element
static func sumasinh(_ a:PtrT, _ n:Int32)->Element
static func sumatan(_ a:PtrT, _ n:Int32)->Element
static func sumatanh(_ a:PtrT, _ n:Int32)->Element
static func sumcbrt(_ a:PtrT, _ n:Int32)->Element
static func sumcos(_ a:PtrT, _ n:Int32)->Element
static func sumcosh(_ a:PtrT, _ n:Int32)->Element
static func sumerf(_ a:PtrT, _ n:Int32)->Element
static func sumerfc(_ a:PtrT, _ n:Int32)->Element
static func sumexp(_ a:PtrT, _ n:Int32)->Element
static func sumexp2(_ a:PtrT, _ n:Int32)->Element
static func sumexpm1(_ a:PtrT, _ n:Int32)->Element
static func sumlog(_ a:PtrT, _ n:Int32)->Element
static func sumlog10(_ a:PtrT, _ n:Int32)->Element
static func sumlog1p(_ a:PtrT, _ n:Int32)->Element
static func sumlog2(_ a:PtrT, _ n:Int32)->Element
static func sumlogb(_ a:PtrT, _ n:Int32)->Element
static func sumnearbyint(_ a:PtrT, _ n:Int32)->Element
static func sumrint(_ a:PtrT, _ n:Int32)->Element
static func sumsin(_ a:PtrT, _ n:Int32)->Element
static func sumsinh(_ a:PtrT, _ n:Int32)->Element
static func sumtan(_ a:PtrT, _ n:Int32)->Element
static func sumtanh(_ a:PtrT, _ n:Int32)->Element
static func sumtgamma(_ a:PtrT, _ n:Int32)->Element

static func ^^(x:Self, a:Self) -> Self
}

extension Float : SupportsBasicMath {
public var nsNumber:NSNumber { return NSNumber(value: self) }

@inlinable public func add(_ b: Float) -> Float {return self + b}
@inlinable public func sub(_ b: Float) -> Float {return self - b}
@inlinable public func mul(_ b: Float) -> Float {return self * b}
@inlinable public func div(_ b: Float) -> Float {return self / b}

@inlinable public static func sqr(_ a:Float) -> Float {return a * a }
@inlinable public func sqr( ) -> Float {return self*self}

@inlinable public func subRev(_ b: Float) -> Float {return b - self}
@inlinable public func divRev(_ b: Float) -> Float {return b / self}

@inlinable public func sqrt() -> Float {return Foundation.sqrt(self)}
@inlinable public func acos() -> Float {return Foundation.acos(self)}
@inlinable public func acosh() -> Float {return Foundation.acosh(self)}
@inlinable public func asin() -> Float {return Foundation.asin(self)}
Expand Down Expand Up @@ -112,18 +161,53 @@ extension Float : SupportsBasicMath {
@inlinable public func nextafter(_ b: Float) -> Float {return Foundation.nextafter(self, b)}

@inlinable public static func sum(_ a:PtrT, _ n:Int32) -> Element { return smSum_float(a, n) }
@inlinable public static func sumsqr(_ a:PtrT, _ n:Int32)->Element { return smSum_sqr_float(a, n) }
@inlinable public static func sumfabs(_ a:PtrT, _ n:Int32)->Element { return smSum_fabs_float(a, n) }
@inlinable public static func sumsqrt(_ a:PtrT, _ n:Int32)->Element { return smSum_sqrt_float(a, n) }
@inlinable public static func sumacos(_ a:PtrT, _ n:Int32)->Element { return smSum_acos_float(a, n) }
@inlinable public static func sumacosh(_ a:PtrT, _ n:Int32)->Element { return smSum_acosh_float(a, n) }
@inlinable public static func sumasin(_ a:PtrT, _ n:Int32)->Element { return smSum_asin_float(a, n) }
@inlinable public static func sumasinh(_ a:PtrT, _ n:Int32)->Element { return smSum_asinh_float(a, n) }
@inlinable public static func sumatan(_ a:PtrT, _ n:Int32)->Element { return smSum_atan_float(a, n) }
@inlinable public static func sumatanh(_ a:PtrT, _ n:Int32)->Element { return smSum_atanh_float(a, n) }
@inlinable public static func sumcbrt(_ a:PtrT, _ n:Int32)->Element { return smSum_cbrt_float(a, n) }
@inlinable public static func sumcos(_ a:PtrT, _ n:Int32)->Element { return smSum_cos_float(a, n) }
@inlinable public static func sumcosh(_ a:PtrT, _ n:Int32)->Element { return smSum_cosh_float(a, n) }
@inlinable public static func sumerf(_ a:PtrT, _ n:Int32)->Element { return smSum_erf_float(a, n) }
@inlinable public static func sumerfc(_ a:PtrT, _ n:Int32)->Element { return smSum_erfc_float(a, n) }
@inlinable public static func sumexp(_ a:PtrT, _ n:Int32)->Element { return smSum_exp_float(a, n) }
@inlinable public static func sumexp2(_ a:PtrT, _ n:Int32)->Element { return smSum_exp2_float(a, n) }
@inlinable public static func sumexpm1(_ a:PtrT, _ n:Int32)->Element { return smSum_expm1_float(a, n) }
@inlinable public static func sumlog(_ a:PtrT, _ n:Int32)->Element { return smSum_log_float(a, n) }
@inlinable public static func sumlog10(_ a:PtrT, _ n:Int32)->Element { return smSum_log10_float(a, n) }
@inlinable public static func sumlog1p(_ a:PtrT, _ n:Int32)->Element { return smSum_log1p_float(a, n) }
@inlinable public static func sumlog2(_ a:PtrT, _ n:Int32)->Element { return smSum_log2_float(a, n) }
@inlinable public static func sumlogb(_ a:PtrT, _ n:Int32)->Element { return smSum_logb_float(a, n) }
@inlinable public static func sumnearbyint(_ a:PtrT, _ n:Int32)->Element { return smSum_nearbyint_float(a, n) }
@inlinable public static func sumrint(_ a:PtrT, _ n:Int32)->Element { return smSum_rint_float(a, n) }
@inlinable public static func sumsin(_ a:PtrT, _ n:Int32)->Element { return smSum_sin_float(a, n) }
@inlinable public static func sumsinh(_ a:PtrT, _ n:Int32)->Element { return smSum_sinh_float(a, n) }
@inlinable public static func sumtan(_ a:PtrT, _ n:Int32)->Element { return smSum_tan_float(a, n) }
@inlinable public static func sumtanh(_ a:PtrT, _ n:Int32)->Element { return smSum_tanh_float(a, n) }
@inlinable public static func sumtgamma(_ a:PtrT, _ n:Int32)->Element { return smSum_tgamma_float(a, n) }

public static func ^^(x:Float, a:Float) -> Float { return x.pow(a) }
}
extension Double : SupportsBasicMath {
public var nsNumber:NSNumber { return NSNumber(value: self) }

@inlinable public func add(_ b: Double) -> Double {return self + b}
@inlinable public func sub(_ b: Double) -> Double {return self - b}
@inlinable public func mul(_ b: Double) -> Double {return self * b}
@inlinable public func div(_ b: Double) -> Double {return self / b}

@inlinable public static func sqr(_ a:Double) -> Double {return a * a }
@inlinable public func sqr( ) -> Double {return self*self}

@inlinable public func subRev(_ b: Double) -> Double {return b - self}
@inlinable public func divRev(_ b: Double) -> Double {return b / self}

@inlinable public func sqrt() -> Double {return Foundation.sqrt(self)}
@inlinable public func acos() -> Double {return Foundation.acos(self)}
@inlinable public func acosh() -> Double {return Foundation.acosh(self)}
@inlinable public func asin() -> Double {return Foundation.asin(self)}
Expand Down Expand Up @@ -166,6 +250,35 @@ extension Double : SupportsBasicMath {
@inlinable public func nextafter(_ b: Double) -> Double {return Foundation.nextafter(self, b)}

@inlinable public static func sum(_ a:PtrT, _ n:Int32) -> Element { return smSum_double(a, n) }
@inlinable public static func sumsqr(_ a:PtrT, _ n:Int32)->Element { return smSum_sqr_double(a, n) }
@inlinable public static func sumfabs(_ a:PtrT, _ n:Int32)->Element { return smSum_fabs_double(a, n) }
@inlinable public static func sumsqrt(_ a:PtrT, _ n:Int32)->Element { return smSum_sqrt_double(a, n) }
@inlinable public static func sumacos(_ a:PtrT, _ n:Int32)->Element { return smSum_acos_double(a, n) }
@inlinable public static func sumacosh(_ a:PtrT, _ n:Int32)->Element { return smSum_acosh_double(a, n) }
@inlinable public static func sumasin(_ a:PtrT, _ n:Int32)->Element { return smSum_asin_double(a, n) }
@inlinable public static func sumasinh(_ a:PtrT, _ n:Int32)->Element { return smSum_asinh_double(a, n) }
@inlinable public static func sumatan(_ a:PtrT, _ n:Int32)->Element { return smSum_atan_double(a, n) }
@inlinable public static func sumatanh(_ a:PtrT, _ n:Int32)->Element { return smSum_atanh_double(a, n) }
@inlinable public static func sumcbrt(_ a:PtrT, _ n:Int32)->Element { return smSum_cbrt_double(a, n) }
@inlinable public static func sumcos(_ a:PtrT, _ n:Int32)->Element { return smSum_cos_double(a, n) }
@inlinable public static func sumcosh(_ a:PtrT, _ n:Int32)->Element { return smSum_cosh_double(a, n) }
@inlinable public static func sumerf(_ a:PtrT, _ n:Int32)->Element { return smSum_erf_double(a, n) }
@inlinable public static func sumerfc(_ a:PtrT, _ n:Int32)->Element { return smSum_erfc_double(a, n) }
@inlinable public static func sumexp(_ a:PtrT, _ n:Int32)->Element { return smSum_exp_double(a, n) }
@inlinable public static func sumexp2(_ a:PtrT, _ n:Int32)->Element { return smSum_exp2_double(a, n) }
@inlinable public static func sumexpm1(_ a:PtrT, _ n:Int32)->Element { return smSum_expm1_double(a, n) }
@inlinable public static func sumlog(_ a:PtrT, _ n:Int32)->Element { return smSum_log_double(a, n) }
@inlinable public static func sumlog10(_ a:PtrT, _ n:Int32)->Element { return smSum_log10_double(a, n) }
@inlinable public static func sumlog1p(_ a:PtrT, _ n:Int32)->Element { return smSum_log1p_double(a, n) }
@inlinable public static func sumlog2(_ a:PtrT, _ n:Int32)->Element { return smSum_log2_double(a, n) }
@inlinable public static func sumlogb(_ a:PtrT, _ n:Int32)->Element { return smSum_logb_double(a, n) }
@inlinable public static func sumnearbyint(_ a:PtrT, _ n:Int32)->Element { return smSum_nearbyint_double(a, n) }
@inlinable public static func sumrint(_ a:PtrT, _ n:Int32)->Element { return smSum_rint_double(a, n) }
@inlinable public static func sumsin(_ a:PtrT, _ n:Int32)->Element { return smSum_sin_double(a, n) }
@inlinable public static func sumsinh(_ a:PtrT, _ n:Int32)->Element { return smSum_sinh_double(a, n) }
@inlinable public static func sumtan(_ a:PtrT, _ n:Int32)->Element { return smSum_tan_double(a, n) }
@inlinable public static func sumtanh(_ a:PtrT, _ n:Int32)->Element { return smSum_tanh_double(a, n) }
@inlinable public static func sumtgamma(_ a:PtrT, _ n:Int32)->Element { return smSum_tgamma_double(a, n) }

public static func ^^(x:Double, a:Double) -> Double { return x.pow(a) }
}
Expand Down
26 changes: 25 additions & 1 deletion Sources/BaseMath/BaseMath.swift.gyb
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import Foundation
import CBaseMath

%{ from mathfuncs import * }%
%{ import sys; sys.path.append('../..'); from mathfuncs import * }%

extension SupportsBasicMath {
public func string(_ digits:Int) -> String {
let fmt = NumberFormatter()
fmt.minimumFractionDigits = digits
fmt.maximumFractionDigits = digits
return fmt.string(from: self.nsNumber) ?? "\(self)"
}
}

precedencegroup ExponentiationPrecedence { associativity: right higherThan: MultiplicationPrecedence }
infix operator ^^: ExponentiationPrecedence
Expand All @@ -13,25 +22,37 @@ public protocol SupportsBasicMath:BinaryFloatingPoint {
init(_ value: Self)
init()

var nsNumber:NSNumber {get}

% for f in binfs:
func ${f}(_ b: Self) -> Self
% end # f
% for f in funcs2:
func ${f}() -> Self
% end # f

static func sqr(_ a:Self) -> Self
func sqr() -> Self
func abs() -> Self
static func sum(_ a:PtrT, _ n:Int32) -> Element
% for f in unarycfs:
static func sum${f}(_ a:PtrT, _ n:Int32)->Element
% end

static func ^^(x:Self, a:Self) -> Self
}

% for t in types:
extension ${t} : SupportsBasicMath {
public var nsNumber:NSNumber { return NSNumber(value: self) }

% for f,op in zip(op_fs,ops):
@inlinable public func ${f}(_ b: ${t}) -> ${t} {return self ${op} b}
% end # f,op

@inlinable public static func sqr(_ a:${t}) -> ${t} {return a * a }
@inlinable public func sqr( ) -> ${t} {return self*self}

@inlinable public func subRev(_ b: ${t}) -> ${t} {return b - self}
@inlinable public func divRev(_ b: ${t}) -> ${t} {return b / self}

Expand All @@ -49,6 +70,9 @@ extension ${t} : SupportsBasicMath {
% end # f

@inlinable public static func sum(_ a:PtrT, _ n:Int32) -> Element { return smSum_${t.lower()}(a, n) }
% for f in unarycfs:
@inlinable public static func sum${f}(_ a:PtrT, _ n:Int32)->Element { return smSum_${f}_${t.lower()}(a, n) }
% end

public static func ^^(x:${t}, a:${t}) -> ${t} { return x.pow(a) }
}
Expand Down
Loading

0 comments on commit acea01c

Please sign in to comment.