SwiftずRustを比范する



この蚘事を曞いた理由は、Swift蚀語の゜ヌスコヌドの公開でした-それをよりよく知るこずが私にずっお興味深いものになりたした。 Rustず呌ばれる別の若いプログラミング蚀語ずの構文の類䌌性はすぐに私の目を匕きたした。たた、類䌌の構文構造に加えお、これらの蚀語の類䌌の応甚分野も芋られたした。 どちらの蚀語も、ロヌカル型掚論による匷力な静的型付けを備えおおり、䞡方ずもマシンコヌドに盎接コンパむルされたす。 䞡方の蚀語は、関数型プログラミングの䞖界から倚くのトリックを吞収したした。 SwiftずRustの䞡方に、Cで蚘述されたコヌドを実行するツヌルがありたす。これにより、膚倧な数のラむブラリのラッパヌを簡単に䜜成できたす。 䞡方の蚀語は、C、C ++、ObjectiveCなどの既存のシステム蚀語の代替ず芋なされたす。 それでは、それらに共通するものず、䜕が違うのでしょうか





構文の基本





すぐに2぀のプログラミング蚀語の基本を読者に䌝えようずはしおいないず蚀わなければなりたせん。䜕か䞍明な点がある堎合は、 RustbookたたはSwiftbookに問い合わせるこずをお勧めしたす。



最初に、最も単玔なプログラムを比范しおみたしょう。



さび

fn main() { let i:i32 = 16; let mut f = 8.0; f *= 2.0; println!("Hello Rust! within vars {} and {}", i, f); }
      
      







スむフト

 let i:Int = 10 var f = 15.0 f /= 2 print("Hello Swift within vars \(i) \(f)")
      
      







letはあちこちで同じこずを意味し、SwiftのvarキヌワヌドはRustのlet mutの組み合わせに䌌おいるこずに気付くかもしれたせん。 ただし、違いがありたす。Rustでは、すべおの数倀型でサむズが明確に瀺されたすが、SwiftはCの䌝統に埓いたす。 この質問では、Rustはより䜎いレベルのようです。

Rustの文字列補間は、printlnの腞で呌び出されるformat、マクロを䜿甚しお行われたす。Swiftでは、これは蚀語自䜓の機胜ですが、同時にドキュメントで曞匏蚭定オプションを蚭定する方法が芋぀かりたせんでした。

「;」の解釈の違いは興味深いです。Rustの堎合、これは匏の終わりの兆候です。これにより、゚レガントなこずを行うこずができたす。たずえば、関数内の最埌の匏が自動的に戻り倀になりたす。 Swiftは、パスカルの䌝統に埓いたす。セミコロンは、同じ行で挔算子を単玔に分離したす。



次に、2぀の匕数の関数を取り、それを1぀の関数に倉換する関数を䜜成しおみたしょう。



さび

 fn apply<F: 'static>(f: F, v1: i32) -> Box<Fn(i32) -> ()> where F: Fn(i32, i32) -> () { Box::new(move |v2| f(v1, v2)) } fn print_sum(a: i32, b: i32) -> () { println!("Rust: sum a and b is {}", a + b); } fn main() { let a = 2; let b = 5; print_sum(a, b); let f = print_sum; let f2 = apply(f, b); f2(a); }
      
      







すばやい

 func apply(f: (_: Int, _: Int) -> (), _ v1: Int) -> (_: Int) -> () { return {(c: Int) -> () in return f(v1, c) } } func print_sum(a: Int, second b: Int) -> () { print("Swift: sum a and b is \(a+b)") } let a = 2; let b = 5; print_sum(a, second:b) let f2 = apply(print_sum, b) f2(a)
      
      







ここで違いはすでに顕著であるため、Swiftの名前付きパラメヌタヌの倖郚名やRustの䞀般化などの玔粋に構文䞊の違いを陀倖し、さらに重芁な違いを怜蚎したす。 Swiftのアプロヌチは明らかに高レベルです。コンパむラヌは結果の関数を保存する方法を決定したすが、移動したクロヌゞャヌは無次元型であるため、Rustをボックスに明瀺的にパックする必芁がありたした。

誰のラムダ関数が速いかを確認したしょう



ベンチマヌクコヌド
さび

 fn apply<F: 'static>(f: F, v1: i32) -> Box<Fn(i32) -> i32> where F: Fn(i32, i32) -> i32 { Box::new(move |v2| f(v1, v2)) } fn make_sum(a: i32, b: i32) -> i32 { a + b } fn main() { let a = 2; let b = 5; let c = make_sum(a, b); println!("Rust: c is {}", c); let f2 = apply(make_sum, b); let mut d = 0; for i in 0..1000000000 { d = f2(i); } println!("Rust: d is {}", d); }
      
      







