 		let markersToShow = prioritizedMarkers.prefix(LabelView.s_maxLabels)
 		let hashesToShow = markersToShow.map { $0.ownerHash }
 		
-		// $ this tag mechanism eats ~30% of the CPU...
-		
 		// First free up any labels that no longer have active markers
 		_ = poiLabels
-			.filter({$0.tag != 0 && !hashesToShow.contains($0.tag)})
+			.filter({ $0.ownerHash != 0 && !hashesToShow.contains($0.ownerHash) })
 			.map(unbindLabel)
 		
 		// Bind new markers into free labels
 		for marker in markersToShow {
-			guard poiLabels.first(where: { $0.tag == marker.ownerHash }) == nil else { // $ hotspot
+			guard poiLabels.first(where: { $0.ownerHash == marker.ownerHash }) == nil else {
 				continue
 			}
-			guard let freeLabel = poiLabels.first(where: { $0.tag == 0 }) else {	// $ hotspot
+			guard let freeLabel = poiLabels.first(where: { $0.ownerHash == 0 }) else {
 				print("Marker \(marker.ownerHash) could not be bound to a free label")
 				continue
 			}

	
	func renderLabels(projection project: (Vertex) -> CGPoint) {
		for label in poiLabels {
			guard let marker = poiPrimitives.values.first(where: { $0.ownerHash == label.tag }) else { // $ hotspot
				continue
			}
			
			// Layout labels (region labels hang under the center, POI labels hang from their top-left)
			let screenPos = project(marker.worldPos)
			switch marker.kind {
			case .Region: label.center = screenPos
			default: label.frame.origin = screenPos
			}
		}
	}
	
	func bindLabel(_ label: UILabel, to marker: LabelMarker) {
		label.tag = marker.ownerHash
		label.isHidden = false
		
		let alignment: NSTextAlignment
		let textColor: UIColor

			 .foregroundColor: textColor,
			 .strokeWidth: strokeWidth]
		
		label.view.textAlignment = alignment
		label.view.attributedText = NSAttributedString(string: marker.name, attributes: strokeAttribs)
	}
	
	func unbindLabel(_ label: Label) {
		label.ownerHash = 0
		label.view.isHidden = true
	}
}
