Skip to content

Commit

Permalink
add swift-dom benchmark and...
Browse files Browse the repository at this point in the history
- added `swift-nio` dependency (for `ByteBuffer`)
- updated `Elementary` to 0.4.1
- updated README benchmark pngs
  • Loading branch information
RandomHashTags committed Oct 17, 2024
1 parent 6199304 commit 3cc427f
Show file tree
Hide file tree
Showing 12 changed files with 103 additions and 47 deletions.
23 changes: 23 additions & 0 deletions Benchmarks/Benchmarks/Benchmarks/Benchmarks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ let benchmarks = {
"Plot" : PlotTests(),
"Pointfreeco" : SwiftHTMLPFTests(),
"SwiftHTMLKit" : SwiftHTMLKitTests(),
"SwiftDOM" : SwiftDOMTests(),
"Swim" : SwimTests(),
"VaporHTMLKit" : VaporHTMLKitTests(),
"Vaux (custom renderer)" : VauxTests()
Expand All @@ -49,4 +50,26 @@ let benchmarks = {
}
}
}

/*let test:SwiftHTMLKitTests = SwiftHTMLKitTests()
Benchmark("SwiftHTMLKit static (StaticString)") {
for _ in $0.scaledIterations {
blackHole(test.staticHTML())
}
}
Benchmark("SwiftHTMLKit static ([UInt8])") {
for _ in $0.scaledIterations {
blackHole(test.staticHTMLUTF8Bytes())
}
}
Benchmark("SwiftHTMLKit static ([UInt16])") {
for _ in $0.scaledIterations {
blackHole(test.staticHTMLUTF16Bytes())
}
}
Benchmark("SwiftHTMLKit static (ByteBuffer)") {
for _ in $0.scaledIterations {
blackHole(test.staticHTMLByteBuffer())
}
}*/
}
17 changes: 12 additions & 5 deletions Benchmarks/Benchmarks/SwiftDOM/SwiftDOM.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
// Created by Evan Anderson on 10/13/24.
//

/*

import Utilities
import DOM
import HTML

package struct SwiftDOMTests : HTMLGenerator {
package init() {}
Expand All @@ -23,7 +22,7 @@ package struct SwiftDOMTests : HTMLGenerator {
return "\(document)"
}
package func dynamicHTML(_ context: HTMLContext) -> String {
let qualities:(inout DOM.HTML.ContentEncoder) throws -> () = {
let qualities:(inout HTML.ContentEncoder) throws -> () = {
for quality in context.user.qualities {
$0[.li] = quality
}
Expand All @@ -32,7 +31,7 @@ package struct SwiftDOMTests : HTMLGenerator {
$0[.html] {
$0[.head] {
$0[.meta] { $0[name: .charset] = context.charset }
$0[.title] { $0[name: .title] = context.title }
$0[.title] = context.title
$0[.meta] {
$0[name: .content] = context.meta_description
$0[name: .name] = "description"
Expand Down Expand Up @@ -61,4 +60,12 @@ package struct SwiftDOMTests : HTMLGenerator {
}
return "\(document)"
}
}*/
}

// required to compile
extension String:HTML.OutputStreamable
{
}
extension String:SVG.OutputStreamable
{
}
37 changes: 36 additions & 1 deletion Benchmarks/Benchmarks/SwiftHTMLKit/SwiftHTMLKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import Utilities
import SwiftHTMLKit
import NIOCore

package struct SwiftHTMLKitTests : HTMLGenerator {
package init() {}
Expand All @@ -21,8 +22,42 @@ package struct SwiftHTMLKitTests : HTMLGenerator {
)
)
}

package func staticHTMLUTF8Bytes() -> [UInt8] {
#htmlUTF8Bytes(
#head(
#title("StaticView")
),
#body(
#h1("Swift HTML Benchmarks")
)
)
}

package func staticHTMLUTF16Bytes() -> [UInt16] {
#htmlUTF16Bytes(
#head(
#title("StaticView")
),
#body(
#h1("Swift HTML Benchmarks")
)
)
}