すばやい

 func apply(f: (_: Int, _: Int) -> Int, _ v1: Int) -> (_: Int) -> Int { return {(c: Int) -> Int in return f(v1, c) } } func make_sum(a: Int, second b: Int) -> Int { return a + b } let a = 2; let b = 5; let c = make_sum(a, second:b) print("Swift: c is \(c)") let f2 = apply(make_sum, b) f2(a) var d = 0; for i in 0...1000000000 { d = f2(i); } print("Swift: d is \(d)");
      
      











最終結果Rustで4.0秒、Swiftで1.17。 より抜象的なコヌドの堎合、コンパむラヌは最適化の機䌚が倚くなりたすが、ruRust / generalチャネルでは、それほど矎しく芋えないかもしれないメ゜ッドでプロンプトが衚瀺されたしたが、オプティマむザヌはすべおを完党に䞎えるこずができたす。 最終的に、Rustはコンパむル時にルヌプ党䜓を芋぀けたした。これは非垞にクヌルです。

熟緎した手で、Rustは䞍思議なこずができたす。



クむックバヌゞョンコヌド
 struct Curry<'a> { f: &'a Fn(i32, i32) -> i32, v1: i32 } impl<'a> Curry<'a> { fn new<F: 'static>(f: &'a F, v1: i32) -> Curry<'a> where F: Fn(i32, i32) -> i32 { Curry { f: f, v1: v1 } } fn call(&'a self, v2: i32) -> i32 { (*self.f)(self.v1, v2) } } fn make_sum(a: i32, b: i32) -> i32 { a + b } fn main() { let a = 2; let b = 5; let c = make_sum(a, b); println!("Rust: c is {}", c); let borrow = &make_sum; let f2 = Curry::new(borrow, b); let mut d = 0; for i in 0..1000000000 { d = f2.call(i); } println!("Rust: d is {}", d); }
      
      









列挙ずパタヌンマッチング





䞡方の蚀語には、パタヌンマッチングの倧きな可胜性がありたす;䞡方の蚀語には、代数デヌタ型がありたす。 これらの機胜を䜿甚した簡単な䟋を䜜成しおみたしょう。数孊挔算を実装しおみおください。 これを行うには、転送を再垰的にする必芁がありたす。 ボヌナスずしお、プロトコル特性を䜿甚しお、操䜜の名前を画面に印刷しようずしたす。

さび

 enum MathOperation { Value(i32), Sum(Box<MathOperation>, Box<MathOperation>), Mul(Box<MathOperation>, Box<MathOperation>) } trait HasName { fn name(&self) -> &'static str; } impl HasName for MathOperation { fn name(&self) -> &'static str { match *self { MathOperation::Value(..) => "Value", MathOperation::Sum(..) => "Sum", MathOperation::Mul(..) => "Mul" } } } impl MathOperation { fn solve(&self) -> i32 { match *self { MathOperation::Value(i) => i, MathOperation::Sum(ref left, ref right) => left.solve() + right.solve(), MathOperation::Mul(ref left, ref right) => left.solve() * right.solve() } } } fn main() { let op = MathOperation::Sum(Box::new(MathOperation::Value(10)), Box::new(MathOperation::Mul(Box::new(MathOperation::Value(20)), Box::new(MathOperation::Value(2))))); ; println!("Rust: op is {} solved {}", op.name(), op.solve()); }
      
      







スむフト

 enum MathOperation { case Value(Int) indirect case Sum(MathOperation, MathOperation) indirect case Mul(MathOperation, MathOperation) func solve() -> Int { switch self { case .Value(let value): return value case .Sum(let left, let right): return left.solve() + right.solve() case .Mul(let left, let right): return left.solve() * right.solve() } } } protocol HasName { func name() -> String; } extension MathOperation : HasName { func name() -> String { switch self { case .Value(_): return "Value" case .Sum(_, _): return "Sum" case .Mul(_, _): return "Mul" } } } let op = MathOperation.Sum(MathOperation.Value(10), MathOperation.Mul(MathOperation.Value(20), MathOperation.Value(2))) print("Swift: op is \(op.name()) solved \(op.solve())");
      
      







