Marker Native Strategy (Experimental)
mapconductor-marker-native-strategy モジュールは、ネイティブC++の空間インデックスを使用した高性能なマーカー管理を提供します。この実験的モジュールは、大量のマーカー(10,000個以上)を持つアプリケーションのパフォーマンスを劇的に向上させます。
⚠️ 実験的モジュール: このモジュールは非常に実験的であり、ネイティブライブラリのサポートが必要です。本番環境での使用には細心の注意を払ってください。
marker native strategy は、Java ベースの空間インデックスを最適化された C++ 実装に置き換え、以下を提供します:
- 90%のメモリ削減: 標準的なマーカー管理と比較して
- ネイティブ空間クエリ: 最大パフォーマンスのための C++ 空間インデックス
- 効率的なビューポートカリング: ビューポート内のマーカーのみをレンダリング
- 並列処理: マルチスレッドマーカー操作
- 最小限の Java オーバーヘッド: ネイティブコードを単一の真実の情報源として
パフォーマンス特性
Section titled “パフォーマンス特性”メモリ使用量
Section titled “メモリ使用量”- 標準 MarkerManager: 1,000マーカーあたり約1MB
- NativeMarkerManager: 1,000マーカーあたり約100KB
- 最適化されたストレージ: エンティティストレージの重複なし
クエリパフォーマンス
Section titled “クエリパフォーマンス”- 標準空間クエリ: Java オーバーヘッドを伴う O(log n)
- ネイティブ空間クエリ: C++ 最適化による O(log n)
- 大規模データセット: 100,000以上のマーカーで10倍〜100倍のパフォーマンス向上
インストール
Section titled “インストール”build.gradle に native strategy モジュールを追加します:
dependencies { implementation "com.mapconductor:marker-native-strategy"
// 必須: Core モジュール implementation "com.mapconductor:mapconductor-bom:$version" // 必須: Core モジュール implementation "com.mapconductor:core"
// 地図SDKを選択 implementation "com.mapconductor:for-googlemaps"}地図SDKモジュールとの連携
Section titled “地図SDKモジュールとの連携”native strategy でも、地図上に描画するためには地図SDKごとの renderer/controller が必要です。
組み込みの地図SDKモジュールは MapServiceRegistry の capability として提供します(Map Service Registry)。
ネイティブライブラリのセットアップ
Section titled “ネイティブライブラリのセットアップ”モジュールにはネイティブC++ライブラリが必要です。アプリが必要なABIをサポートしていることを確認してください:
android { defaultConfig { ndk { abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64' } }}コアコンポーネント
Section titled “コアコンポーネント”NativeMarkerManager
Section titled “NativeMarkerManager”ネイティブ空間インデックスを使用した高性能マーカーマネージャー:
import com.mapconductor.marker.nativestrategy.NativeMarkerManagerimport com.mapconductor.core.geocell.HexGeocell
// Create native marker managerval nativeManager = NativeMarkerManager<ActualMarker>( hexGeocell = HexGeocell.defaultGeocell())
// Use like standard MarkerManagernativeManager.registerEntity(markerEntity)val nearestMarker = nativeManager.findNearest(position)val markersInBounds = nativeManager.findMarkersInBounds(bounds)ネイティブレンダリングストラテジ
Section titled “ネイティブレンダリングストラテジ”SimpleNativeParallelStrategy
Section titled “SimpleNativeParallelStrategy”ネイティブインデックスによる並列マーカーレンダリング:
import com.mapconductor.marker.nativestrategy.SimpleNativeParallelStrategy
val strategy = SimpleNativeParallelStrategy<ActualMarker>( expandMargin = 0.2, // Viewport expansion maxConcurrency = 4, // Parallel threads geocell = HexGeocell.defaultGeocell())基本的な使用方法
Section titled “基本的な使用方法”シンプルなネイティブマネージャー
Section titled “シンプルなネイティブマネージャー”@Composablefun BasicNativeExample() { // Create native marker manager val nativeManager = remember { NativeMarkerManager<GoogleMapActualMarker>( hexGeocell = HexGeocell.defaultGeocell() ) }
// Add markers to native manager LaunchedEffect(Unit) { val markers = generateLargeMarkerDataset() // 10,000以上のマーカー markers.forEach { markerData -> val entity = MarkerEntityInterface( state = MarkerState( id = markerData.id, position = markerData.position, icon = DefaultIcon() ) ) nativeManager.registerEntity(entity) } }
// GoogleMapView、MapboxMapView などの選択した地図SDKに置き換えてください MapView(state = mapViewState) { // Markers are managed by native strategy // No need to manually add Marker composables }
DisposableEffect(Unit) { onDispose { nativeManager.destroy() // Important: cleanup native resources } }}パフォーマンスモニタリング
Section titled “パフォーマンスモニタリング”@Composablefun NativePerformanceExample() { val nativeManager = remember { NativeMarkerManager<GoogleMapActualMarker>( hexGeocell = HexGeocell.defaultGeocell() ) }
var stats by remember { mutableStateOf<NativeMarkerManagerStats?>(null) }
// Monitor performance LaunchedEffect(Unit) { while (true) { delay(5000) // Update every 5 seconds stats = nativeManager.getNativeMemoryStats() } }
Column { stats?.let { s -> Text("Markers: ${s.entityCount}") Text("Native Index: ${s.nativeIndexCount}") Text("Memory: ${s.estimatedMemoryKB} KB") Text("Pure Native: ${s.usesPureNativeIndex}") }
// GoogleMapView、MapboxMapView などの選択した地図SDKに置き換えてください MapView(state = mapViewState) { // Native-managed markers } }}高度な使用方法
Section titled “高度な使用方法”並列レンダリングストラテジ
Section titled “並列レンダリングストラテジ”@Composablefun ParallelRenderingExample() { val parallelStrategy = remember { SimpleNativeParallelStrategy<GoogleMapActualMarker>( expandMargin = 0.3, // Larger viewport expansion maxConcurrency = 6, // More parallel threads geocell = HexGeocell( baseHexSideLength = 1000.0, // Optimized cell size zoom = 15.0 ) ) }
// Configure marker controller with parallel strategy LaunchedEffect(mapViewState) { mapViewState.getMapViewHolder()?.let { holder -> // Setup parallel strategy with map controller // Implementation depends on map provider } }
// GoogleMapView、MapboxMapView などの選択した地図SDKに置き換えてください MapView(state = mapViewState) { // Parallel-rendered markers }}動的マーカーロード
Section titled “動的マーカーロード”@Composablefun DynamicNativeLoadingExample() { val nativeManager = remember { NativeMarkerManager<GoogleMapActualMarker>( hexGeocell = HexGeocell.defaultGeocell() ) }
var currentBounds by remember { mutableStateOf<GeoRectBounds?>(null) } var visibleMarkers by remember { mutableStateOf<List<MarkerEntityInterface<GoogleMapActualMarker>>>(emptyList()) }
// Load markers dynamically based on viewport LaunchedEffect(currentBounds) { currentBounds?.let { bounds -> // Native spatial query is extremely fast visibleMarkers = nativeManager.findMarkersInBounds(bounds) } }
Column { Text("Visible Markers: ${visibleMarkers.size}")
// GoogleMapView、MapboxMapView などの選択した地図SDKに置き換えてください MapView( state = mapViewState, onCameraMove = { cameraPosition -> currentBounds = cameraPosition.visibleRegion?.bounds } ) { // Only visible markers are processed } }}ネイティブストラテジによるクラスタリング
Section titled “ネイティブストラテジによるクラスタリング”@Composablefun NativeClusteringExample() { val clusteringStrategy = remember { NativeSpatialMarkerStrategy<GoogleMapActualMarker>( clusteringEnabled = true, clusterThreshold = 50, // Cluster when > 50 markers nearby clusterRadius = 100.0, // 100-meter clustering radius geocell = HexGeocell.defaultGeocell() ) }
val nativeManager = remember { NativeMarkerManager<GoogleMapActualMarker>( hexGeocell = HexGeocell.defaultGeocell() ) }
// Add large dataset for clustering LaunchedEffect(Unit) { val denseMarkers = generateDenseMarkerCluster( center = GeoPoint.fromLatLong(37.7749, -122.4194), count = 1000, radiusMeters = 500.0 // 500 meters )
denseMarkers.forEach { markerData -> val entity = MarkerEntityInterface( state = MarkerState( id = markerData.id, position = markerData.position, icon = DefaultIcon(fillColor = Color.Blue, scale = 0.8f) ) ) nativeManager.registerEntity(entity) } }
// GoogleMapView、MapboxMapView などの選択した地図SDKに置き換えてください MapView(state = mapViewState) { // Native clustering automatically groups nearby markers }}ネイティブインデックス操作
Section titled “ネイティブインデックス操作”直接空間クエリ
Section titled “直接空間クエリ”fun performNativeQueries(nativeManager: NativeMarkerManager<ActualMarker>) { val center = GeoPoint.fromLatLong(37.7749, -122.4194) val bounds = GeoRectBounds( southWest = GeoPoint.fromLatLong(37.7700, -122.4250), northEast = GeoPoint.fromLatLong(37.7800, -122.4150) )
// Native spatial queries are extremely fast val nearestMarker = nativeManager.findNearest(center) val boundedMarkers = nativeManager.findMarkersInBounds(bounds) val totalMarkers = nativeManager.allEntities().size
// Memory and performance stats val stats = nativeManager.getNativeMemoryStats() println("Query performance: ${stats.nativeIndexCount} indexed markers") println("Memory usage: ${stats.estimatedMemoryKB} KB")}suspend fun batchNativeOperations(nativeManager: NativeMarkerManager<ActualMarker>) { val markersBatch = generateMarkerBatch(10000) // 10,000 markers
// Efficient batch registration withContext(Dispatchers.Default) { markersBatch.forEach { markerData -> val entity = MarkerEntityInterface( state = MarkerState( id = markerData.id, position = markerData.position, icon = DefaultIcon() ) ) nativeManager.registerEntity(entity) } }
// Verify registration val totalMarkers = nativeManager.allEntities().size println("Registered $totalMarkers markers in native index")}リソースクリーンアップ
Section titled “リソースクリーンアップ”@Composablefun ResourceManagementExample() { val nativeManager = remember { NativeMarkerManager<GoogleMapActualMarker>( hexGeocell = HexGeocell.defaultGeocell() ) }
// Proper cleanup is crucial for native resources DisposableEffect(nativeManager) { onDispose { // Cleanup native resources nativeManager.destroy() } }
// Monitor memory usage var memoryStats by remember { mutableStateOf<NativeMarkerManagerStats?>(null) }
LaunchedEffect(Unit) { while (true) { delay(10000) // Check every 10 seconds memoryStats = nativeManager.getNativeMemoryStats()
// Log memory usage for monitoring memoryStats?.let { stats -> if (stats.estimatedMemoryKB > 10000) { // > 10MB println("High memory usage detected: ${stats.estimatedMemoryKB} KB") } } } }
// GoogleMapView、MapboxMapView などの選択した地図SDKに置き換えてください MapView(state = mapViewState) { // Native-managed markers }}パフォーマンス最適化
Section titled “パフォーマンス最適化”設定チューニング
Section titled “設定チューニング”// Optimal configuration for large datasetsval optimizedGeocell = HexGeocell( baseHexSideLength = 500.0, // Smaller cells for dense data zoom = 18.0 // Higher resolution)
val optimizedManager = NativeMarkerManager<ActualMarker>( hexGeocell = optimizedGeocell)
// Optimal parallel strategyval optimizedStrategy = SimpleNativeParallelStrategy<ActualMarker>( expandMargin = 0.1, // Smaller expansion for performance maxConcurrency = Runtime.getRuntime().availableProcessors(), geocell = optimizedGeocell)ベンチマークテスト
Section titled “ベンチマークテスト”suspend fun benchmarkNativePerformance() { val standardManager = MarkerManager<ActualMarker>(HexGeocell.defaultGeocell()) val nativeManager = NativeMarkerManager<ActualMarker>(HexGeocell.defaultGeocell())
val testMarkers = generateTestDataset(50000) // 50,000 markers
// Benchmark registration val standardTime = measureTimeMillis { testMarkers.forEach { standardManager.registerEntity(it) } }
val nativeTime = measureTimeMillis { testMarkers.forEach { nativeManager.registerEntity(it) } }
// Benchmark spatial queries val testBounds = GeoRectBounds( southWest = GeoPoint.fromLatLong(37.7700, -122.4250), northEast = GeoPoint.fromLatLong(37.7800, -122.4150) )
val standardQueryTime = measureTimeMillis { repeat(1000) { standardManager.findMarkersInBounds(testBounds) } }
val nativeQueryTime = measureTimeMillis { repeat(1000) { nativeManager.findMarkersInBounds(testBounds) } }
println("Registration - Standard: ${standardTime}ms, Native: ${nativeTime}ms") println("Queries - Standard: ${standardQueryTime}ms, Native: ${nativeQueryTime}ms")}ベストプラクティス
Section titled “ベストプラクティス”- リソース管理: 使用後は必ず NativeMarkerManager で
destroy()を呼び出す - バッチ操作: 大規模データセットにはバッチ登録を使用する
- メモリ監視: 本番環境でネイティブメモリ使用量を監視する
- テスト: 特定のデータパターンで徹底的にテストする
- フォールバックストラテジ: サポートされていないデバイス向けに非ネイティブフォールバックを用意する
制限事項と考慮事項
Section titled “制限事項と考慮事項”- プラットフォームサポート: ターゲットABI向けのネイティブライブラリサポートが必要
- メモリ管理: ネイティブメモリはガベージコレクトされない
- デバッグ: ネイティブクラッシュはJavaクラッシュよりもデバッグが困難
- バイナリサイズ: ネイティブライブラリによりAPKサイズが増加
- 互換性: すべてのデバイス/エミュレータで動作しない可能性がある
トラブルシューティング
Section titled “トラブルシューティング”ネイティブライブラリ読み込みの問題
Section titled “ネイティブライブラリ読み込みの問題”// Check native library availabilitytry { val nativeManager = NativeMarkerManager<ActualMarker>( hexGeocell = HexGeocell.defaultGeocell() ) // Native library loaded successfully} catch (e: UnsatisfiedLinkError) { // Fallback to standard marker manager val standardManager = MarkerManager<ActualMarker>( hexGeocell = HexGeocell.defaultGeocell() )}メモリリーク防止
Section titled “メモリリーク防止”class MarkerActivity : ComponentActivity() { private var nativeManager: NativeMarkerManager<ActualMarker>? = null
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState)
nativeManager = NativeMarkerManager(HexGeocell.defaultGeocell()) }
override fun onDestroy() { super.onDestroy()
// Critical: cleanup native resources nativeManager?.destroy() nativeManager = null }}marker native strategy モジュールは、マーカーを多用するアプリケーションに大幅なパフォーマンス向上をもたらしますが、実験的な性質のため、慎重なリソース管理と徹底的なテストが必要です。