add performance tester (#396)
Motivation: NIO wants to be a highly performant library and therefore we need performance tests. Modifications: Add a couple of performance tests. Result: Easier to talk about perf improvements/regressions if we use similar benchmarks.
This commit is contained in:
parent
1fa9f15aca
commit
ec79bf2b0b
|
@ -51,6 +51,8 @@ var targets: [PackageDescription.Target] = [
|
||||||
dependencies: ["NIO", "NIOHTTP1", "CNIOSHA1"]),
|
dependencies: ["NIO", "NIOHTTP1", "CNIOSHA1"]),
|
||||||
.target(name: "NIOWebSocketServer",
|
.target(name: "NIOWebSocketServer",
|
||||||
dependencies: ["NIO", "NIOHTTP1", "NIOWebSocket"]),
|
dependencies: ["NIO", "NIOHTTP1", "NIOWebSocket"]),
|
||||||
|
.target(name: "NIOPerformanceTester",
|
||||||
|
dependencies: ["NIO", "NIOHTTP1", "NIOFoundationCompat"]),
|
||||||
.testTarget(name: "NIOTests",
|
.testTarget(name: "NIOTests",
|
||||||
dependencies: ["NIO", "NIOFoundationCompat"]),
|
dependencies: ["NIO", "NIOFoundationCompat"]),
|
||||||
.testTarget(name: "NIOConcurrencyHelpersTests",
|
.testTarget(name: "NIOConcurrencyHelpersTests",
|
||||||
|
@ -73,6 +75,8 @@ let package = Package(
|
||||||
.executable(name: "NIOHTTP1Server", targets: ["NIOHTTP1Server"]),
|
.executable(name: "NIOHTTP1Server", targets: ["NIOHTTP1Server"]),
|
||||||
.executable(name: "NIOWebSocketServer",
|
.executable(name: "NIOWebSocketServer",
|
||||||
targets: ["NIOWebSocketServer"]),
|
targets: ["NIOWebSocketServer"]),
|
||||||
|
.executable(name: "NIOPerformanceTester",
|
||||||
|
targets: ["NIOPerformanceTester"]),
|
||||||
.library(name: "NIO", targets: ["NIO"]),
|
.library(name: "NIO", targets: ["NIO"]),
|
||||||
.library(name: "NIOTLS", targets: ["NIOTLS"]),
|
.library(name: "NIOTLS", targets: ["NIOTLS"]),
|
||||||
.library(name: "NIOHTTP1", targets: ["NIOHTTP1"]),
|
.library(name: "NIOHTTP1", targets: ["NIOHTTP1"]),
|
||||||
|
|
|
@ -0,0 +1,505 @@
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This source file is part of the SwiftNIO open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
|
||||||
|
// Licensed under Apache License v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE.txt for license information
|
||||||
|
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
import NIO
|
||||||
|
import NIOHTTP1
|
||||||
|
import NIOFoundationCompat
|
||||||
|
import Foundation
|
||||||
|
import Dispatch
|
||||||
|
|
||||||
|
// MARK: Test Harness
|
||||||
|
|
||||||
|
var warning: String = ""
|
||||||
|
assert({
|
||||||
|
print("======================================================")
|
||||||
|
print("= YOU ARE RUNNING NIOPerformanceTester IN DEBUG MODE =")
|
||||||
|
print("======================================================")
|
||||||
|
warning = " <<< DEBUG MODE >>>"
|
||||||
|
return true
|
||||||
|
}())
|
||||||
|
|
||||||
|
public func measure(_ fn: () throws -> Int) rethrows -> [TimeInterval] {
|
||||||
|
func measureOne(_ fn: () throws -> Int) rethrows -> TimeInterval {
|
||||||
|
let start = Date()
|
||||||
|
_ = try fn()
|
||||||
|
let end = Date()
|
||||||
|
return end.timeIntervalSince(start)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = try measureOne(fn) /* pre-heat and throw away */
|
||||||
|
var measurements = Array(repeating: 0.0, count: 10)
|
||||||
|
for i in 0..<10 {
|
||||||
|
measurements[i] = try measureOne(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
return measurements
|
||||||
|
}
|
||||||
|
|
||||||
|
public func measureAndPrint(desc: String, fn: () throws -> Int) rethrows -> Void {
|
||||||
|
print("measuring\(warning): \(desc): ", terminator: "")
|
||||||
|
let measurements = try measure(fn)
|
||||||
|
print(measurements.reduce("") { $0 + "\($1), " })
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Utilities
|
||||||
|
|
||||||
|
private final class SimpleHTTPServer: ChannelInboundHandler {
|
||||||
|
typealias InboundIn = HTTPServerRequestPart
|
||||||
|
typealias OutboundOut = HTTPServerResponsePart
|
||||||
|
|
||||||
|
private var files: [String] = Array()
|
||||||
|
private var seenEnd: Bool = false
|
||||||
|
private var sentEnd: Bool = false
|
||||||
|
private var isOpen: Bool = true
|
||||||
|
|
||||||
|
private let cachedHead: HTTPResponseHead
|
||||||
|
private let cachedBody: [UInt8]
|
||||||
|
private let bodyLength = 1024
|
||||||
|
private let numberOfAdditionalHeaders = 10
|
||||||
|
|
||||||
|
init() {
|
||||||
|
var head = HTTPResponseHead(version: HTTPVersion(major: 1, minor: 1), status: .ok)
|
||||||
|
head.headers.add(name: "Content-Length", value: "\(self.bodyLength)")
|
||||||
|
for i in 0..<self.numberOfAdditionalHeaders {
|
||||||
|
head.headers.add(name: "X-Random-Extra-Header", value: "\(i)")
|
||||||
|
}
|
||||||
|
self.cachedHead = head
|
||||||
|
|
||||||
|
var body: [UInt8] = []
|
||||||
|
body.reserveCapacity(self.bodyLength)
|
||||||
|
for i in 0..<self.bodyLength {
|
||||||
|
body.append(UInt8(i % Int(UInt8.max)))
|
||||||
|
}
|
||||||
|
self.cachedBody = body
|
||||||
|
}
|
||||||
|
|
||||||
|
public func channelRead(ctx: ChannelHandlerContext, data: NIOAny) {
|
||||||
|
if case .head(let req) = self.unwrapInboundIn(data) {
|
||||||
|
switch req.uri {
|
||||||
|
case "/perf-test-1":
|
||||||
|
var buffer = ctx.channel.allocator.buffer(capacity: self.cachedBody.count)
|
||||||
|
buffer.write(bytes: self.cachedBody)
|
||||||
|
ctx.write(self.wrapOutboundOut(.head(self.cachedHead)), promise: nil)
|
||||||
|
ctx.write(self.wrapOutboundOut(.body(.byteBuffer(buffer))), promise: nil)
|
||||||
|
ctx.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: nil)
|
||||||
|
return
|
||||||
|
case "/perf-test-2":
|
||||||
|
var req = HTTPResponseHead(version: HTTPVersion(major: 1, minor: 1), status: .ok)
|
||||||
|
for i in 1...8 {
|
||||||
|
req.headers.add(name: "X-ResponseHeader-\(i)", value: "foo")
|
||||||
|
}
|
||||||
|
req.headers.add(name: "content-length", value: "0")
|
||||||
|
ctx.write(self.wrapOutboundOut(.head(req)), promise: nil)
|
||||||
|
ctx.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: nil)
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
fatalError("unknown uri \(req.uri)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let group = MultiThreadedEventLoopGroup(numThreads: System.coreCount)
|
||||||
|
defer {
|
||||||
|
try! group.syncShutdownGracefully()
|
||||||
|
}
|
||||||
|
|
||||||
|
let serverChannel = try ServerBootstrap(group: group)
|
||||||
|
.serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
|
||||||
|
.childChannelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1)
|
||||||
|
.childChannelInitializer { channel in
|
||||||
|
channel.pipeline.configureHTTPServerPipeline(withPipeliningAssistance: true).then {
|
||||||
|
channel.pipeline.add(handler: SimpleHTTPServer())
|
||||||
|
}
|
||||||
|
}.bind(host: "127.0.0.1", port: 0).wait()
|
||||||
|
|
||||||
|
defer {
|
||||||
|
try! serverChannel.close().wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
var head = HTTPRequestHead(version: HTTPVersion(major: 1, minor: 1), method: .GET, uri: "/perf-test-1")
|
||||||
|
head.headers.add(name: "Host", value: "localhost")
|
||||||
|
|
||||||
|
final class RepeatedRequests: ChannelInboundHandler {
|
||||||
|
typealias InboundIn = HTTPClientResponsePart
|
||||||
|
typealias OutboundOut = HTTPClientRequestPart
|
||||||
|
|
||||||
|
private let numberOfRequests: Int
|
||||||
|
private var remainingNumberOfRequests: Int
|
||||||
|
private var doneRequests = 0
|
||||||
|
private let isDonePromise: EventLoopPromise<Int>
|
||||||
|
|
||||||
|
init(numberOfRequests: Int, eventLoop: EventLoop) {
|
||||||
|
self.remainingNumberOfRequests = numberOfRequests
|
||||||
|
self.numberOfRequests = numberOfRequests
|
||||||
|
self.isDonePromise = eventLoop.newPromise()
|
||||||
|
}
|
||||||
|
|
||||||
|
func wait() throws -> Int {
|
||||||
|
let reqs = try self.isDonePromise.futureResult.wait()
|
||||||
|
precondition(reqs == self.numberOfRequests)
|
||||||
|
return reqs
|
||||||
|
}
|
||||||
|
|
||||||
|
func errorCaught(ctx: ChannelHandlerContext, error: Error) {
|
||||||
|
ctx.channel.close(promise: nil)
|
||||||
|
self.isDonePromise.fail(error: error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func channelRead(ctx: ChannelHandlerContext, data: NIOAny) {
|
||||||
|
let reqPart = self.unwrapInboundIn(data)
|
||||||
|
if case .end(nil) = reqPart {
|
||||||
|
if self.remainingNumberOfRequests <= 0 {
|
||||||
|
ctx.channel.close().map { self.doneRequests }.cascade(promise: self.isDonePromise)
|
||||||
|
} else {
|
||||||
|
self.doneRequests += 1
|
||||||
|
self.remainingNumberOfRequests -= 1
|
||||||
|
|
||||||
|
ctx.write(self.wrapOutboundOut(.head(head)), promise: nil)
|
||||||
|
ctx.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Performance Tests
|
||||||
|
|
||||||
|
measureAndPrint(desc: "bytebuffer_lots_of_rw") {
|
||||||
|
let dispatchData = ("A" as StaticString).withUTF8Buffer { ptr in
|
||||||
|
DispatchData(bytes: UnsafeRawBufferPointer(start: UnsafeRawPointer(ptr.baseAddress), count: ptr.count))
|
||||||
|
}
|
||||||
|
var buffer = ByteBufferAllocator().buffer(capacity: 7 * 1024 * 1024)
|
||||||
|
let foundationData = "A".data(using: .utf8)!
|
||||||
|
@inline(never)
|
||||||
|
func doWrites(buffer: inout ByteBuffer) {
|
||||||
|
/* all of those should be 0 allocations */
|
||||||
|
|
||||||
|
// buffer.write(bytes: foundationData) // see SR-7542
|
||||||
|
buffer.write(bytes: [0x41])
|
||||||
|
buffer.write(bytes: dispatchData)
|
||||||
|
buffer.write(bytes: "A".utf8)
|
||||||
|
buffer.write(string: "A")
|
||||||
|
buffer.write(staticString: "A")
|
||||||
|
buffer.write(integer: 0x41, as: UInt8.self)
|
||||||
|
}
|
||||||
|
@inline(never)
|
||||||
|
func doReads(buffer: inout ByteBuffer) {
|
||||||
|
/* these ones are zero allocations */
|
||||||
|
let val = buffer.readInteger(as: UInt8.self)
|
||||||
|
precondition(0x41 == val, "\(val!)")
|
||||||
|
var slice = buffer.readSlice(length: 1)
|
||||||
|
let sliceVal = slice!.readInteger(as: UInt8.self)
|
||||||
|
precondition(0x41 == sliceVal, "\(sliceVal!)")
|
||||||
|
buffer.withUnsafeReadableBytes { ptr in
|
||||||
|
precondition(ptr[0] == 0x41)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* those down here should be one allocation each */
|
||||||
|
let arr = buffer.readBytes(length: 1)
|
||||||
|
precondition([0x41] == arr!, "\(arr!)")
|
||||||
|
let str = buffer.readString(length: 1)
|
||||||
|
precondition("A" == str, "\(str!)")
|
||||||
|
}
|
||||||
|
for _ in 0 ..< 1024*1024 {
|
||||||
|
doWrites(buffer: &buffer)
|
||||||
|
doReads(buffer: &buffer)
|
||||||
|
}
|
||||||
|
return buffer.readableBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeExampleHTTPResponseAsString(buffer: inout ByteBuffer) {
|
||||||
|
buffer.write(string: "HTTP/1.1 200 OK")
|
||||||
|
buffer.write(string: "\r\n")
|
||||||
|
buffer.write(string: "Connection")
|
||||||
|
buffer.write(string: ":")
|
||||||
|
buffer.write(string: " ")
|
||||||
|
buffer.write(string: "close")
|
||||||
|
buffer.write(string: "\r\n")
|
||||||
|
buffer.write(string: "Proxy-Connection")
|
||||||
|
buffer.write(string: ":")
|
||||||
|
buffer.write(string: " ")
|
||||||
|
buffer.write(string: "close")
|
||||||
|
buffer.write(string: "\r\n")
|
||||||
|
buffer.write(string: "Via")
|
||||||
|
buffer.write(string: ":")
|
||||||
|
buffer.write(string: " ")
|
||||||
|
buffer.write(string: "HTTP/1.1 localhost (IBM-PROXY-WTE)")
|
||||||
|
buffer.write(string: "\r\n")
|
||||||
|
buffer.write(string: "Date")
|
||||||
|
buffer.write(string: ":")
|
||||||
|
buffer.write(string: " ")
|
||||||
|
buffer.write(string: "Tue, 08 May 2018 13:42:56 GMT")
|
||||||
|
buffer.write(string: "\r\n")
|
||||||
|
buffer.write(string: "Server")
|
||||||
|
buffer.write(string: ":")
|
||||||
|
buffer.write(string: " ")
|
||||||
|
buffer.write(string: "Apache/2.2.15 (Red Hat)")
|
||||||
|
buffer.write(string: "\r\n")
|
||||||
|
buffer.write(string: "Strict-Transport-Security")
|
||||||
|
buffer.write(string: ":")
|
||||||
|
buffer.write(string: " ")
|
||||||
|
buffer.write(string: "max-age=15768000; includeSubDomains")
|
||||||
|
buffer.write(string: "\r\n")
|
||||||
|
buffer.write(string: "Last-Modified")
|
||||||
|
buffer.write(string: ":")
|
||||||
|
buffer.write(string: " ")
|
||||||
|
buffer.write(string: "Tue, 08 May 2018 13:39:13 GMT")
|
||||||
|
buffer.write(string: "\r\n")
|
||||||
|
buffer.write(string: "ETag")
|
||||||
|
buffer.write(string: ":")
|
||||||
|
buffer.write(string: " ")
|
||||||
|
buffer.write(string: "357031-1809-56bb1e96a6240")
|
||||||
|
buffer.write(string: "\r\n")
|
||||||
|
buffer.write(string: "Accept-Ranges")
|
||||||
|
buffer.write(string: ":")
|
||||||
|
buffer.write(string: " ")
|
||||||
|
buffer.write(string: "bytes")
|
||||||
|
buffer.write(string: "\r\n")
|
||||||
|
buffer.write(string: "Content-Length")
|
||||||
|
buffer.write(string: ":")
|
||||||
|
buffer.write(string: " ")
|
||||||
|
buffer.write(string: "6153")
|
||||||
|
buffer.write(string: "\r\n")
|
||||||
|
buffer.write(string: "Content-Type")
|
||||||
|
buffer.write(string: ":")
|
||||||
|
buffer.write(string: " ")
|
||||||
|
buffer.write(string: "text/html; charset=UTF-8")
|
||||||
|
buffer.write(string: "\r\n")
|
||||||
|
buffer.write(string: "\r\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeExampleHTTPResponseAsStaticString(buffer: inout ByteBuffer) {
|
||||||
|
buffer.write(staticString: "HTTP/1.1 200 OK")
|
||||||
|
buffer.write(staticString: "\r\n")
|
||||||
|
buffer.write(staticString: "Connection")
|
||||||
|
buffer.write(staticString: ":")
|
||||||
|
buffer.write(staticString: " ")
|
||||||
|
buffer.write(staticString: "close")
|
||||||
|
buffer.write(staticString: "\r\n")
|
||||||
|
buffer.write(staticString: "Proxy-Connection")
|
||||||
|
buffer.write(staticString: ":")
|
||||||
|
buffer.write(staticString: " ")
|
||||||
|
buffer.write(staticString: "close")
|
||||||
|
buffer.write(staticString: "\r\n")
|
||||||
|
buffer.write(staticString: "Via")
|
||||||
|
buffer.write(staticString: ":")
|
||||||
|
buffer.write(staticString: " ")
|
||||||
|
buffer.write(staticString: "HTTP/1.1 localhost (IBM-PROXY-WTE)")
|
||||||
|
buffer.write(staticString: "\r\n")
|
||||||
|
buffer.write(staticString: "Date")
|
||||||
|
buffer.write(staticString: ":")
|
||||||
|
buffer.write(staticString: " ")
|
||||||
|
buffer.write(staticString: "Tue, 08 May 2018 13:42:56 GMT")
|
||||||
|
buffer.write(staticString: "\r\n")
|
||||||
|
buffer.write(staticString: "Server")
|
||||||
|
buffer.write(staticString: ":")
|
||||||
|
buffer.write(staticString: " ")
|
||||||
|
buffer.write(staticString: "Apache/2.2.15 (Red Hat)")
|
||||||
|
buffer.write(staticString: "\r\n")
|
||||||
|
buffer.write(staticString: "Strict-Transport-Security")
|
||||||
|
buffer.write(staticString: ":")
|
||||||
|
buffer.write(staticString: " ")
|
||||||
|
buffer.write(staticString: "max-age=15768000; includeSubDomains")
|
||||||
|
buffer.write(staticString: "\r\n")
|
||||||
|
buffer.write(staticString: "Last-Modified")
|
||||||
|
buffer.write(staticString: ":")
|
||||||
|
buffer.write(staticString: " ")
|
||||||
|
buffer.write(staticString: "Tue, 08 May 2018 13:39:13 GMT")
|
||||||
|
buffer.write(staticString: "\r\n")
|
||||||
|
buffer.write(staticString: "ETag")
|
||||||
|
buffer.write(staticString: ":")
|
||||||
|
buffer.write(staticString: " ")
|
||||||
|
buffer.write(staticString: "357031-1809-56bb1e96a6240")
|
||||||
|
buffer.write(staticString: "\r\n")
|
||||||
|
buffer.write(staticString: "Accept-Ranges")
|
||||||
|
buffer.write(staticString: ":")
|
||||||
|
buffer.write(staticString: " ")
|
||||||
|
buffer.write(staticString: "bytes")
|
||||||
|
buffer.write(staticString: "\r\n")
|
||||||
|
buffer.write(staticString: "Content-Length")
|
||||||
|
buffer.write(staticString: ":")
|
||||||
|
buffer.write(staticString: " ")
|
||||||
|
buffer.write(staticString: "6153")
|
||||||
|
buffer.write(staticString: "\r\n")
|
||||||
|
buffer.write(staticString: "Content-Type")
|
||||||
|
buffer.write(staticString: ":")
|
||||||
|
buffer.write(staticString: " ")
|
||||||
|
buffer.write(staticString: "text/html; charset=UTF-8")
|
||||||
|
buffer.write(staticString: "\r\n")
|
||||||
|
buffer.write(staticString: "\r\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
measureAndPrint(desc: "bytebuffer_write_http_response_ascii_only_as_string") {
|
||||||
|
var buffer = ByteBufferAllocator().buffer(capacity: 16 * 1024)
|
||||||
|
for _ in 0..<20_000 {
|
||||||
|
writeExampleHTTPResponseAsString(buffer: &buffer)
|
||||||
|
buffer.write(string: htmlASCIIOnly)
|
||||||
|
buffer.clear()
|
||||||
|
}
|
||||||
|
return buffer.readableBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
measureAndPrint(desc: "bytebuffer_write_http_response_ascii_only_as_staticstring") {
|
||||||
|
var buffer = ByteBufferAllocator().buffer(capacity: 16 * 1024)
|
||||||
|
for _ in 0..<20_000 {
|
||||||
|
writeExampleHTTPResponseAsStaticString(buffer: &buffer)
|
||||||
|
buffer.write(staticString: htmlASCIIOnlyStaticString)
|
||||||
|
buffer.clear()
|
||||||
|
}
|
||||||
|
return buffer.readableBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
measureAndPrint(desc: "bytebuffer_write_http_response_some_nonascii_as_string") {
|
||||||
|
var buffer = ByteBufferAllocator().buffer(capacity: 16 * 1024)
|
||||||
|
for _ in 0..<20_000 {
|
||||||
|
writeExampleHTTPResponseAsString(buffer: &buffer)
|
||||||
|
buffer.write(string: htmlMostlyASCII)
|
||||||
|
buffer.clear()
|
||||||
|
}
|
||||||
|
return buffer.readableBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
measureAndPrint(desc: "bytebuffer_write_http_response_some_nonascii_as_staticstring") {
|
||||||
|
var buffer = ByteBufferAllocator().buffer(capacity: 16 * 1024)
|
||||||
|
for _ in 0..<20_000 {
|
||||||
|
writeExampleHTTPResponseAsStaticString(buffer: &buffer)
|
||||||
|
buffer.write(staticString: htmlMostlyASCIIStaticString)
|
||||||
|
buffer.clear()
|
||||||
|
}
|
||||||
|
return buffer.readableBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
try measureAndPrint(desc: "no-net_http1_10k_reqs_1_conn") {
|
||||||
|
final class MeasuringHandler: ChannelDuplexHandler {
|
||||||
|
typealias InboundIn = Never
|
||||||
|
typealias InboundOut = ByteBuffer
|
||||||
|
typealias OutboundIn = ByteBuffer
|
||||||
|
|
||||||
|
private var requestBuffer: ByteBuffer!
|
||||||
|
private var expectedResponseBuffer: ByteBuffer?
|
||||||
|
private var remainingNumberOfRequests: Int
|
||||||
|
|
||||||
|
private let completionHandler: (Int) -> Void
|
||||||
|
private let numberOfRequests: Int
|
||||||
|
|
||||||
|
init(numberOfRequests: Int, completionHandler: @escaping (Int) -> Void) {
|
||||||
|
self.completionHandler = completionHandler
|
||||||
|
self.numberOfRequests = numberOfRequests
|
||||||
|
self.remainingNumberOfRequests = numberOfRequests
|
||||||
|
}
|
||||||
|
|
||||||
|
func handlerAdded(ctx: ChannelHandlerContext) {
|
||||||
|
self.requestBuffer = ctx.channel.allocator.buffer(capacity: 512)
|
||||||
|
self.requestBuffer.write(string: """
|
||||||
|
GET /perf-test-2 HTTP/1.1\r
|
||||||
|
Host: example.com\r
|
||||||
|
X-Some-Header-1: foo\r
|
||||||
|
X-Some-Header-2: foo\r
|
||||||
|
X-Some-Header-3: foo\r
|
||||||
|
X-Some-Header-4: foo\r
|
||||||
|
X-Some-Header-5: foo\r
|
||||||
|
X-Some-Header-6: foo\r
|
||||||
|
X-Some-Header-7: foo\r
|
||||||
|
X-Some-Header-8: foo\r\n\r\n
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
func write(ctx: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise<Void>?) {
|
||||||
|
var buf = self.unwrapOutboundIn(data)
|
||||||
|
if self.expectedResponseBuffer == nil {
|
||||||
|
self.expectedResponseBuffer = buf
|
||||||
|
}
|
||||||
|
precondition(buf == self.expectedResponseBuffer, "got \(buf.readString(length: buf.readableBytes)!)")
|
||||||
|
let channel = ctx.channel
|
||||||
|
self.remainingNumberOfRequests -= 1
|
||||||
|
if self.remainingNumberOfRequests > 0 {
|
||||||
|
ctx.eventLoop.execute {
|
||||||
|
self.kickOff(channel: channel)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.completionHandler(self.numberOfRequests)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func kickOff(channel: Channel) -> Void {
|
||||||
|
try! (channel as! EmbeddedChannel).writeInbound(self.requestBuffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let eventLoop = EmbeddedEventLoop()
|
||||||
|
let channel = EmbeddedChannel(handler: nil, loop: eventLoop)
|
||||||
|
var done = false
|
||||||
|
var requestsDone = -1
|
||||||
|
let measuringHandler = MeasuringHandler(numberOfRequests: 10_000) { reqs in
|
||||||
|
requestsDone = reqs
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
try channel.pipeline.configureHTTPServerPipeline(withPipeliningAssistance: true,
|
||||||
|
withErrorHandling: true).then {
|
||||||
|
channel.pipeline.add(handler: SimpleHTTPServer())
|
||||||
|
}.then {
|
||||||
|
channel.pipeline.add(handler: measuringHandler, first: true)
|
||||||
|
}.wait()
|
||||||
|
|
||||||
|
measuringHandler.kickOff(channel: channel)
|
||||||
|
|
||||||
|
while !done {
|
||||||
|
eventLoop.run()
|
||||||
|
}
|
||||||
|
_ = try channel.finish()
|
||||||
|
precondition(requestsDone == 10_000)
|
||||||
|
return requestsDone
|
||||||
|
}
|
||||||
|
|
||||||
|
measureAndPrint(desc: "http1_10k_reqs_1_conn") {
|
||||||
|
let repeatedRequestsHandler = RepeatedRequests(numberOfRequests: 10_000, eventLoop: group.next())
|
||||||
|
|
||||||
|
let clientChannel = try! ClientBootstrap(group: group)
|
||||||
|
.channelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1)
|
||||||
|
.channelInitializer { channel in
|
||||||
|
channel.pipeline.addHTTPClientHandlers().then {
|
||||||
|
channel.pipeline.add(handler: repeatedRequestsHandler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.connect(to: serverChannel.localAddress!)
|
||||||
|
.wait()
|
||||||
|
|
||||||
|
clientChannel.write(NIOAny(HTTPClientRequestPart.head(head)), promise: nil)
|
||||||
|
try! clientChannel.writeAndFlush(NIOAny(HTTPClientRequestPart.end(nil))).wait()
|
||||||
|
return try! repeatedRequestsHandler.wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
measureAndPrint(desc: "http1_10k_reqs_100_conns") {
|
||||||
|
var reqs: [Int] = []
|
||||||
|
let reqsPerConn = 100
|
||||||
|
reqs.reserveCapacity(reqsPerConn)
|
||||||
|
for _ in 0..<reqsPerConn {
|
||||||
|
let repeatedRequestsHandler = RepeatedRequests(numberOfRequests: 100, eventLoop: group.next())
|
||||||
|
|
||||||
|
let clientChannel = try! ClientBootstrap(group: group)
|
||||||
|
.channelInitializer { channel in
|
||||||
|
channel.pipeline.addHTTPClientHandlers().then {
|
||||||
|
channel.pipeline.add(handler: repeatedRequestsHandler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.connect(to: serverChannel.localAddress!)
|
||||||
|
.wait()
|
||||||
|
|
||||||
|
clientChannel.write(NIOAny(HTTPClientRequestPart.head(head)), promise: nil)
|
||||||
|
try! clientChannel.writeAndFlush(NIOAny(HTTPClientRequestPart.end(nil))).wait()
|
||||||
|
reqs.append(try! repeatedRequestsHandler.wait())
|
||||||
|
}
|
||||||
|
return reqs.reduce(0, +) / reqsPerConn
|
||||||
|
}
|
|
@ -0,0 +1,993 @@
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This source file is part of the SwiftNIO open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
|
||||||
|
// Licensed under Apache License v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE.txt for license information
|
||||||
|
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
// generated using:
|
||||||
|
// curl -s https://swift.org/ | iconv -c -f utf-8 -t ascii | pbcopy
|
||||||
|
let htmlASCIIOnly: String = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Swift.org - Welcome to Swift.org</title>
|
||||||
|
<meta name="author" content="Apple Inc." />
|
||||||
|
<meta name="viewport" content="width=device-width initial-scale=1" />
|
||||||
|
<link rel="license" href="/LICENSE.txt" />
|
||||||
|
<link rel="stylesheet" media="all" href="/assets/stylesheets/application.css" />
|
||||||
|
<link rel="shortcut icon" sizes="16x16 24x24 32x32 48x48 64x64" type="image/vnd.microsoft.icon" href="/favicon.ico" />
|
||||||
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon-76x76.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-114x114.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="120x120" href="/apple-touch-icon-120x120.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-144x144.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="/apple-touch-icon-152x152.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png" />
|
||||||
|
<link rel="mask-icon" href="/assets/images/icon-swift.svg" color="#F05339" />
|
||||||
|
|
||||||
|
<link rel="alternate" type="application/atom+xml" title="Swift.org (Atom Feed)" href="/atom.xml" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="canonical" href="https://swift.org/" />
|
||||||
|
|
||||||
|
|
||||||
|
<meta name="twitter:card" content="summary" />
|
||||||
|
<meta name="twitter:site" content="@SwiftLang" />
|
||||||
|
|
||||||
|
<meta name="twitter:title" content="Swift.org" />
|
||||||
|
<meta name="twitter:description" content="Swift is a general-purpose programming language built using a modern approach to safety, performance, and software design patterns." />
|
||||||
|
|
||||||
|
|
||||||
|
<meta property="og:site_name" content="Swift.org" />
|
||||||
|
<meta property="og:image" content="https://swift.org/touch-icon-ipad-180x180.png" />
|
||||||
|
|
||||||
|
<meta property="og:title" content="Swift.org" />
|
||||||
|
<meta property="og:url" content="https://swift.org" />
|
||||||
|
<meta property="og:description" content="Swift is a general-purpose programming language built using a modern approach to safety, performance, and software design patterns." />
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<nav role="navigation">
|
||||||
|
<header role="banner">
|
||||||
|
<h1 id="logo">
|
||||||
|
<a href="/" title="Swift.org" role="img" aria-label="Swift.org">
|
||||||
|
Swift.org
|
||||||
|
</a>
|
||||||
|
</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div id="menu-toggle" class="menu-toggle open"></div>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/about/">About Swift</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/blog/">Blog</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/download/">Download</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/getting-started/">Getting Started</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/documentation/">Documentation</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/migration-guide-swift4/">Migrating to Swift 4</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/source-code/">Source Code</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/community/">Community</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/contributing/">Contributing</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/continuous-integration/">Continuous Integration</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/source-compatibility/">Source Compatibility</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Focus Areas</h2>
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/abi-stability/">ABI Stability</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/server-apis/">Server APIs <small><small><small><i>(Work Group)</i></small></small></small></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Projects</h2>
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/compiler-stdlib/">Compiler and Standard Library</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/package-manager/">Package Manager</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/core-libraries/">Core Libraries</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/lldb/">REPL, Debugger & Playgrounds</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
|
<main role="main">
|
||||||
|
<h1 id="welcome-to-swiftorg">Welcome to Swift.org</h1>
|
||||||
|
|
||||||
|
<p>Welcome to the Swift community. Together we are working to build a programming language to empower everyone to turn their ideas into apps on any platform.</p>
|
||||||
|
|
||||||
|
<p>Announced in 2014, the Swift programming language has quickly become one of the fastest growing languages in history. Swift makes it easy to write software that is incredibly fast and safe by design. Our goals for Swift are ambitious: we want to make programming simple things easy, and difficult things possible.</p>
|
||||||
|
|
||||||
|
<p>For students, learning Swift has been a great introduction to modern programming concepts and best practices. And because it is open, their Swift skills will be able to be applied to an even broader range of platforms, from mobile devices to the desktop to the cloud.</p>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer role="contentinfo">
|
||||||
|
<aside>
|
||||||
|
<a href="/atom.xml" title="Subscribe to Site Updates"><i class="feed"></i></a>
|
||||||
|
<a href="https://twitter.com/swiftlang" rel="nofollow" title="Follow @SwiftLang on Twitter"><i class="twitter"></i></a>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<p class="copyright">Copyright 2018 Apple Inc. All rights reserved.</p>
|
||||||
|
<p class="trademark">Swift and the Swift logo are trademarks of Apple Inc.</p>
|
||||||
|
<p class="privacy">
|
||||||
|
<a href="//www.apple.com/privacy/privacy-policy/">Privacy Policy</a>
|
||||||
|
<a href="//www.apple.com/legal/privacy/en-ww/cookies/">Cookies</a>
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var s_account="dappswiftorg";
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript" src="/assets/javascripts/vendor/s_code_h.js"></script>
|
||||||
|
<script src="/assets/javascripts/application.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
s.pageName=AC.Tracking.pageName();
|
||||||
|
var s_code=s.t();if(s_code)document.write(s_code);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
// generated using:
|
||||||
|
// curl -s https://swift.org/ | pbcopy
|
||||||
|
let htmlMostlyASCII: String = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Swift.org - Welcome to Swift.org</title>
|
||||||
|
<meta name="author" content="Apple Inc." />
|
||||||
|
<meta name="viewport" content="width=device-width initial-scale=1" />
|
||||||
|
<link rel="license" href="/LICENSE.txt" />
|
||||||
|
<link rel="stylesheet" media="all" href="/assets/stylesheets/application.css" />
|
||||||
|
<link rel="shortcut icon" sizes="16x16 24x24 32x32 48x48 64x64" type="image/vnd.microsoft.icon" href="/favicon.ico" />
|
||||||
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon-76x76.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-114x114.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="120x120" href="/apple-touch-icon-120x120.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-144x144.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="/apple-touch-icon-152x152.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png" />
|
||||||
|
<link rel="mask-icon" href="/assets/images/icon-swift.svg" color="#F05339" />
|
||||||
|
|
||||||
|
<link rel="alternate" type="application/atom+xml" title="Swift.org (Atom Feed)" href="/atom.xml" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="canonical" href="https://swift.org/" />
|
||||||
|
|
||||||
|
|
||||||
|
<meta name="twitter:card" content="summary" />
|
||||||
|
<meta name="twitter:site" content="@SwiftLang" />
|
||||||
|
|
||||||
|
<meta name="twitter:title" content="Swift.org" />
|
||||||
|
<meta name="twitter:description" content="Swift is a general-purpose programming language built using a modern approach to safety, performance, and software design patterns." />
|
||||||
|
|
||||||
|
|
||||||
|
<meta property="og:site_name" content="Swift.org" />
|
||||||
|
<meta property="og:image" content="https://swift.org/touch-icon-ipad-180x180.png" />
|
||||||
|
|
||||||
|
<meta property="og:title" content="Swift.org" />
|
||||||
|
<meta property="og:url" content="https://swift.org" />
|
||||||
|
<meta property="og:description" content="Swift is a general-purpose programming language built using a modern approach to safety, performance, and software design patterns." />
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<nav role="navigation">
|
||||||
|
<header role="banner">
|
||||||
|
<h1 id="logo">
|
||||||
|
<a href="/" title="Swift.org" role="img" aria-label="Swift.org">
|
||||||
|
Swift.org
|
||||||
|
</a>
|
||||||
|
</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div id="menu-toggle" class="menu-toggle open"></div>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/about/">About Swift</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/blog/">Blog</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/download/">Download</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/getting-started/">Getting Started</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/documentation/">Documentation</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/migration-guide-swift4/">Migrating to Swift 4</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/source-code/">Source Code</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/community/">Community</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/contributing/">Contributing</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/continuous-integration/">Continuous Integration</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/source-compatibility/">Source Compatibility</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Focus Areas</h2>
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/abi-stability/">ABI Stability</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/server-apis/">Server APIs <small><small><small><i>(Work Group)</i></small></small></small></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Projects</h2>
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/compiler-stdlib/">Compiler and Standard Library</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/package-manager/">Package Manager</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/core-libraries/">Core Libraries</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/lldb/">REPL, Debugger & Playgrounds</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
|
<main role="main">
|
||||||
|
<h1 id="welcome-to-swiftorg">Welcome to Swift.org</h1>
|
||||||
|
|
||||||
|
<p>Welcome to the Swift community. Together we are working to build a programming language to empower everyone to turn their ideas into apps on any platform.</p>
|
||||||
|
|
||||||
|
<p>Announced in 2014, the Swift programming language has quickly become one of the fastest growing languages in history. Swift makes it easy to write software that is incredibly fast and safe by design. Our goals for Swift are ambitious: we want to make programming simple things easy, and difficult things possible.</p>
|
||||||
|
|
||||||
|
<p>For students, learning Swift has been a great introduction to modern programming concepts and best practices. And because it is open, their Swift skills will be able to be applied to an even broader range of platforms, from mobile devices to the desktop to the cloud.</p>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer role="contentinfo">
|
||||||
|
<aside>
|
||||||
|
<a href="/atom.xml" title="Subscribe to Site Updates"><i class="feed"></i></a>
|
||||||
|
<a href="https://twitter.com/swiftlang" rel="nofollow" title="Follow @SwiftLang on Twitter"><i class="twitter"></i></a>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<p class="copyright">Copyright © 2018 Apple Inc. All rights reserved.</p>
|
||||||
|
<p class="trademark">Swift and the Swift logo are trademarks of Apple Inc.</p>
|
||||||
|
<p class="privacy">
|
||||||
|
<a href="//www.apple.com/privacy/privacy-policy/">Privacy Policy</a>
|
||||||
|
<a href="//www.apple.com/legal/privacy/en-ww/cookies/">Cookies</a>
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var s_account="dappswiftorg";
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript" src="/assets/javascripts/vendor/s_code_h.js"></script>
|
||||||
|
<script src="/assets/javascripts/application.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
s.pageName=AC.Tracking.pageName();
|
||||||
|
var s_code=s.t();if(s_code)document.write(s_code);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
// generated using:
|
||||||
|
// curl -s https://swift.org/ | iconv -c -f utf-8 -t ascii | pbcopy
|
||||||
|
let htmlASCIIOnlyStaticString: StaticString = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Swift.org - Welcome to Swift.org</title>
|
||||||
|
<meta name="author" content="Apple Inc." />
|
||||||
|
<meta name="viewport" content="width=device-width initial-scale=1" />
|
||||||
|
<link rel="license" href="/LICENSE.txt" />
|
||||||
|
<link rel="stylesheet" media="all" href="/assets/stylesheets/application.css" />
|
||||||
|
<link rel="shortcut icon" sizes="16x16 24x24 32x32 48x48 64x64" type="image/vnd.microsoft.icon" href="/favicon.ico" />
|
||||||
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon-76x76.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-114x114.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="120x120" href="/apple-touch-icon-120x120.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-144x144.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="/apple-touch-icon-152x152.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png" />
|
||||||
|
<link rel="mask-icon" href="/assets/images/icon-swift.svg" color="#F05339" />
|
||||||
|
|
||||||
|
<link rel="alternate" type="application/atom+xml" title="Swift.org (Atom Feed)" href="/atom.xml" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="canonical" href="https://swift.org/" />
|
||||||
|
|
||||||
|
|
||||||
|
<meta name="twitter:card" content="summary" />
|
||||||
|
<meta name="twitter:site" content="@SwiftLang" />
|
||||||
|
|
||||||
|
<meta name="twitter:title" content="Swift.org" />
|
||||||
|
<meta name="twitter:description" content="Swift is a general-purpose programming language built using a modern approach to safety, performance, and software design patterns." />
|
||||||
|
|
||||||
|
|
||||||
|
<meta property="og:site_name" content="Swift.org" />
|
||||||
|
<meta property="og:image" content="https://swift.org/touch-icon-ipad-180x180.png" />
|
||||||
|
|
||||||
|
<meta property="og:title" content="Swift.org" />
|
||||||
|
<meta property="og:url" content="https://swift.org" />
|
||||||
|
<meta property="og:description" content="Swift is a general-purpose programming language built using a modern approach to safety, performance, and software design patterns." />
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<nav role="navigation">
|
||||||
|
<header role="banner">
|
||||||
|
<h1 id="logo">
|
||||||
|
<a href="/" title="Swift.org" role="img" aria-label="Swift.org">
|
||||||
|
Swift.org
|
||||||
|
</a>
|
||||||
|
</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div id="menu-toggle" class="menu-toggle open"></div>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/about/">About Swift</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/blog/">Blog</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/download/">Download</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/getting-started/">Getting Started</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/documentation/">Documentation</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/migration-guide-swift4/">Migrating to Swift 4</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/source-code/">Source Code</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/community/">Community</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/contributing/">Contributing</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/continuous-integration/">Continuous Integration</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/source-compatibility/">Source Compatibility</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Focus Areas</h2>
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/abi-stability/">ABI Stability</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/server-apis/">Server APIs <small><small><small><i>(Work Group)</i></small></small></small></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Projects</h2>
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/compiler-stdlib/">Compiler and Standard Library</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/package-manager/">Package Manager</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/core-libraries/">Core Libraries</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/lldb/">REPL, Debugger & Playgrounds</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
|
<main role="main">
|
||||||
|
<h1 id="welcome-to-swiftorg">Welcome to Swift.org</h1>
|
||||||
|
|
||||||
|
<p>Welcome to the Swift community. Together we are working to build a programming language to empower everyone to turn their ideas into apps on any platform.</p>
|
||||||
|
|
||||||
|
<p>Announced in 2014, the Swift programming language has quickly become one of the fastest growing languages in history. Swift makes it easy to write software that is incredibly fast and safe by design. Our goals for Swift are ambitious: we want to make programming simple things easy, and difficult things possible.</p>
|
||||||
|
|
||||||
|
<p>For students, learning Swift has been a great introduction to modern programming concepts and best practices. And because it is open, their Swift skills will be able to be applied to an even broader range of platforms, from mobile devices to the desktop to the cloud.</p>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer role="contentinfo">
|
||||||
|
<aside>
|
||||||
|
<a href="/atom.xml" title="Subscribe to Site Updates"><i class="feed"></i></a>
|
||||||
|
<a href="https://twitter.com/swiftlang" rel="nofollow" title="Follow @SwiftLang on Twitter"><i class="twitter"></i></a>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<p class="copyright">Copyright 2018 Apple Inc. All rights reserved.</p>
|
||||||
|
<p class="trademark">Swift and the Swift logo are trademarks of Apple Inc.</p>
|
||||||
|
<p class="privacy">
|
||||||
|
<a href="//www.apple.com/privacy/privacy-policy/">Privacy Policy</a>
|
||||||
|
<a href="//www.apple.com/legal/privacy/en-ww/cookies/">Cookies</a>
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var s_account="dappswiftorg";
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript" src="/assets/javascripts/vendor/s_code_h.js"></script>
|
||||||
|
<script src="/assets/javascripts/application.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
s.pageName=AC.Tracking.pageName();
|
||||||
|
var s_code=s.t();if(s_code)document.write(s_code);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
// generated using:
|
||||||
|
// curl -s https://swift.org/ | pbcopy
|
||||||
|
let htmlMostlyASCIIStaticString: StaticString = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Swift.org - Welcome to Swift.org</title>
|
||||||
|
<meta name="author" content="Apple Inc." />
|
||||||
|
<meta name="viewport" content="width=device-width initial-scale=1" />
|
||||||
|
<link rel="license" href="/LICENSE.txt" />
|
||||||
|
<link rel="stylesheet" media="all" href="/assets/stylesheets/application.css" />
|
||||||
|
<link rel="shortcut icon" sizes="16x16 24x24 32x32 48x48 64x64" type="image/vnd.microsoft.icon" href="/favicon.ico" />
|
||||||
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon-76x76.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-114x114.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="120x120" href="/apple-touch-icon-120x120.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-144x144.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="/apple-touch-icon-152x152.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png" />
|
||||||
|
<link rel="mask-icon" href="/assets/images/icon-swift.svg" color="#F05339" />
|
||||||
|
|
||||||
|
<link rel="alternate" type="application/atom+xml" title="Swift.org (Atom Feed)" href="/atom.xml" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="canonical" href="https://swift.org/" />
|
||||||
|
|
||||||
|
|
||||||
|
<meta name="twitter:card" content="summary" />
|
||||||
|
<meta name="twitter:site" content="@SwiftLang" />
|
||||||
|
|
||||||
|
<meta name="twitter:title" content="Swift.org" />
|
||||||
|
<meta name="twitter:description" content="Swift is a general-purpose programming language built using a modern approach to safety, performance, and software design patterns." />
|
||||||
|
|
||||||
|
|
||||||
|
<meta property="og:site_name" content="Swift.org" />
|
||||||
|
<meta property="og:image" content="https://swift.org/touch-icon-ipad-180x180.png" />
|
||||||
|
|
||||||
|
<meta property="og:title" content="Swift.org" />
|
||||||
|
<meta property="og:url" content="https://swift.org" />
|
||||||
|
<meta property="og:description" content="Swift is a general-purpose programming language built using a modern approach to safety, performance, and software design patterns." />
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<nav role="navigation">
|
||||||
|
<header role="banner">
|
||||||
|
<h1 id="logo">
|
||||||
|
<a href="/" title="Swift.org" role="img" aria-label="Swift.org">
|
||||||
|
Swift.org
|
||||||
|
</a>
|
||||||
|
</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div id="menu-toggle" class="menu-toggle open"></div>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/about/">About Swift</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/blog/">Blog</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/download/">Download</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/getting-started/">Getting Started</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/documentation/">Documentation</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/migration-guide-swift4/">Migrating to Swift 4</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/source-code/">Source Code</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/community/">Community</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/contributing/">Contributing</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/continuous-integration/">Continuous Integration</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/source-compatibility/">Source Compatibility</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Focus Areas</h2>
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/abi-stability/">ABI Stability</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/server-apis/">Server APIs <small><small><small><i>(Work Group)</i></small></small></small></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Projects</h2>
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/compiler-stdlib/">Compiler and Standard Library</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/package-manager/">Package Manager</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/core-libraries/">Core Libraries</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<a href="/lldb/">REPL, Debugger & Playgrounds</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
|
<main role="main">
|
||||||
|
<h1 id="welcome-to-swiftorg">Welcome to Swift.org</h1>
|
||||||
|
|
||||||
|
<p>Welcome to the Swift community. Together we are working to build a programming language to empower everyone to turn their ideas into apps on any platform.</p>
|
||||||
|
|
||||||
|
<p>Announced in 2014, the Swift programming language has quickly become one of the fastest growing languages in history. Swift makes it easy to write software that is incredibly fast and safe by design. Our goals for Swift are ambitious: we want to make programming simple things easy, and difficult things possible.</p>
|
||||||
|
|
||||||
|
<p>For students, learning Swift has been a great introduction to modern programming concepts and best practices. And because it is open, their Swift skills will be able to be applied to an even broader range of platforms, from mobile devices to the desktop to the cloud.</p>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer role="contentinfo">
|
||||||
|
<aside>
|
||||||
|
<a href="/atom.xml" title="Subscribe to Site Updates"><i class="feed"></i></a>
|
||||||
|
<a href="https://twitter.com/swiftlang" rel="nofollow" title="Follow @SwiftLang on Twitter"><i class="twitter"></i></a>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<p class="copyright">Copyright © 2018 Apple Inc. All rights reserved.</p>
|
||||||
|
<p class="trademark">Swift and the Swift logo are trademarks of Apple Inc.</p>
|
||||||
|
<p class="privacy">
|
||||||
|
<a href="//www.apple.com/privacy/privacy-policy/">Privacy Policy</a>
|
||||||
|
<a href="//www.apple.com/legal/privacy/en-ww/cookies/">Cookies</a>
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var s_account="dappswiftorg";
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript" src="/assets/javascripts/vendor/s_code_h.js"></script>
|
||||||
|
<script src="/assets/javascripts/application.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
s.pageName=AC.Tracking.pageName();
|
||||||
|
var s_code=s.t();if(s_code)document.write(s_code);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
Loading…
Reference in New Issue