package func staticHTMLByteBuffer() -> ByteBuffer {
#htmlByteBuffer(
#head(
#title("StaticView")
),
#body(
#h1("Swift HTML Benchmarks")
)
)
}

// performance notes
// - maping makes unneccessary copies and hurts throughput
// - maping makes unnecessary copies and hurts throughput
// - interpolation hurts performance, a lot less than maping but still noticeable
// - adding strings (concatenation) is faster than interpolation
// - calculating the size of the result than assigning the contents in a String is significantly worse than interpolation and concatenation
Expand Down
15 changes: 10 additions & 5 deletions Benchmarks/Benchmarks/UnitTests/UnitTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,33 +26,38 @@ struct UnitTests {
"Elementary" : ElementaryTests(),
"Plot" : PlotTests(),
"Pointfreeco" : SwiftHTMLPFTests(),
//"SwiftDOM" : SwiftDOMTests(),
"SwiftDOM" : SwiftDOMTests(),
"SwiftHTMLKit" : SwiftHTMLKitTests(),
"Swim" : SwimTests(),
"VaporHTMLKit" : VaporHTMLKitTests(),
"Vaux (custom renderer)" : VauxTests()
]
// Some tests fail due to:
// - Plot closes void tags
// - Swim doesn't minify (keeps new line characters and some whitespace); uses a dictionary for meta values; closes void tags
// - Vaux is doodoo

