Reduce memory usage of adding tracks
Significantly reduce the memory footprint of adding tracks to the playlist, by coalescing the NSString and NSData objects in the info dictionaries as they are being loaded in the background, into a common data set which will then be discarded when the whole job is completed. Signed-off-by: Christopher Snowhill <kode54@gmail.com>CQTexperiment
parent
d30746bffa
commit
5611d08df1
|
@ -164,6 +164,7 @@
|
||||||
838F851C256B4AC400C3E614 /* icon_blank.icns in Resources */ = {isa = PBXBuildFile; fileRef = 838F851B256B4AC400C3E614 /* icon_blank.icns */; };
|
838F851C256B4AC400C3E614 /* icon_blank.icns in Resources */ = {isa = PBXBuildFile; fileRef = 838F851B256B4AC400C3E614 /* icon_blank.icns */; };
|
||||||
838F851E256B4E5E00C3E614 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 838F851D256B4E5E00C3E614 /* Sparkle.framework */; };
|
838F851E256B4E5E00C3E614 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 838F851D256B4E5E00C3E614 /* Sparkle.framework */; };
|
||||||
838F851F256B4E8B00C3E614 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 838F851D256B4E5E00C3E614 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
838F851F256B4E8B00C3E614 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 838F851D256B4E5E00C3E614 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
|
83988F0E27BE0A5900A0E89A /* RedundantPlaylistDataStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 83988F0D27BE0A5900A0E89A /* RedundantPlaylistDataStore.m */; };
|
||||||
8399D4E21805A55000B503B1 /* XmlContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8399D4E01805A55000B503B1 /* XmlContainer.m */; };
|
8399D4E21805A55000B503B1 /* XmlContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8399D4E01805A55000B503B1 /* XmlContainer.m */; };
|
||||||
839DA7CF274A2D4C001B18E5 /* NSDictionary+Merge.m in Sources */ = {isa = PBXBuildFile; fileRef = 839DA7CE274A2D4C001B18E5 /* NSDictionary+Merge.m */; };
|
839DA7CF274A2D4C001B18E5 /* NSDictionary+Merge.m in Sources */ = {isa = PBXBuildFile; fileRef = 839DA7CE274A2D4C001B18E5 /* NSDictionary+Merge.m */; };
|
||||||
83A360B220E4E81D00192DAB /* Flac.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8303A30C20E4E3D000951EF8 /* Flac.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
83A360B220E4E81D00192DAB /* Flac.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8303A30C20E4E3D000951EF8 /* Flac.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
|
@ -974,6 +975,8 @@
|
||||||
838F84FF25687C5C00C3E614 /* Cog-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Cog-Bridging-Header.h"; sourceTree = "<group>"; };
|
838F84FF25687C5C00C3E614 /* Cog-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Cog-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
838F851B256B4AC400C3E614 /* icon_blank.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = icon_blank.icns; sourceTree = "<group>"; };
|
838F851B256B4AC400C3E614 /* icon_blank.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = icon_blank.icns; sourceTree = "<group>"; };
|
||||||
838F851D256B4E5E00C3E614 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = ThirdParty/Frameworks/Sparkle.framework; sourceTree = "<group>"; };
|
838F851D256B4E5E00C3E614 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = ThirdParty/Frameworks/Sparkle.framework; sourceTree = "<group>"; };
|
||||||
|
83988F0C27BE0A5900A0E89A /* RedundantPlaylistDataStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RedundantPlaylistDataStore.h; sourceTree = "<group>"; };
|
||||||
|
83988F0D27BE0A5900A0E89A /* RedundantPlaylistDataStore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RedundantPlaylistDataStore.m; sourceTree = "<group>"; };
|
||||||
8399D4E01805A55000B503B1 /* XmlContainer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XmlContainer.m; sourceTree = "<group>"; };
|
8399D4E01805A55000B503B1 /* XmlContainer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XmlContainer.m; sourceTree = "<group>"; };
|
||||||
8399D4E11805A55000B503B1 /* XmlContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XmlContainer.h; sourceTree = "<group>"; };
|
8399D4E11805A55000B503B1 /* XmlContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XmlContainer.h; sourceTree = "<group>"; };
|
||||||
839DA7CB274A2D4C001B18E5 /* NSDictionary+Merge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Merge.h"; sourceTree = "<group>"; };
|
839DA7CB274A2D4C001B18E5 /* NSDictionary+Merge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Merge.h"; sourceTree = "<group>"; };
|
||||||
|
@ -1204,6 +1207,8 @@
|
||||||
177EC01D0B8BC2CF0000BC8C /* TrackingSlider.m */,
|
177EC01D0B8BC2CF0000BC8C /* TrackingSlider.m */,
|
||||||
8370D739277419D200245CE0 /* SQLiteStore.h */,
|
8370D739277419D200245CE0 /* SQLiteStore.h */,
|
||||||
8370D73C277419F700245CE0 /* SQLiteStore.m */,
|
8370D73C277419F700245CE0 /* SQLiteStore.m */,
|
||||||
|
83988F0C27BE0A5900A0E89A /* RedundantPlaylistDataStore.h */,
|
||||||
|
83988F0D27BE0A5900A0E89A /* RedundantPlaylistDataStore.m */,
|
||||||
);
|
);
|
||||||
path = Utils;
|
path = Utils;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -2511,6 +2516,7 @@
|
||||||
179D03200E0CB2500064A77A /* DirectoryNode.m in Sources */,
|
179D03200E0CB2500064A77A /* DirectoryNode.m in Sources */,
|
||||||
179D03210E0CB2500064A77A /* FileIconCell.m in Sources */,
|
179D03210E0CB2500064A77A /* FileIconCell.m in Sources */,
|
||||||
179D03220E0CB2500064A77A /* FileNode.m in Sources */,
|
179D03220E0CB2500064A77A /* FileNode.m in Sources */,
|
||||||
|
83988F0E27BE0A5900A0E89A /* RedundantPlaylistDataStore.m in Sources */,
|
||||||
179D03230E0CB2500064A77A /* FileTreeDataSource.m in Sources */,
|
179D03230E0CB2500064A77A /* FileTreeDataSource.m in Sources */,
|
||||||
179D03240E0CB2500064A77A /* FileTreeController.m in Sources */,
|
179D03240E0CB2500064A77A /* FileTreeController.m in Sources */,
|
||||||
8370D73D277419F700245CE0 /* SQLiteStore.m in Sources */,
|
8370D73D277419F700245CE0 /* SQLiteStore.m in Sources */,
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
|
|
||||||
#import "NSDictionary+Merge.h"
|
#import "NSDictionary+Merge.h"
|
||||||
|
|
||||||
|
#import "RedundantPlaylistDataStore.h"
|
||||||
|
|
||||||
@implementation PlaylistLoader
|
@implementation PlaylistLoader
|
||||||
|
|
||||||
- (id)init {
|
- (id)init {
|
||||||
|
@ -540,9 +542,11 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
|
||||||
|
|
||||||
NSLock *outLock = [[NSLock alloc] init];
|
NSLock *outLock = [[NSLock alloc] init];
|
||||||
NSMutableArray *outArray = [[NSMutableArray alloc] init];
|
NSMutableArray *outArray = [[NSMutableArray alloc] init];
|
||||||
|
RedundantPlaylistDataStore *dataStore = [[RedundantPlaylistDataStore alloc] init];
|
||||||
|
|
||||||
__block NSLock *weakLock = outLock;
|
__block NSLock *weakLock = outLock;
|
||||||
__block NSMutableArray *weakArray = outArray;
|
__block NSMutableArray *weakArray = outArray;
|
||||||
|
__block RedundantPlaylistDataStore *weakDataStore = dataStore;
|
||||||
|
|
||||||
{
|
{
|
||||||
[load_info_indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *_Nonnull stop) {
|
[load_info_indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *_Nonnull stop) {
|
||||||
|
@ -566,6 +570,7 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc
|
||||||
NSDictionary *entryInfo = [NSDictionary dictionaryByMerging:entryProperties with:entryMetadata];
|
NSDictionary *entryInfo = [NSDictionary dictionaryByMerging:entryProperties with:entryMetadata];
|
||||||
|
|
||||||
[weakLock lock];
|
[weakLock lock];
|
||||||
|
entryInfo = [weakDataStore coalesceEntryInfo:entryInfo];
|
||||||
[weakArray addObject:weakPe];
|
[weakArray addObject:weakPe];
|
||||||
[weakArray addObject:entryInfo];
|
[weakArray addObject:entryInfo];
|
||||||
[self setProgressBarStatus:progress];
|
[self setProgressBarStatus:progress];
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
//
|
||||||
|
// RedundantPlaylistDataStore.h
|
||||||
|
// Cog
|
||||||
|
//
|
||||||
|
// Created by Christopher Snowhill on 2/16/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
// This is designed primarily for the PlaylistEntry info loader, to prevent
|
||||||
|
// memory overrun due to redundant blobs of data being duplicated repeatedly
|
||||||
|
// until the list is fully loaded. This instance will be discarded after the
|
||||||
|
// info is loaded, freeing up hopefully way less memory afterward than now.
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface RedundantPlaylistDataStore : NSObject {
|
||||||
|
NSMutableArray *stringStore;
|
||||||
|
NSMutableArray *artStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)init;
|
||||||
|
- (NSDictionary *)coalesceEntryInfo:(NSDictionary *)pe;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,68 @@
|
||||||
|
//
|
||||||
|
// RedundantPlaylistDataStore.m
|
||||||
|
// Cog
|
||||||
|
//
|
||||||
|
// Created by Christopher Snowhill on 2/16/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Coalesce an entryInfo dictionary from tag loading into a common data dictionary, to
|
||||||
|
// reduce the memory footprint of adding a lot of tracks to the playlist.
|
||||||
|
|
||||||
|
#import "RedundantPlaylistDataStore.h"
|
||||||
|
|
||||||
|
@implementation RedundantPlaylistDataStore
|
||||||
|
|
||||||
|
- (id)init {
|
||||||
|
self = [super init];
|
||||||
|
|
||||||
|
if(self) {
|
||||||
|
stringStore = [[NSMutableArray alloc] init];
|
||||||
|
artStore = [[NSMutableArray alloc] init];
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)coalesceString:(NSString *)in {
|
||||||
|
if(in == nil) return in;
|
||||||
|
|
||||||
|
NSUInteger index = [stringStore indexOfObject:in];
|
||||||
|
if(index == NSNotFound) {
|
||||||
|
[stringStore addObject:in];
|
||||||
|
return in;
|
||||||
|
} else {
|
||||||
|
return [stringStore objectAtIndex:index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSData *)coalesceArt:(NSData *)in {
|
||||||
|
if(in == nil) return in;
|
||||||
|
|
||||||
|
NSUInteger index = [artStore indexOfObject:in];
|
||||||
|
if(index == NSNotFound) {
|
||||||
|
[artStore addObject:in];
|
||||||
|
return in;
|
||||||
|
} else {
|
||||||
|
return [artStore objectAtIndex:index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)coalesceEntryInfo:(NSDictionary *)entryInfo {
|
||||||
|
__block NSMutableDictionary *ret = [[NSMutableDictionary alloc] initWithCapacity:[entryInfo count]];
|
||||||
|
|
||||||
|
[entryInfo enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) {
|
||||||
|
if([[obj class] isSubclassOfClass:[NSString class]]) {
|
||||||
|
NSString *stringObj = (NSString *)obj;
|
||||||
|
[ret setObject:[self coalesceString:stringObj] forKey:key];
|
||||||
|
} else if([[obj class] isSubclassOfClass:[NSData class]]) {
|
||||||
|
NSData *dataObj = (NSData *)obj;
|
||||||
|
[ret setObject:[self coalesceArt:dataObj] forKey:key];
|
||||||
|
} else {
|
||||||
|
[ret setObject:obj forKey:key];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
return [NSDictionary dictionaryWithDictionary:ret];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
Loading…
Reference in New Issue