I don't really see a reason why this should be the case, so I hope it will improve over time.
But using mandatory reference counting does limit the sort of tasks for which Swift will ultimately be suitable. I wouldn't put it in the same category as C++ and Rust for that reason.
- Swift performance issues involving working with strings, some of which have been ameliorated by later releases;
- 'Hidden' bridging between NSString and String, which can be expensive and might have something to do with some of String's APIs actually being Foundation APIs in disguise.
There is much to be done with regards to Swift performance, though. If you have any sample benchmarks I would be interested in writing/running them myself, just for personal edification.
In terms of 'close to the metal', I freely admit this is a ill-defined term I am using for my own benefit. Swift code doesn't execute on something akin to the JVM or CLR; it still requires a heavier runtime than (e.g.) Rust. It is AOT-compiled to machine code. (But then again, this is possible to some degree for Java as far as I can tell, although it is certainly not the most popular way to run Java code.)
I agree that mandatory RC for classes and boxed types makes Swift impractical for a certain definition of 'systems software' typically written in C++ and occasionally Rust. For a different definition, one that encompasses the garbage-collected Go, Swift is theoretically suitable. The language designer has mentioned that he would personally like to see Swift gain some form of borrowing/lifetimes, although even if that did happen Swift would not get it for years to come.
The simplest benchmark I used is a function that does the equivalent of
str.split(/\s*,\s*/)
So it splits a string around a separator and removes any whitespace around the items. For a million strings the numbers I get are as follows:Swift: 3.5 seconds
Java: 0.31 seconds (after warmup)
So that makes Java more than 10 times as fast as Swift.
Here's the source code. I would be more than happy if you could tell me that I'm doing something horribly wrong (which I frequently do):
import Foundation
let N = 1000000
func generateTestData() -> [String] {
var a = [String]()
for _ in 0..<N {
a.append(",,abc, 123 ,x, , more more more,\u{A0}and yet more, ")
}
return a
}
func splitAndTrim(s: String, sep: UInt16) -> [String] {
var result = [String]()
result.reserveCapacity(10)
let space = NSCharacterSet.whitespaceAndNewlineCharacterSet()
let cs = s.utf16
let eos = cs.endIndex
var begin = eos
var end = eos
for i in cs.startIndex..<eos {
let c = cs[i]
if c == sep {
result.append(begin == eos ? "" : String(cs[begin..<end.successor()]))
begin = eos
end = eos
} else if !space.characterIsMember(c) {
if begin == eos {
begin = i
}
end = i
}
}
result.append(begin == eos ? "" : String(cs[begin..<end.successor()]))
return result
}
func doSplits(data: [String]) -> Int {
var count = 0
let sep = ",".utf16.first!
for s in data {
let parts = splitAndTrim(s, sep: sep)
count += parts.count
}
return count
}
let data = generateTestData()
let start = NSDate()
let sum = doSplits(data)
print("elapsed: \(NSDate().timeIntervalSinceDate(start))")
print("sum: \(sum)")
And here's the Java code: import java.util.List;
import java.util.ArrayList;
public class ParseTest {
static int N = 1000000;
public static void main(String[] args) {
List<String> data = generateTestData();
for (int i = 0; i < 3; ++i) {
long start = System.currentTimeMillis();
int sum = doSplits(data);
System.out.println("elapsed: " +
((System.currentTimeMillis() - start) / 1000.0 + " seconds"));
System.out.println("sum: " + sum);
}
}
static int doSplits(List<String> a) {
int count = 0;
for (String s : a) {
List<String> parts = splitAndTrim(s, ',');
count += parts.size();
}
return count;
}
static List<String> splitAndTrim(String s, char sep) {
List<String> result = new ArrayList<>(10);
int eos = s.length();
int begin = eos;
int end = eos;
for (int i = 0; i != eos; ++i) {
char c = s.charAt(i);
if (c == sep) {
result.add(begin == eos ? "" : s.substring(begin, end + 1));
begin = eos;
end = eos;
} else if (!Character.isSpaceChar(c)) {
if (begin == eos) {
begin = i;
}
end = i;
}
}
result.add(begin == eos ? "" : s.substring(begin, end + 1));
return result;
}
static List<String> generateTestData() {
List<String> a = new ArrayList<>();
for (int i = 0; i < N; i++) {
a.add(",,abc, 123 ,x, , more more more,\u00A0and yet more, ");
}
return a;
}
}There is nothing special in Go about it.