@Test func staticHTML() {
let expected_value:String = libraries["SwiftHTMLKit"]!.staticHTML()
// Swim doesn't minify (keeps new line characters and some whitespace)
for (key, value) in libraries {
var string:String = value.staticHTML()
if key == "Swim" {
string.replace("\n", with: "")
} else if key == "SwiftDOM" {
string.replace("'", with: "\"")
}
#expect(string == expected_value, Comment(rawValue: key))
}
}
@Test func dynamicHTML() {
let context:HTMLContext = HTMLContext()
let expected_value:String = libraries["SwiftHTMLKit"]!.dynamicHTML(context)
// Plot closes void tags
// Swim doesn't minify (keeps new line characters and some whitespace); uses a dictionary for meta values; closes void tags
// Vaux is doodoo
for (key, value) in libraries {
var string:String = value.dynamicHTML(context)
if key == "Swim" {
string.replace("\n", with: "")
} else if key == "SwiftDOM" {
string.replace("'", with: "\"")
}
#expect(string == expected_value, Comment(rawValue: key))
}
Expand Down
25 changes: 14 additions & 11 deletions Benchmarks/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,34 @@ let package = Package(
.macOS(.v14)
],
dependencies: [
// dsls
.package(url: "https://github.com/ordo-one/package-benchmark", from: "1.27.0"),
.package(url: "https://github.com/swiftlang/swift-syntax", from: "600.0.0"),
// dsls
.package(name: "swift-htmlkit", path: "../"),
.package(url: "https://github.com/sliemeobn/elementary", from: "0.4.0"),
.package(url: "https://github.com/vapor-community/HTMLKit", from: "2.8.1"),
.package(url: "https://github.com/pointfreeco/swift-html", from: "0.4.1"),
.package(url: "https://github.com/sliemeobn/elementary", exact: "0.4.1"),
.package(url: "https://github.com/vapor-community/HTMLKit", exact: "2.8.1"),
.package(url: "https://github.com/pointfreeco/swift-html", exact: "0.4.1"),
.package(url: "https://github.com/RandomHashTags/fork-bb-swift-html", branch: "main"),
.package(url: "https://github.com/JohnSundell/Plot", from: "0.14.0"),
.package(url: "https://github.com/JohnSundell/Plot", exact: "0.14.0"),
//.package(url: "https://github.com/toucansites/toucan", from: "1.0.0-alpha.1"), // unstable
.package(url: "https://github.com/robb/Swim", from: "0.4.0"),
.package(url: "https://github.com/robb/Swim", exact: "0.4.0"),
.package(url: "https://github.com/RandomHashTags/fork-Vaux", branch: "master"),
//.package(url: "https://github.com/tayloraswift/swift-dom", from: "1.1.0"), // bad exports
.package(url: "https://github.com/RandomHashTags/fork-swift-dom", branch: "master"),
//.package(url: "https://github.com/TokamakUI/Tokamak", from: "0.11.1"), // swift-benchmark problem

.package(url: "https://github.com/vapor/leaf", from: "4.4.0"),
.package(url: "https://github.com/vapor/leaf", exact: "4.4.0"),

// networking
.package(url: "https://github.com/apple/swift-nio", from: "2.75.0"),
.package(url: "https://github.com/vapor/vapor", from: "4.106.0"),
.package(url: "https://github.com/hummingbird-project/hummingbird", from: "2.1.0")
],
targets: [
.target(
name: "Utilities",
dependencies: [
.product(name: "NIOCore", package: "swift-nio")
],
path: "Benchmarks/Utilities"
),
.target(
Expand Down Expand Up @@ -63,8 +67,7 @@ let package = Package(
name: "TestSwiftDOM",
dependencies: [
"Utilities",
//.product(name: "DOM", package: "swift-dom", moduleAliases: ["DOM":"SwiftDOM"]),
//.product(name: "HTML", package: "swift-dom", moduleAliases: ["HTML":"SwiftDOMHTML"])
.product(name: "DOM", package: "fork-swift-dom", moduleAliases: ["DOM":"SwiftDOM"])
],
path: "Benchmarks/SwiftDOM"
),
Expand All @@ -81,7 +84,7 @@ let package = Package(
dependencies: [
"Utilities",
.product(name: "HTMLKit", package: "swift-htmlkit", moduleAliases: ["HTMLKit":"SwiftHTMLKit"]),
.product(name: "HTMLKit", package: "HTMLKit", moduleAliases: ["HTMLKit":"VaporHTMLKit"]),
.product(name: "HTMLKit", package: "HTMLKit", moduleAliases: ["HTMLKit":"VaporHTMLKit"])
],
path: "Benchmarks/SwiftHTMLKit"
),
Expand Down
Binary file modified Benchmarks/img/throughput_dynamic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Benchmarks/img/throughput_static.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 5 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@ let package = Package(
),
],
dependencies: [
.package(url: "https://github.com/swiftlang/swift-syntax.git", from: "600.0.0"),
.package(url: "https://github.com/swiftlang/swift-syntax", from: "600.0.0"),
.package(url: "https://github.com/apple/swift-nio", from: "2.75.0")
],
targets: [
.target(
name: "HTMLKitUtilities",
dependencies: []
dependencies: [
.product(name: "NIOCore", package: "swift-nio")
]
),
.macro(
name: "HTMLKitMacros",
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,9 @@ Use a different html macro. Currently supported types (more to come with new lan
## Benchmarks
- Libraries tested
- [BinaryBuilds/swift-html](https://github.com/BinaryBirds/swift-html) v1.7.0 (patched version [here](https://github.com/RandomHashTags/fork-bb-swift-html))
- [sliemeobn/elementary](https://github.com/sliemeobn/elementary) v0.4.0
- [sliemeobn/elementary](https://github.com/sliemeobn/elementary) v0.4.1
- [JohnSundell/Plot](https://github.com/JohnSundell/Plot) v0.14.0
- [tayloraswift/swift-dom](https://github.com/tayloraswift/swift-dom) v1.1.0 (patched version [here](https://github.com/RandomHashTags/fork-swift-dom))
- [RandomHashTags/swift-htmlkit](https://github.com/RandomHashTags/swift-htmlkit) v0.6.0 (this library)
- [pointfreeco/swift-html](https://github.com/pointfreeco/swift-html) v0.4.1
- [robb/Swim](https://github.com/robb/Swim) v0.4.0
Expand Down
5 changes: 0 additions & 5 deletions Sources/HTMLKit/HTMLKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ import HTMLKitUtilities
import struct Foundation.Data
#endif


/*#if canImport(NIOCore)
import struct NIOCore.ByteBuffer
#endif*/

// MARK: StaticString equality
public extension StaticString {
Expand Down Expand Up @@ -48,10 +45,8 @@ public macro htmlUTF8CString<T: ExpressibleByStringLiteral>(attributes: [HTMLEle
public macro htmlData<T: ExpressibleByStringLiteral>(attributes: [HTMLElementAttribute] = [], xmlns: T? = nil, _ innerHTML: T...) -> Data = #externalMacro(module: "HTMLKitMacros", type: "HTMLElement")
#endif

/*#if canImport(NIOCore)
@freestanding(expression)
public macro htmlByteBuffer<T: ExpressibleByStringLiteral>(attributes: [HTMLElementAttribute] = [], xmlns: T? = nil, _ innerHTML: T...) -> ByteBuffer = #externalMacro(module: "HTMLKitMacros", type: "HTMLElement")
#endif*/

// MARK: Elements

Expand Down
12 changes: 1 addition & 11 deletions Sources/HTMLKitMacros/HTMLElement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,17 @@ import HTMLKitUtilities
import struct Foundation.Data
#endif

/*#if canImport(NIOCore)
import struct NIOCore.ByteBuffer
#endif*/

enum HTMLElement : ExpressionMacro {
static func expansion(of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext) throws -> ExprSyntax {
let string:String = parse_macro(context: context, expression: node.macroExpansion!)
var set:Set<HTMLElementType?> = [.htmlUTF8Bytes, .htmlUTF16Bytes, .htmlUTF8CString]
var set:Set<HTMLElementType?> = [.htmlUTF8Bytes, .htmlUTF16Bytes, .htmlUTF8CString, .htmlByteBuffer]

#if canImport(Foundation)
set.insert(.htmlData)
#endif

/*#if canImport(NIOCore)
set.insert(.htmlByteBuffer)
#endif*/

if set.contains(HTMLElementType(rawValue: node.macroName.text)) {
let has_interpolation:Bool = !string.ranges(of: try! Regex("\\((.*)\\)")).isEmpty
guard !has_interpolation else {
Expand All @@ -53,10 +47,8 @@ enum HTMLElement : ExpressionMacro {
return "Data(\(raw: bytes([UInt8](string.utf8))))"
#endif

/*#if canImport(NIOCore)
case .htmlByteBuffer:
return "ByteBuffer(bytes: \(raw: bytes([UInt8](string.utf8))))"
#endif*/

default: break
}
Expand Down Expand Up @@ -373,9 +365,7 @@ enum HTMLElementType : String, CaseIterable {
case htmlData
#endif

/*#if canImport(NIOCore)
case htmlByteBuffer
#endif*/

case a
case abbr
Expand Down
6 changes: 0 additions & 6 deletions Tests/HTMLKitTests/HTMLKitTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ import HTMLKit
import struct Foundation.Data
#endif

/*#if canImport(NIOCore)
import struct NIOCore.ByteBuffer
#endif*/

// MARK: Escaping HTML
struct HTMLKitTests {
Expand Down Expand Up @@ -58,9 +56,7 @@ extension HTMLKitTests {
#if canImport(Foundation)
let _:Data = #htmlData("")
#endif
/*#if canImport(NIOCore)
let _:ByteBuffer = #htmlByteBuffer("")
#endif*/

//let bro:String = ""
//let _:[UInt8] = #htmlUTF8Bytes("\(bro)")
Expand All @@ -85,11 +81,9 @@ extension HTMLKitTests {
#htmlData("")
}
#endif
/*#if canImport(NIOCore)
func representation6() -> ByteBuffer {
#htmlByteBuffer("")
}
#endif*/
}

// MARK: Element tests
Expand Down

0 comments on commit 3cc427f

Please sign in to comment.