Rustでは、再垰的な列挙を敎理するために、ボックスコンテナヌにパックする必芁がありたした。サむズは任意であり、コンパむル段階でこれに必芁なメモリ量がわからないためです。 Swiftは間接的な単語を䜿甚しお再垰的な列挙を指したすが、そのメモリモデルを考えるず、疑問が生じたすコンパむラはメモリを割り圓おる方法を理解できたせんか どうやらこのキヌワヌドは、その人にずっおより可胜性が高いず思われたす。

たた、原則ずしおimplずextensionがほが同じ䜜業を実行し、タむプがプロトコルに䌌おいるこずもわかりたす。 しかし、Swiftでは、アプロヌチはより劥協的です。メ゜ッドを拡匵機胜ずしお远加する必芁はなく、リスト宣蚀で盎接指定できたす。



次に、パタヌンマッチングの䟋をいく぀か芋おみたしょう。



Rust 䟋によるRustの䟋



 match some_value { Ok(value) => println!("got a value: {}", value), Err(_) => println!("an error occurred"), } enum OptionalTuple { Value(i32, i32, i32), Missing, } let x = OptionalTuple::Value(5, -2, 3); match x { OptionalTuple::Value(..) => println!("Got a tuple!"), OptionalTuple::Missing => println!("No such luck."), } let x = 1; match x { 1 ... 5 => println!("one through five"), _ => println!("anything"), } let x = 1; match x { e @ 1 ... 5 => println!("got a range element {}", e), _ => println!("anything"), }
      
      





Swift Swiftbookから取埗したサンプルコヌド

 let count = 3000000000000 let countedThings = "stars in the Milky Way" var naturalCount: String switch count { case 0: naturalCount = "no" case 1...3: naturalCount = "a few" case 4...9: naturalCount = "several" case 10...99: naturalCount = "tens of" case 100...999: naturalCount = "hundreds of" case 1000...999999: naturalCount = "thousands of" default: naturalCount = "millions and millions of" } print("There are \(naturalCount) \(countedThings).") //  "There are millions and millions of stars in the Milky Way." let somePoint = (1, 1) switch somePoint { case (0, 0): print("(0, 0) is at the origin") case (_, 0): print("(\(somePoint.0), 0) is on the x-axis") case (0, _): print("(0, \(somePoint.1)) is on the y-axis") case (-2...2, -2...2): print("(\(somePoint.0), \(somePoint.1)) is inside the box") default: print("(\(somePoint.0), \(somePoint.1)) is outside of the box") }//  "(1, 1) is inside the box let anotherPoint = (2, 0) switch anotherPoint { case (let x, 0): print("on the x-axis with an x value of \(x)") case (0, let y): print("on the y-axis with ay value of \(y)") case let (x, y): print("somewhere else at (\(x), \(y))") } //  "on the x-axis with an x value of 2
      
      







ここでは、䞀般的に、すべおが非垞に䌌おいたす。 比范にディップがないはずです。倀の範囲を指定できたす。倀をバむンドでき、比范でタプルを䜿甚できたす。 Rustでは、䞀臎時に構造党䜓を䜿甚するこずもできたす。 それぞれifずwhereで远加の条件を指定できたす。 しかし同時に、Swiftには远加のフロヌ制埡挔算子がありたす。 それらを䜿甚するのが良いアむデアかどうかはわかりたせんが。



より珟実的な䟋





Bresenhamラスタヌ化アルゎリズムを蚘述しおみたしょう。 通垞の圢匏ではなく、n次元ベクトルの圢匏で実数を䜿甚したせん。 これはすべお、コンピュヌタグラフィックスの短期コヌスを勉匷するずきに圹立ちたす。



たず、3次元ベクトルを䜜成しお、むンデックスの取埗ず比范の操䜜を決定しおみおください。

さび

 #[derive(Copy, Clone, PartialEq)] struct Vec3 { x: i32, y: i32, z: i32 } impl Index<usize> for Vec3 { type Output = i32; fn index<'a>(&'a self, i: usize) -> &'a Self::Output { match i { 0 => &self.x, 1 => &self.y, 2 => &self.z, _ => panic!("Wrong index"), } } } impl IndexMut<usize> for Vec3 { fn index_mut<'a>(&'a mut self, i: usize) -> &'a mut Self::Output { match i { 0 => &mut self.x, 1 => &mut self.y, 2 => &mut self.z, _ => panic!("Wrong index"), } } }
      
      





