Monday, April 9, 2012

Is mdworker Jeopardizing Your Resources? Run It at a Lower Priority

Since I've installed OS X Lion, I've been complaining about the performance degradation I was observing. More often than not, I find the guilty to be mdworker, the (in)famous Spotlight's indexer process, which may use lots of CPU cycles and generate lots of IOPs. Perhaps, unless you're using an SSD, the hard disk is going to be the bottleneck in many situations. Even if it's not, I've seen mdworker using an entire CPU core for entire seconds: it's a lot of computational time, and even a human being is going to notice the performance degradation of his machine when mdworker stubbornly keeps on using CPU time over and over again.

It's important to realize that the job of mdworker is observing changes in the file system and reflect those changes into Spotlight's content indexes. That's not the problem. The problem is that, by default, mdworker appears to be running with the same priority and I/O priority than most your user space processes. Depending on your computer usage pattern, you may or may not observe any performance degradation caused by mdworker: in fact, I bet most users won't notice it, but when you do, it's a real pain.

In my case, I notice a huge performance degradation when using programs that can generate massive changes in the file system in a short period of time, such as Adobe Lightroom or Apple Aperture. I found myself watching the Lightroom window freezing so often, waiting for mdworker to finish its job and relinquish some resources, that I decided to look for a solution to this problem.

Fortunately, there's a quick workaround that works pretty well and addresses the core issue with mdworker: its priority.

OS X Tiger (10.4) introduced a service management framework, launchd, that essentially replaced a bunch of legacy service handling daemons such as init, inetd, xinetd, and all the related scripts. launchd centralizes service management and, as such, it also centralizes service configuration using a property list files. If you're new to OS X development, property list essentially are XML files using a schema defined by Apple.

If you want to customize the behavior of an OS X service, you can just modify the corresponding configuration file without any deeper knowledge of the service behavior. You can read the launchd.plist official documentation for a list of valid configuration keys.

Although we're focusing on mdworker here, the concepts herein can be applied to any service you'd like to configure.

Modifying a Service Configuration File

Service configuration files are currently stored in two directories:

  • /System/Library/LaunchAgents
  • /System/Library/LaunchDaemons

First of all, you've got to locate the configuration file of the service you want to customize. Open the file in your favorite XML editor and carefully modify it. An invalid property list file will result in launchd rejecting the invalid configuration and the corresponding service failing to start.

With the release of XCode 4, the Property List Editor is no longer available as a separate application and all the editing is performed inside XCode. If you've got XCode, it's very easy to modify property list files using its GUI. If you haven't got XCode, it's a bit overkill to install such a huge application only to modify a property list file. I'd rather use an alternative XML editor and carefully review the changes before applying them.

In any case, I'm including my entire configuration file in this blog post so that you can just copy it over yours.

Reducing the mdworker Priority

The configuration file of mdworker (the Spotlight's indexer) is:

/System/Library/LaunchDaemons/com.apple.metadata.mds.plist

As we can see in launchd.plist documentation, we can take advantage of two configuration keys to lower the working priority of this process:

  • LowPriorityIO
  • Nice


For CPU-intensive processes, Nice can be the solution, since it specifies the service's scheduling priority. The higher the nice level, the lower its priority. Nice can take values in the [-20, 20] range.

Since mdworker is also a I/O-intensive process, we will use the LowPriorityIO configuration key as well. This configuration key takes a boolean value and, when set to true, it instructs the kernel to consider the process as low priority when performing file system I/O. That's just what we need to avoid mdworker jeopardizing our resources when using I/O intensive processes such as Adobe Lightroom.

The resulting configuration file is the following:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>KeepAlive</key>
  <true/>
  <key>Label</key>
  <string>com.apple.metadata.mds</string>
  <key>ProgramArguments</key>
  <array>
    <string>/System/Library/Frameworks/CoreServices.framework/Frameworks/Metadata.framework/Support/mds</string>
  </array>
  <key>MachServices</key>
  <dict>
    <key>com.apple.metadata.mds</key>
    <true/>
    <key>com.apple.metadata.mds.xpc</key>
    <true/>
    <key>com.apple.metadata.mds.xpcs</key>
    <true/>
  </dict>
  <key>SoftResourceLimits</key>
  <dict>
    <key>NumberOfFiles</key>
    <integer>2048</integer>
  </dict>
  <key>HardResourceLimits</key>
  <dict>
    <key>NumberOfFiles</key>
    <integer>2048</integer>
  </dict>
  <key>LowPriorityIO</key>
  <true/>
  <key>Nice</key>
  <integer>20</integer>
</dict>
</plist>

Here's the same file as seen in XCode:

XCode 4 - Property List Editor (Integrated)

Conclusion

The next time the service restarts the new configuration will take effect. The easiest way to do it is just reboot. mdworker will now work as usual, but won't steal your resources as aggressively as before.

You can check that mdworker is now running with a lower priority examining ps output (6th and 7th column):

$ ps -axl | grep mdworker

  UID   PID  PPID        F CPU PRI NI       SZ    RSS WCHAN     S             ADDR TTY           TIME CMD

  501   234   232    84004   0   4 17  2485976  25916 -      SN   ffffff800f0bbdc0 ??         3:56.98 /System/Library/Frameworks/CoreServices.framework/Frameworks/Metadata.framework/Versions/A/Support/mdworker -s mdworker -c MDSImporterWorker -m com.apple.mdworker.pool.0
   89   881   879    84004   0   4 17  2479740   6420 -      SN   ffffff800fd84200 ??         0:00.14 /System/Library/Frameworks/CoreServices.framework/Frameworks/Metadata.framework/Versions/A/Support/mdworker -s mdworker -c MDSImporterWorker -m com.apple.mdworker.pool.0


2 comments:

Bahman said...

I'm using mountain lion. I just used your method but it stopped spotlight from working. Even when I revert the changes, it doesn't work. I followed every thing very carefully. I used this command to launch the indexer:

sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.metadata.mds.plist

But it gives me this error:

dyld: DYLD_ environment variables being ignored because main executable (/usr/bin/sudo) is setuid or setgid
launchctl: Dubious ownership on file (skipping): /System/Library/LaunchDaemons/com.apple.metadata.mds.plist
nothing found to load

what should I do now? please help me!

Enrico Maria Crisostomo said...

Hi Bahman.

This procedure should work without problems in Mountain Lion. The error you receive is pretty self-explanatory and I suspect you've hitting some ownership issues on that property list file. Please, check it's owned by root:wheel and that it cannot be written by a non root user. To restore its default ownership and permissions you can use:

# chown root:wheel filename
# chmod 644 filename

Cheers,
-- Enrico