Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
310 views
in Technique[技术] by (71.8m points)

ios - Realm delay at end of write function

I've built a sports game in Xcode/Swift using Realm as a base. It's running and working, but realm transactions are getting slower and slower over time, to the point where it's significantly slower than i'd hope. This is particularly evident in this example.

Each week there are a number of matches, with each player having a GameStat object created for every match (about 2,000 new GameStats created each week).

class GameStat: Object {         
  @objc dynamic var iden = UUID().uuidString
  @objc dynamic var playerIden = "" // Links to iden of a Player Object
  @objc dynamic var statOne = 0
  @objc dynamic var statTwo = 0
  @objc dynamic var statThree = 0
  etc...

  // Primary Key
  override static func primaryKey() -> String? {
    return "iden"
  }
}

To prevent multiple realm writes throughout each game, I create GameStats outside of Realm (store in thisGameGameStats array) and then batch write these GameStats to realm once the game is complete. It is this batch write to Realm which is very slow.

try! realm.write {
  for gmStat in thisGameGameStats {
    realm.add(gmStat)
  }
}

I've measured time taken and I always see the 'for' loop complete after 0.05 seconds. In the first couple of loops, there is no delay for the realm.write function to complete, however after a couple of seasons (c70k records in database) it takes a further 0.50 seconds for the realm.write function to complete.

Is this to be expected, or is there an opportunity to optimise here?

EDIT: By removing the primaryKey, the write becomes significantly quicker and doesn't seem to slow down as the database grows. I'm assuming there is therefore some form of sort by primaryKey that happens on commit.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This isn't exactly an answer but may provide some validatation and illustrate an interesting point.

TL;DR

Writing chunks of objects over time does not have a significant impact on write times - you should not be seeing a significant increase in write times based on the existing volume of data in this use case.

The long version with setup and testing:

When a write has completed, realm will generate a notification so we're using that notification to 'time' the writes.

We start with a class var to store the start time of each write and a notification to track when it's done

var startTime = Date()
var gameStatNotificationToken: NotificationToken?

then we add an observer to realm that will execute when the write has completed that determines the elapsed time between the start time and now

func addObserver {
    if let realm = gGetRealm() { //this just gets a realm
        self.gameStatNotificationToken = realm.observe { notification, realm in
            let elapsed = Date().timeIntervalSince(self.startTime)
            print("(elapsed)")
        }
    }
}

Then the code to actually write the data

func writeLotsOfData() {
    autoreleasepool {

        var  gameStatArray = [GameStat]()

        for index in 0..<10000 {
            let stat = GameStat()
            stat.playerIden = "(index)"
            gameStatArray.append(stat)
        }

    
        if let realm = gGetRealm() {
            self.startTime = Date()
            try! realm.write {
                realm.add(gameStatArray)
            }
        }
    }
}

and then a loop that calls the above code 10 times; writing 10k per loop, 100k total objects.

func looper() {
    for i in 0..<10 {
        self.writeLotsOfData()
    }
}

So the end result shows no increase in writing over time

0.19487500190734863
0.1404169797897339
0.14565002918243408
0.15493690967559814
0.14426898956298828
0.15933406352996826
0.15379595756530762
0.16700804233551025
0.1598280668258667
0.15421009063720703

EDIT

Here's the GameStat object I am using - it's identical to the one in the question

class GameStat: Object {
  @objc dynamic var iden = UUID().uuidString
  @objc dynamic var playerIden = "" // Links to iden of a Player Object
  @objc dynamic var statOne = 0
  @objc dynamic var statTwo = 0
  @objc dynamic var statThree = 0

  // Primary Key
  override static func primaryKey() -> String? {
    return "iden"
  }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...