-
-
Notifications
You must be signed in to change notification settings - Fork 731
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GRDB Inflections takes too much memory (~600kb) #755
Comments
Hello @zavsby, Thanks for reporting this.
There is no such possibility today. (Edit: I was partially wrong - see comments below) Since it would be good to lower the memory consumption of inflections, we could improve on the current straight and naive port from Ruby on Rails inflections. A simpler alternative would be to delay the loading of inflections in memory until they are actually used, so that only users of associations "pay" the price for inflections. I would be quite interested in a contribution in this area. Do you feel like investigating the topic, for your own benefit, as well as the benefit of other users? |
This picture shows that your app does use associations (belongsTo, hasMany, etc.) It is inflections that help you declare associations without telling GRDB the raw low-level strings that make it work under the hood. They are a core component of the ergonomics of associations, and they won't be removed. My suggestion above of "delay the loading of inflections in memory until they are actually used" will thus not help your app (and I'll now even assume, unless proven otherwise, that inflections are already lazily loaded). |
However, you can use your own inflections in lieu of the default ones: // Early in the lifetime of your app: remove default inflections
Inflections.default = Inflections() This should remove from memory the default inflections. But some of your associations may break, and you may have to declare string keys explicitly in order to manually perform the job of inflections: struct Team {
// Explicit key in order to deal with missing inflections
// ---------------------------------------v
static let players = hasMany(Player.self, key: "players")
}
// Explicit key in order to deal with missing inflections
// ---------------------------------------------------v
let request = Team.annotated(with: Team.players.count.forKey("playerCount")) See https://github.com/groue/GRDB.swift/blob/master/Documentation/AssociationsBasics.md#convention-for-database-table-names for more information Please tell if this provides a quick fix for your app, thank you. |
Thanks for quick response. Actually we are not using any associations (we override databaseTableName and do not use relations for now). As I see from code Inflections are created here anyway in row
@groue Seems your suggestion to remove default inflections works well for us (for memory consumption). We will recheck if nothing is broken by this fix. Thanks. |
All right, @zavsby, thanks for the information 👍 I'll check if association objects can be created out of nowhere. |
I've run the full GRDB test suite, minus tests that involve associations, and inflections. The default inflections are never loaded in memory. This applies to the version 4.8.1 you are running, up to the the latest version.
I'm somewhat embarrassed, because there are only two possibilities:
Would you please put a breakpoint in |
@groue I'm trying to reproduce it and seems to be very strange actually. It does not ever enter in SQLAssociation or Inflections code when I'm building GRDB in Debug so it does not take any memory. But when I'm building it using release configuration it behaves unpredictable:
Looks like swift optimizes code somehow in release and initializes Inflections before it is actually used but I cannot explain this... |
Wow, this is unexpected, isn't it? 😅 I'm able to reproduce this behavior as well, with a plain iOS app, using the manual installation method, compiled in Release configuration, running in the simulator. The following code is just enough: try! DatabaseQueue().write { db in
try db.create(table: "player") { t in
t.autoIncrementedPrimaryKey("id")
}
struct Player: TableRecord, FetchableRecord {
init(row: Row) { }
}
_ = try Player.fetchOne(db)
} Now let's understand how to workaround this compiler "optimization". |
OK, I won't pretend I precisely understand what is happening, but we have a little more information from lldb. If I set a breakpoint in stacktrace
Basically, everything is inlined. This exposes the eventual need for Let's put this interpretation to the test, and let's never inline uses of How to do that? https://forums.swift.org/t/is-it-possible-to-prevent-inlining-and-tail-call-optimization/21680 This gives this patch: // GRDB/Utils/Inflections.swift
extension String {
+ @inline(never)
var pluralized: String {
return Inflections.default.pluralize(self)
}
+ @inline(never)
var singularized: String {
return Inflections.default.singularize(self)
}
} Success! Will you tell if the same patch solves your issue, @zavsby, without setting Inflections.default to an empty instance? |
@groue Hi, thanks for this fix. Looks like it works well and Inflections are not created now according to Allocations profiler. |
…ease builds This commit addresses #755
Fix pushed in v4.12.2. |
…ized release builds" This reverts commit c62ccf1 (aimed at fixing #755). We can do this because swiftlang/swift#30445 is present in the Swift 5.3 release https://github.com/apple/swift/releases/tag/swift-5.3-RELEASE
What did you do?
We are using GRDB for sqlite database in Notification service extension on iOS. It has very strict memory usage up to 24MB (including process itself). We are using database pool with maximum 1 reader and found that GRDB takes more than 1MB of memory persistently. Most of the memory is taken by Inflections structure (600kb or ~9% of all memory we are using):
197.27 KB 2.8% 555 Inflections.plural(:options::)
180.09 KB 2.5% 816 Inflections.irregularSuffix(::)
143.48 KB 2.0% 649 Inflections.singular(:options::)
85.89 KB 1.2% 327 Inflections.uncountableWords(_:)
Please see call stacks below:
https://imgur.com/pwcCtVj
https://imgur.com/BKuuB1o
As I understand we are not using any of these features (we have only tables without any relations).
Can we disable this feature so it won't consume memory if this feature is not required (we are not using any relations in tables in notification service extension) and how could we do this?
Thanks.
Environment
GRDB flavor(s): GRDB
GRDB version: 4.8.1
Installation method: manual
Xcode version: 11.4
Swift version: 5.2
Platform(s) running GRDB: iOS Notification Service extension
macOS version running Xcode: MacOS Catalina
The text was updated successfully, but these errors were encountered: