Skip to content

[benchmark] Add a CharacterProperties benchmark #13739

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 5, 2018
Merged
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
1 change: 1 addition & 0 deletions benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ set(SWIFT_BENCH_MODULES
single-source/CaptureProp
single-source/CharacterLiteralsLarge
single-source/CharacterLiteralsSmall
single-source/CharacterProperties
single-source/Chars
single-source/ClassArrayGetter
single-source/DeadArray
Expand Down
170 changes: 170 additions & 0 deletions benchmark/single-source/CharacterProperties.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
//===--- CharacterProperties.swift ----------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

////////////////////////////////////////////////////////////////////////////////
// WARNING: This file is manually generated from .gyb template and should not
// be directly modified. Instead, make changes to CharacterProperties.swift.gyb
// and run scripts/generate_harness/generate_harness.py to regenerate this file.
////////////////////////////////////////////////////////////////////////////////

import TestsUtils
import Foundation

public let CharacterPropertiesFetch = BenchmarkInfo(
name: "CharacterPropertiesFetch",
runFunction: run_CharacterPropertiesFetch,
tags: [.validation, .api, .String])

public let CharacterPropertiesStashed = BenchmarkInfo(
name: "CharacterPropertiesStashed",
runFunction: run_CharacterPropertiesStashed,
tags: [.validation, .api, .String])


extension Character {
var firstScalar: UnicodeScalar { return unicodeScalars.first! }
}


// Fetch the CharacterSet for every call
func isControl(_ c: Character) -> Bool {
return CharacterSet.controlCharacters.contains(c.firstScalar)
}
func isAlphanumeric(_ c: Character) -> Bool {
return CharacterSet.alphanumerics.contains(c.firstScalar)
}
func isLowercase(_ c: Character) -> Bool {
return CharacterSet.lowercaseLetters.contains(c.firstScalar)
}
func isPunctuation(_ c: Character) -> Bool {
return CharacterSet.punctuationCharacters.contains(c.firstScalar)
}
func isWhitespace(_ c: Character) -> Bool {
return CharacterSet.whitespaces.contains(c.firstScalar)
}
func isLetter(_ c: Character) -> Bool {
return CharacterSet.letters.contains(c.firstScalar)
}
func isUppercase(_ c: Character) -> Bool {
return CharacterSet.uppercaseLetters.contains(c.firstScalar)
}
func isDecimal(_ c: Character) -> Bool {
return CharacterSet.decimalDigits.contains(c.firstScalar)
}
func isNewline(_ c: Character) -> Bool {
return CharacterSet.newlines.contains(c.firstScalar)
}
func isCapitalized(_ c: Character) -> Bool {
return CharacterSet.capitalizedLetters.contains(c.firstScalar)
}

// Stash the set
let controlCharacters = CharacterSet.controlCharacters
func isControlStashed(_ c: Character) -> Bool {
return controlCharacters.contains(c.firstScalar)
}
let alphanumerics = CharacterSet.alphanumerics
func isAlphanumericStashed(_ c: Character) -> Bool {
return alphanumerics.contains(c.firstScalar)
}
let lowercaseLetters = CharacterSet.lowercaseLetters
func isLowercaseStashed(_ c: Character) -> Bool {
return lowercaseLetters.contains(c.firstScalar)
}
let punctuationCharacters = CharacterSet.punctuationCharacters
func isPunctuationStashed(_ c: Character) -> Bool {
return punctuationCharacters.contains(c.firstScalar)
}
let whitespaces = CharacterSet.whitespaces
func isWhitespaceStashed(_ c: Character) -> Bool {
return whitespaces.contains(c.firstScalar)
}
let letters = CharacterSet.letters
func isLetterStashed(_ c: Character) -> Bool {
return letters.contains(c.firstScalar)
}
let uppercaseLetters = CharacterSet.uppercaseLetters
func isUppercaseStashed(_ c: Character) -> Bool {
return uppercaseLetters.contains(c.firstScalar)
}
let decimalDigits = CharacterSet.decimalDigits
func isDecimalStashed(_ c: Character) -> Bool {
return decimalDigits.contains(c.firstScalar)
}
let newlines = CharacterSet.newlines
func isNewlineStashed(_ c: Character) -> Bool {
return newlines.contains(c.firstScalar)
}
let capitalizedLetters = CharacterSet.capitalizedLetters
func isCapitalizedStashed(_ c: Character) -> Bool {
return capitalizedLetters.contains(c.firstScalar)
}

// Compute on the fly
//
// TODO: If UnicodeScalars ever exposes category, etc., implement the others!
func isNewlineComputed(_ c: Character) -> Bool {
switch c.firstScalar.value {
case 0x000A...0x000D: return true
case 0x0085: return true
case 0x2028...0x2029: return true
default: return false
}
}

let workload = """
the quick brown 🦊 jumped over the lazy 🐶.
в чащах юга жил-был цитрус? да, но фальшивый экземпляр
𓀀𓀤𓁓𓁲𓃔𓃗𓃀𓃁𓃂𓃃𓆌𓆍𓆎𓆏𓆐𓆑𓆒𓆲𓁿
🝁ꃕ躍‾∾📦⺨
👍👩‍👩‍👧‍👧👨‍👨‍👦‍👦🇺🇸🇨🇦🇲🇽👍🏻👍🏼👍🏽👍🏾👍🏿
Lorem ipsum something something something...
"""

@inline(never)
public func run_CharacterPropertiesFetch(_ N: Int) {
for _ in 1...N {
for c in workload {
blackHole(isControl(c))
blackHole(isAlphanumeric(c))
blackHole(isLowercase(c))
blackHole(isPunctuation(c))
blackHole(isWhitespace(c))
blackHole(isLetter(c))
blackHole(isUppercase(c))
blackHole(isDecimal(c))
blackHole(isNewline(c))
blackHole(isCapitalized(c))
}
}
}

@inline(never)
public func run_CharacterPropertiesStashed(_ N: Int) {
for _ in 1...N {
for c in workload {
blackHole(isControlStashed(c))
blackHole(isAlphanumericStashed(c))
blackHole(isLowercaseStashed(c))
blackHole(isPunctuationStashed(c))
blackHole(isWhitespaceStashed(c))
blackHole(isLetterStashed(c))
blackHole(isUppercaseStashed(c))
blackHole(isDecimalStashed(c))
blackHole(isNewlineStashed(c))
blackHole(isCapitalizedStashed(c))
}
}
}

// TODO: run_CharacterPropertiesComputed

108 changes: 108 additions & 0 deletions benchmark/single-source/CharacterProperties.swift.gyb
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//===--- CharacterProperties.swift ----------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

% # Ignore the following warning. This _is_ the correct file to edit.
////////////////////////////////////////////////////////////////////////////////
// WARNING: This file is manually generated from .gyb template and should not
// be directly modified. Instead, make changes to CharacterProperties.swift.gyb
// and run scripts/generate_harness/generate_harness.py to regenerate this file.
////////////////////////////////////////////////////////////////////////////////

import TestsUtils
import Foundation

public let CharacterPropertiesFetch = BenchmarkInfo(
name: "CharacterPropertiesFetch",
runFunction: run_CharacterPropertiesFetch,
tags: [.validation, .api, .String])

public let CharacterPropertiesStashed = BenchmarkInfo(
name: "CharacterPropertiesStashed",
runFunction: run_CharacterPropertiesStashed,
tags: [.validation, .api, .String])


extension Character {
var firstScalar: UnicodeScalar { return unicodeScalars.first! }
}

% Properties = { "Alphanumeric": "alphanumerics", "Capitalized": "capitalizedLetters", \
% "Control": "controlCharacters", \
% "Decimal": "decimalDigits", \
% "Letter": "letters", \
% "Lowercase": "lowercaseLetters", \
% "Uppercase": "uppercaseLetters", \
% "Newline": "newlines", \
% "Whitespace": "whitespaces", \
% "Punctuation": "punctuationCharacters" \
% }

// Fetch the CharacterSet for every call
% for Property, Set in Properties.items():
func is${Property}(_ c: Character) -> Bool {
return CharacterSet.${Set}.contains(c.firstScalar)
}
% end

// Stash the set
% for Property, Set in Properties.items():
let ${Set} = CharacterSet.${Set}
func is${Property}Stashed(_ c: Character) -> Bool {
return ${Set}.contains(c.firstScalar)
}
% end

// Compute on the fly
//
// TODO: If UnicodeScalars ever exposes category, etc., implement the others!
func isNewlineComputed(_ c: Character) -> Bool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it a good idea to leave this in without a run_CharacterPropertiesComputed?

Copy link
Member Author

@milseman milseman Jan 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We only have an implementation of isNewline because CharacterSet's newlines are fixed, while the others are defined in terms of Unicode categories or properties that are version-specific (and thus run-time-dependent).

A run_CharacterPropertiesComputed that only executes on newlines wouldn't be comparable to others, so I'm not sure it gives a useful signal.

edit: clearer wording

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm open to suggestions :-)

switch c.firstScalar.value {
case 0x000A...0x000D: return true
case 0x0085: return true
case 0x2028...0x2029: return true
default: return false
}
}

let workload = """
the quick brown 🦊 jumped over the lazy 🐶.
в чащах юга жил-был цитрус? да, но фальшивый экземпляр
𓀀𓀤𓁓𓁲𓃔𓃗𓃀𓃁𓃂𓃃𓆌𓆍𓆎𓆏𓆐𓆑𓆒𓆲𓁿
🝁ꃕ躍‾∾📦⺨
👍👩‍👩‍👧‍👧👨‍👨‍👦‍👦🇺🇸🇨🇦🇲🇽👍🏻👍🏼👍🏽👍🏾👍🏿
Lorem ipsum something something something...
"""

@inline(never)
public func run_CharacterPropertiesFetch(_ N: Int) {
for _ in 1...N {
for c in workload {
% for Property, Set in Properties.items():
blackHole(is${Property}(c))
% end
}
}
}

@inline(never)
public func run_CharacterPropertiesStashed(_ N: Int) {
for _ in 1...N {
for c in workload {
% for Property, Set in Properties.items():
blackHole(is${Property}Stashed(c))
% end
}
}
}

// TODO: run_CharacterPropertiesComputed

3 changes: 3 additions & 0 deletions benchmark/utils/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import ByteSwap
import CString
import Calculator
import CaptureProp
import CharacterProperties
import CharacterLiteralsLarge
import CharacterLiteralsSmall
import Chars
Expand Down Expand Up @@ -153,6 +154,8 @@ registerBenchmark(ByteSwap)
registerBenchmark(CString)
registerBenchmark(Calculator)
registerBenchmark(CaptureProp)
registerBenchmark(CharacterPropertiesFetch)
registerBenchmark(CharacterPropertiesStashed)
registerBenchmark(CharacterLiteralsLarge)
registerBenchmark(CharacterLiteralsSmall)
registerBenchmark(Chars)
Expand Down