すばやい

 struct Vector3 { var x: Int; var y: Int; var z: Int; subscript(i: Int) -> Int { get { precondition(i >= 0 && i < 3, "Index out-of-bounds") switch i { case 0: return self.x case 1: return self.y case 2: return self.z default: return 0 } } set { precondition(i >= 0 && i < 3, "Index out-of-bounds") switch i { case 0: self.x = newValue case 1: self.y = newValue case 2: self.z = newValue default: break } } } } func == (left: Vector3, right: Vector3) -> Bool { return (left.x == right.x) && (left.y == right.y) && (left.z == right.z) } func != (left: Vector3, right: Vector3) -> Bool { return !(left == right) }
      
      





実際、Rustでは、挔算子は特性を介しお実装されたす。構造䜓がむンデックス特性を実装する堎合、[]挔算子を䜿甚できたす。Swiftでは、むンデックスを取埗する挔算子はキヌワヌド添え字を䜿甚しお蚘述され、特別な前提条件を远加できたす。 Rustは通垞のアサヌションに䟝存しおいたす。 他の挔算子の堎合、Swiftを䜿甚するず、既存の挔算子を再定矩し、さらに独自の優先床、結合性を瀺す定矩を行うこずができたす。 これはすべお、数孊ラむブラリを䜜成するずきに非垞に圹立ち、コヌドを元の数孊匏により類䌌させたす。 ただし、Rustは、deriv属性を介しお䞀郚のタむプの実装を自動的に䜜成できたす。これにより、倚くのささいなケヌスでコヌドを自分で蚘述するこずを回避できたす。



次に、むンデックス挔算子を䜿甚しお任意のピクセルの色にアクセスできるように、画像を保存するための䟿利なバッファヌを䜜成しおみたしょう。 同時に、ピクセルマップが異なる色深床に察応できるずいう芁件を提唱するため、䞀般化するこずにしたす。



錆び

 struct GenericPixmap<T> { w: usize, h: usize, data: Vec<T> } impl<T> GenericPixmap<T> where T: Copy + Clone { fn new(w: usize, h: usize, fill_value: T) -> GenericPixmap<T> { GenericPixmap { w: w, h: h, data: vec![fill_value; w*h] } } } impl<T> Index<usize> for GenericPixmap<T> where T: Copy + Clone { type Output = [T]; fn index<'a>(&'a self, i: usize) -> &'a Self::Output { let from = i*self.w; let to = from+self.w; &self.data[from..to] } } impl<T> IndexMut<usize> for GenericPixmap<T> where T: Copy + Clone { fn index_mut<'a>(&'a mut self, i: usize) -> &'a mut Self::Output { let from = i*self.w; let to = from+self.w; &mut self.data[from..to] } } type Pixmap = GenericPixmap<u32>;
      
      







すばやい

 struct GenericPixmap<T> { let w: Int let h: Int var data: [T] init(width: Int, height: Int, fillValue: T) { self.w = width self.h = height self.data = [T](count: w*h, repeatedValue: fillValue) } func indexIsValid(x: Int, _ y: Int) -> Bool { return x >= 0 && x < w && y >= 0 && y < h } subscript(x: Int, y: Int) -> T { get { precondition(indexIsValid(x, y), "Index out-of-bounds") return data[x * y + y] } set { precondition(indexIsValid(x,y), "Index out-of-bounds") data[x * y + y] = newValue } } } typealias Pixmap = GenericPixmap<UInt32>
      
      







Rustの䞀般化のルヌルはより厳密であり、テンプレヌトタむプが独自のコピヌをコピヌおよび䜜成できる必芁があるこずを明瀺的に瀺す必芁がありたす。 Swiftでは、構造䜓フィヌルドに察しお、その可倉性を明瀺的に蚭定できたす。 initキヌワヌドにも泚目できたす。 これはクラスたたは構造のコンストラクタヌであり、それらのいく぀かが存圚する可胜性があり、それらは互いに力を委任できたす。 結果ずしお、これはかなり耇雑で倚段階のプロセスに倉換されたすが、それでも各メンバヌが初期化されるこずを正確に保蚌したす。 Rustでは、静的関数newによっおオブゞェクトを䜜成するずいう甚語ごずの初期化ず合意がありたす。 プロセスが耇雑になるず予想される堎合は、工堎を䜿甚するこずをお勧めしたす。 静的関数に関しおは、この意味でのRustの構文はpythonの䌝統に埓いたすが、SwiftはC ++です。

Swiftのむンデックス挔算子は任意の型の匕数をいく぀でも取るこずができるので、配列の特定の芁玠をすぐに受け取る挔算子を曞くこずができたすが、Rustではスラむスを䜜成する必芁がありたす。



次に、プロセス自䜓の実装を考えずに描画できるCanvasトレむトを䜜成したしょう。

錆び

 trait Canvas { fn set_pixel(&mut self, x: usize, y:usize, color:u32); } impl Canvas for Pixmap { fn set_pixel(&mut self, x: usize, y:usize, color:u32) { self[x][y] = color; } }
      
      





すばやい

 protocol Canvas { func setPixel(x: Int, _ y: Int, color: UInt32); } class MyCanvas : Canvas { var pixmap: Pixmap init(width: Int, height: Int, fillValue: UInt32) { self.pixmap = Pixmap(width:width, height:height, fillValue:fillValue) } func setPixel(x: Int, _ y: Int, color: UInt32) { pixmap[x, y] = color } }
      
      







残念ながら、GenericPixmap型の拡匵機胜を実装する方法をすばやく理解できなかったため、SwiftのRustずは異なり、プロトコルなどから継承できるCanvasプロトコルを実装する新しいMyCanvasクラスを䜜成するこずにしたした。



次に、最も興味深いのは、ブレれンハムアルゎリズムの実装です。 ポむントx1、y1、z1からポむントx2、y2、z2に線を匕きたいので、方向に| x2-x1 |、| y2-y1 |、| z2-z1 |ステップを螏む必芁がありたす。 、匏の笊号x2-x1、y2-y1、z2-z1に䟝存したす。

したがっお、方向sx、sy、szでrx、ry、rzステップを実行する必芁がありたす。これにより、最倧数のステップを䜜成する必芁がある軞が芋぀かりたす。 各ステップでの倉䜍はrx / r [max、ry / r [max]、rz / r [max]に等しく、ステップは総倉䜍dが1より倧きくなった堎合にのみ発生し、軞に沿っおステップが䜜成されたす。総倉䜍控陀ナニット。 それは

 d[i] += r[i] / r[max] if d[i] >= 1 { r[i] -= s[i]; d[i] -= 1; }
      
      





条件にrmaxを掛けるず、陀算操䜜を行わなくおもよいこずがわかりたす。

 d[i] += r[i] if d[i] >= r[max] { r[i] -= s[i]; d[i] -= r[max]; }
      
      





その結果、このアルゎリズムの倉圢は、任意の数の座暙で機胜し、浮動小数点挔算を回避したす。これは、ほずんどの堎合、敎数による挔算よりも高䟡です。



゚ンドノヌドaずbの間に䞀連のポむントを䜜成するゞェネレヌタヌを䜜成しおみたしょう。 この堎合、ポむントaがシヌケンスに含たれたす。



錆び

 struct RasterState { step: Vec3, d: Vec3, major_axis: usize, } struct LineRasterizer { from: Vec3, to: Vec3, state: Option<RasterState> } impl LineRasterizer { fn new(from: Vec3, to: Vec3) -> LineRasterizer { LineRasterizer { from: from, to: to, state: None } } fn next_point(&mut self) -> Option<Vec3> { match self.state { None => { let mut state = RasterState { step: Vec3 { x: 0, y: 0, z: 0 }, d: Vec3 { x: 0, y: 0, z: 0 }, major_axis: 0 }; let mut max = 0; for i in 0..3 { let d = self.to[i] - self.from[i]; state.step[i] = if d > 0 { 1 } else { -1 }; let d = d.abs(); if d > max { max = d; state.major_axis = i as usize; }; } self.state = Some(state); Some(self.from) }, Some(ref mut state) => { if self.from == self.to { None } else { let from = self.from; let to = self.to; let calc_residual_steps = |axis| { (to[axis] - from[axis]).abs() }; self.from[state.major_axis] += state.step[state.major_axis]; let rs_base = calc_residual_steps(state.major_axis); for i in 0..3 { let rs = calc_residual_steps(i); if rs > 0 && i != state.major_axis { state.d[i] += rs; if state.d[i] >= rs_base { state.d[i] -= rs_base; self.from[i] += state.step[i]; } } } Some(self.from) } }, } } }
      
      





すばやい

 class LineRaster { class State { var step: Vector3 var d: Vector3 var majorAxis: Int init() { self.step = Vector3(x: 0, y: 0, z: 0) self.d = Vector3(x: 0, y: 0, z: 0) self.majorAxis = 0 } } var from: Vector3 let to: Vector3 var state: State? init(from: Vector3, to: Vector3) { self.from = from self.to = to } func next_point() -> Vector3? { if let state = self.state { if (self.from == self.to) { return nil } else { let calsResidualSteps = {axis in return abs(self.to[axis] - self.from[axis])} self.from[state.majorAxis] += state.step[state.majorAxis]; let rsBase = calsResidualSteps(state.majorAxis); for i in 0..<3 { let rs = calsResidualSteps(i); if rs > 0 && i != state.majorAxis { state.d[i] += rs; if state.d[i] >= rsBase { state.d[i] -= rsBase; self.from[i] += state.step[i]; } } } return self.from } } else { let state = State() var max = 0; for i in 0..<3 { let d = self.to[i] - self.from[i]; state.step[i] = d > 0 ? 1 : -1; let da = abs(d); if da > max { max = da; state.majorAxis = i; }; } self.state = state return self.from } } }
      
      







ゞェネレヌタヌの状態をオプションの倀の圢匏にするこずにしたした。これにより、远加のフラグを必芁ずせずに、ゞェネレヌタヌから開始点を簡単か぀即座に返すこずができたす。 Rustでは、オプション倀は単にenum Optionで䜜成されたすが、Swiftではそれらは蚀語の䞀郚であるため、構文䞊のノむズがあたりなく、オプションのコヌルチェヌンを簡単に蚘述できたす。

Rustは、参照によっお列挙からStateを借甚しおいるこずを䌝えるために、高床な所有暩システムを䜿甚しおいたす。キヌワヌドrefを蚘述する必芁がありたす。 Swiftでは、状態はデフォルトで参照デヌタ型であり、蚀語では移動のセマンティクスがただ芳察されおいないため、䜕も心配するこずなく状態を取埗および展開できたす。



次のようなコヌドを蚘述したす。

 while let Some(point) = rasterizer.next_point() { ... }
      
      





私にはあたり゚レガントではないようですが、これははるかに論理的に芋えたす。

 for point in generator { ... }
      
      





幞いなこずに、forルヌプを䜿甚するために、ゞェネレヌタヌにいく぀かのタむプを実装するのは非垞に簡単です。

錆び

 impl Iterator for LineRasterizer { type Item = Vec3; fn next(&mut self) -> Option<Self::Item> { self.next_point() } }
      
      





すばやい

 extension LineRaster : GeneratorType { func next() -> Vector3? { return self.next_point() } } extension LineRaster : SequenceType { func generate() -> LineRaster { return self } }
      
      







さらに、Swiftの堎合、最倧2぀のプロトコルを実装する必芁がありたすが、Rustの堎合は1぀で十分ですが、根本的な違いはありたせん。



パフォヌマンスを少し枬定したしょう





コヌドの実行速床は、異なるプログラミング蚀語を比范する際の重芁な芁玠の1぀であり、パフォヌマンスを枬定せずに蚘事を曞くのは愚かなこずです。



単玔な比范オプション



錆び

 fn test_code(canvas: &mut Canvas) { let a = Vec3 { x: 0, y:0, z:0 }; let b = Vec3 { x: 50, y: 55, z: -20 }; let rasterizer = LineRasterizer::new(a, b); for point in rasterizer { let color = std::u32::MAX; canvas.set_pixel(point.x as usize, point.y as usize, color); } } for _ in 0..1000000 { test_code(&mut canvas) }
      
      





すばやい

 func testCode(canvas: Canvas) -> () { let a = Vector3(x: 0, y:0, z:0) let b = Vector3(x: 50, y:55, z:-20) let raster = LineRaster(from: a, to: b) for point in raster { let color = UInt32.max canvas.setPixel(point.x, point.y, color: color) } } ... var myCanvas: Canvas = canvas for _ in 0..<1000000 { testCode(myCanvas) }
      
      







Canvasぞのリンクをテストする関数に枡すだけで、時間を枬定したす。

Rustでは0.86、Swiftでは5.3でした。 Swiftは動的ディスパッチのレベルに留たっおいる䞀方で、Rustは䜕らかの圢で呌び出しをむンラむン化する可胜性がありたす。 これを確認するために、䞀般化された関数を曞きたす。

錆び

 fn test_code_generic<T: Canvas>(canvas: &mut T) { let a = Vec3 { x: 0, y:0, z:0 }; let b = Vec3 { x: 50, y: 55, z: -20 }; let rasterizer = LineRasterizer::new(a, b); for point in rasterizer { let color = std::u32::MAX; canvas.set_pixel(point.x as usize, point.y as usize, color); } }
      
      





すばやい

 func testCodeGeneric<T:Canvas>(canvas: T) -> () { let a = Vector3(x: 0, y:0, z:0) let b = Vector3(x: 50, y:55, z:-20) let raster = LineRaster(from: a, to: b) for point in raster { let color = UInt32.max canvas.setPixel(point.x, point.y, color: color) } }
      
      





Rustの結果は0.83でしたが、Swiftの4.94ず比范するず、Swiftの方がコヌドを最適化できたこずがわかりたすが、他のどこかにボトルネックが芋぀かりたせんでした。



次に、Canvasをボックスにパックしおみたしょう。Swiftには、inout修食子を䜿甚したす。これは、効果がmutに䌌おいたす。

錆び

 fn test_code_boxed(canvas: &mut Box<Canvas>) { let a = Vec3 { x: 0, y:0, z:0 }; let b = Vec3 { x: 50, y: 55, z: -20 }; let rasterizer = LineRasterizer::new(a, b); for point in rasterizer { let color = std::u32::MAX; canvas.set_pixel(point.x as usize, point.y as usize, color); } }
      
      





すばやい

 func testCodeInout(inout canvas: Canvas) -> () { let a = Vector3(x: 0, y:0, z:0) let b = Vector3(x: 50, y:55, z:-20) let raster = LineRaster(from: a, to: b) for point in raster { let color = UInt32.max canvas.setPixel(point.x, point.y, color: color) } }
      
      







結果はRustで0.91、Swiftで6.44です。



ボクシングはコヌドの実行を少し遅くしたしたが、それほど倧きくはありたせんでしたが、inoutの远加はSwiftに非垞に倧きな圱響を䞎えたした。 どうやらキャンバスぞのリンクを倉曎する機胜により、手がオプティマむザヌにバむンドされたす。



䞀般的に、Rustは同様のコヌディングスタむルでより生産的です。 所有暩の抂念により、コンパむラヌはコンパむル段階でプログラムの正圓性をチェックするだけでなく、必芁でない堎合に参照カりンタヌの䜿甚を避けるこずもできたす。反察に、必芁な堎合は明らかに䜿甚する必芁がありたす。 Swiftは、デフォルトで、すべおの堎所でクラスの参照カりンタヌを䜿甚したすが、これは倚くの堎合パフォヌマンスの䜎䞋に぀ながりたす。コンパむラヌがいく぀かの些现なケヌスで参照カりントを削陀できたずしおも、わずかに耇雑なケヌスで実行できるこずを保蚌したせん。 Rustはコヌドをコンパむルしたせん。 これは、コンパむラの成熟床ずそれに含たれる抂念の有効性の䞡方を瀺しおいたす。



結論ずしお、RustずSwiftが叀いC ++を背景にどのように芋えるかを比范したいず思いたす。

かなり䞀般的なベンチマヌクコヌド
 #include <iostream> #include <vector> #include <cassert> #include <memory> #include <cstdlib> template <typename T> struct GenericPixmap { size_t w, h; std::vector<T> data; GenericPixmap(size_t w_, size_t h_, T fill_data = T()) : w(w_), h(h_), data(w_*h_, fill_data) { } T* operator[] (size_t i) { return &data[i*w]; } }; struct Vec3 { int x, y, z; int& operator[] (size_t i) { assert(i >=0 && i < 3); switch (i) { case 0: return x; case 1: return y; case 2: return z; default: break; } return z; } }; bool operator== (const Vec3 &a, const Vec3 &b) { return ax == bx && ay == by && az && bz; } bool operator!= (const Vec3 &a, const Vec3 &b) { return !(a == b); } struct RasterState { Vec3 step; Vec3 d; size_t majorAxis; }; struct LineRaster { Vec3 from; Vec3 to; bool firstStep; RasterState state; LineRaster(const Vec3 &f, const Vec3 &t) : from(f), to(t), firstStep(true), state{} {} bool next_point() { if (firstStep) { size_t max = 0; for (int i = 0; i < 3; ++i) { auto d = to[i] - from[i]; state.step[i] = d > 0 ? 1 : -1; d = std::abs(d); if (d > max) { max = d; state.majorAxis = i; } } firstStep = false; return true; } else { if (from == to) return false; else { auto calc_rs = [this](auto axis) { return std::abs(to[axis] - from[axis]); }; from[state.majorAxis] += state.step[state.majorAxis]; auto rs_base = calc_rs(state.majorAxis); for (int i = 0; i < 3; ++i) { auto rs = calc_rs(i); if (rs > 0 && i != state.majorAxis) { state.d[i] += rs; if (state.d[i] >= rs_base) { state.d[i] -= rs_base; from[i] += state.step[i]; } } } return true; } } } }; using Pixmap = GenericPixmap<uint32_t>; void test_code(Pixmap &pixmap) { Vec3 a { 0, 0, 0 }; Vec3 b { 50, 55, -20 }; LineRaster raster(a, b); while (raster.next_point()) { const auto &p = raster.from; pixmap[px][py] = 0xFFFFFF; } } int main(int, char **) { Pixmap pixmap(300, 300, 0); Vec3 a { 0, 0, 0 }; Vec3 b { 10, 5, -4 }; LineRaster raster(a, b); while (raster.next_point()) { const auto &p = raster.from; uint32_t color = 0xffffff; pixmap[px][py] = color; std::cout << "C++: point x:" << px << " y:" << py << " z:" << pz << " color:" << color << std::endl; } for (size_t i = 0; i < 1000000; ++i) test_code(pixmap); }
      
      







執筆の過皋で、いく぀かのセグメンテヌション゚ラヌが発生し、結果を取埗する前にlldbでデバッグする必芁がありたした。



Rustでは0.79、gccでは0.31。



結果は非垞に興味深いものです。䞀方で、Rustはclangずほが同じ速床を瀺したすが、他方では、gccは単にすべおを䞊回りたした。぀たり、䞀般に、llvmプラットフォヌムには倚くの努力が必芁ですが、そのフレヌムワヌク内では、Rustは既に背埌でclangを吞い蟌んでいたす。぀たり、パフォヌマンス芁件に重芁なセクションを䜜成するために既に安党に起動できたす。



完党なベンチマヌクコヌドはgithubにありたす。パフォヌマンスの改善に関する提案は受け入れられたす。



残念なこずに、゚ラヌ凊理、むンフラストラクチャ、CたたはObjectiveCコヌドずの通信、プロパティなど、興味深いものを1぀の蚘事ですべお考慮するこずは困難であり、今埌これらに觊れるこずができるこずを願っおいたす。



結論





Swiftの開発者は、倚くの構文糖を蚀語に远加するこずにより、コヌドの読み取りにより倚くの時間を費やしたす。 Rustはこの問題でより犁欲的で、゚ンティティの数を最小限に抑えようずしたす。ただし、マクロたたはプラグむンを䜿甚しおコンパむラに倚くのものを远加できたす。効果的なれロコストの抜象化により、Rustコヌドの生産性は向䞊したすが、理解が難しくなりたす。 Swiftを䜿甚するず、メモリ管理にあたり泚意を払わずにプログラムを䜜成できたすが、コヌドは非垞に効果的ですが、この点ではRustに劣りたす。



むンフラストラクチャに関しおは、Swiftには優れたXcode IDEがありたすが、OS Xでのみ䜿甚可胜であり、CocoaバむンディングはiOSたたはOS X甚のグラフィカルアプリケヌションを蚘述およびリリヌスできるようになりたした。Rustは、cargoおよびcrates.io膚倧な数のラむブラリずアプリケヌションが存圚し、開発されおいたすが、これたでのずころ、開発されたIDEも優れたGUIフレヌムワヌクもありたせん。



残念ながら、SwiftにはただGrand Central Dispatchによるネむティブサポヌトがないため、マルチスレッドプログラミングに぀いおは䜕も蚀えたせんが、Rustのアプロヌチにより、高速で信頌性の高いアプリケヌションを䜜成できたす。厳密なコンパむラは、同期゚ラヌからあなたを単に救いたす。



Swiftの求人ははるかに倧きくなり、孊習も容易になりたしたが、これはRustのスペシャリストが近い将来に需芁がないずいう意味ではありたせん。䞖界には、これらの蚀語の䞡方が容赊なく互いに競合する倚くの問題があるず思いたす。



あずがき





Rustは劥協を蚱さず犁欲的です。Light偎に有利です。Swiftはその構文糖をDark偎に持ち蟌むこずができたす。



曎新する

ナヌザヌYarryがプルリク゚ストを送信したした。これにより、クラスを構造䜓に眮き換えるこずで、Swiftバヌゞョンを半分に高速化するこずができたした。Swiftの構造䜓はCの䞖界の察応物ずしお動䜜し、スタック䞊で際立っおおり、関数に枡されるずきにリンクがコピヌされるため、リンクをカりントする必芁はありたせん。しかし同時に、Swiftのドキュメントでは、実際には、コピヌは必芁な堎合にのみ発生するず䞻匵しおいたす。したがっお、このような埮劙な知識があるず、コヌドを倧幅に高速化できたす。䞀方、Rustでは、孊習曲線をより急にしたすが、明瀺的に考えるこずを䜙儀なくされたすが、プロファむリングやあらゆる皮類の実隓でコヌドを最適化する手間を枛らす必芁がありたす。



All Articles