Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2006-110
$eventId
ID of event: wwdc2006
$eventContentId
ID of session without event part: 110
$eventShortId
Shortened ID of event: wwdc06
$year
Year of session: 2006
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC06 • Session 110

64-bit Overview

Application Technologies • 50:34

Today's software uses increasing amounts of memory - and many products are pushing the boundaries of 32-bit addressing. Now in Leopard you have a full 64-bit application stack for both PowerPC and Intel-based Macintoshes. Learn the what, why, and how basics of 64-bit development, and understand what you need to know to scope out your 64-bit development efforts today.

Speaker: Ali Ozer

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it may have transcription errors.

Welcome to the 64-bit overview session. My name is Ali Ozer. I'm the manager of the Cocoa Frameworks team. I am also--I've also been coordinating the 64-bit effort across Leopard for the past year, and that's why I'm speaking to you today. Okay, so today's talk, we're first going to cover what 64-bit is.

Then we're going to look at the 64-bit landscape. We're going to talk about what 64-bit means to you. Then we're going to talk a bit about the porting and pitfalls that you might encounter during the porting and have some closing thoughts at the end. Thank you. So what is 64-bit? 64-bit is support for 64-bit address space. That's the most basic definition. 32-bit can support 4 gigabytes, which is about 4 billion bytes, of course, while 64-bit extends that to 16 exabytes, which is 16 billion billion bytes, or 16 quintillion.

Now, the important thing about 64-bit support is that it's concurrent use of 64-bit. There are already some 32-bit applications out there, such as iMovie, Final Cut Pro, database programs, et cetera, that let you manage more than four gigabytes of data. However, the way they do that is by loading those files into memory in chunks, and they sort of do their own paging if needed, and they never load more than four gigabytes into memory at once. With 64-bit support, you get concurrent access to potentially 64-bit's worth of address space. Now let me give you sort of a scale of what 64-bit means. Now 4 billion, 32-bit, is a pretty large number. If you started counting to 4 billion today, it would take you about 200 years to get there. On the other hand, 4 billion is not really such a big number. It is not enough to represent the world population.

It's not big enough to represent the U.S. debt. It's not big enough to even represent the US deficit. It's not big enough to represent Bill Gates's fortune. On good days, it's not enough to represent Steve Jobs's fortune. And it's not enough to represent the number of pages indexed by Google or the number of hamburgers sold by McDonald's. So really, 32-bit is very inadequate, in fact, for many purposes. On the other hand, 64-bit is huge. If you started counting to 16 quintillion, And let's say you started the day the universe started, which was about 14 billion years ago, you'd only be about 1.5% of the way there today. So it's a pretty big number.

Okay, so let's talk about the data type changes in 64-bit. Since it's a 64-bit address space, and since you have concurrent access to this address space, pointers go from 32 to 64 bits. In addition, the long data type goes from 32 to 64 bits, and all other data types remain the same.

And that's why this is referred to as the LP64 model. Longs and pointers go to 64-bit. Now, the interesting thing about the LP64 model is it is the model that other Unixes use, So therefore, it is compatible with a lot of open source software that you will find out there, which, of course, is a good thing.

And here is a chart of the data type changes. In ILP32, which is where we are today, integers, longs, and pointers are 32-bit. These are the data types. And in the LP64 world, they all remain the same size, except long goes to 8 bytes and pointer goes to 8 bytes as well. Now one interesting thing to note here is I said other Unixes are LP64, but Windows in the 64-bit world is LLP64. What that means is Windows programs under 64-bit have longs which are 32 bits, but long longs which are 64 bits. So if you have part of your source space which is cross platform that needs to go in Windows as well, that's something to keep in mind. You might want to avoid the long data type in those cases.

So given that a pointer is 64-bit, that sort of implies that the process is either 32-bit or 64-bit, but it cannot be both. There's no notion of mixed mode where a 64-bit process can load 32-bit plugins or libraries. So this means all libraries, frameworks, plugins needed for your application have to be 64-bit for your application to run as 64-bit.

So here's the way the world looked about a year and a half ago. It was nice and simple. We had PowerPC, text-edit, 32-bit, and it in turn depended on a 32-bit PowerPC Cocoa, and that depended on application services, core services, lib system, down what we refer to as the framework stack, and everything was 32-bit PowerPC, and that's all we had. Now with 64-bit, you basically have another parallel stack of frameworks. And the 64-bit text-edit, To run a 64-bit text edit, you need a 64-bit Cocoa, which needs application services, core services, lib system, etc. And this is very similar, really, to the way we did the Intel transition when we introduced it a year ago.

32-bit Intel text edit requires a 32-bit Intel Cocoa, and so on down the stack. They all have to be built for that architecture. And of course, yesterday we introduced 64-bit Intel as well, and that's also yet another stack. Now, of course, the interesting thing to know that is although these are all separate architectures, They are all bundled together in the universal packaging format. So it is actually one binary that contains four separate architectures.

Now, so let me give you a perspective on what we're doing. The 64-bit effort requires that all libraries, frameworks, et cetera, be available as 64-bit. In that way, it's sort of similar to the widening of the Panama Canal. Panama Canal used to be, when it was first built, it was wide enough for most ships or all ships of today, but these days it no longer is wide enough for the large super tankers or the larger cargo ships. And there is an effort or a desire to get the Panama Canal widened. Now, one interesting thing about this effort is that it's not an effort you can do 95%, because if you did, this is what it would look like, and that big ship would get through most of the canal and still get stuck. So really, the effort, doing something like the Panama Canal requires that you do the job 100%. Similarly, if you're an application that wants to run a 64-bit, you need to make sure everything you depend on is available to you as 64-bit. If only 95% of the things are available, your application will not be able to run. And that's what you need. So that's sort of a cheerful realization. So let's think about why we're doing all this, why we're going to all this effort. Why 64-bit?

Now one reason is developers. There are certain developers, certain applications where you're already running into limits of 32-bit and telling us about it. You have applications that really are pushing the limits of 32-bit, and you know you need 64-bit to get those applications running. Similarly, users are also telling us the same thing. They hear from developers that there are certain applications which really need more than 32-bit to be effective. Another reason for doing 64-bit is competition. Microsoft already has 64-bit version of Windows XP out, and there is 64-bit version of Vista on the horizon. So this is something which, again, developers and users point to as something that they need to have on Mac OS X as well.

Hardware is another very important reason for doing 64-bit. We already have 64-bit capable hardware out there. 64-bit chips are available. And on this hardware, like especially Intel 64, there is more registers available to 64-bit. There are wider registers. There is the instruction relative addressing mode, which actually helps out. So therefore, there's some hardware benefits to running in 64-bit. Not to mention the fact that you can put up to 8 or 16 gigabytes of memory into these machines. So really having an application which can access just the piddly 4 gigabytes on your 16 gigabyte hardware, that seems pretty limiting. So today's hardware is already pushing the limits, so the software has to catch up in some way.

And one other reason that I'm going to mention is this idea of investing in the future. That's really doing something whose eventual benefits we can't even foresee or think about today. And that reminds me of Hoover Dam. So Hoover Dam was built in the 1930s. And after it was built, it ended up providing electricity to Las Vegas. And Las Vegas, which used to sort of look like this a few years before Hoover Dam, eventually ended up looking like this. Now, when they built Hoover Dam, there was no master plan that said, we're going to provide electricity to Las Vegas and build one of America's biggest gambling cities. But that's what happened, and Las Vegas blossomed out of the ground. And let me give you another comparison here, an interesting number. Hoover Dam cost $60 million to build in 1930s dollars. That's about $600 million in 1990s dollars. Just the MGM Grand, which you see over there, which was built in the 1990s, just that one hotel cost a billion dollars to build. So again, there you have Hoover Dam, which was a big project for its day, but it ended up resulting in something much bigger than itself, and nobody really had a master plan for this sort of thing. So by invest in this future, what we mean is, we hope to enable you to do things that no one has thought of before. So there's certainly things that we know we wanna do with 64-bit, but there are also many other possibilities that open up because of it.

So let's look at the 64-bit landscape. Let's look at where we are and where others are in 64-bit. Now, in Tiger, only lib system and accelerated frameworks are available, and it's only C and C++. We don't have support for Objective-C, and we don't have support for Java in Tiger. So what this means is in Tiger, you don't have Carbon, you don't have Cocoa, you don't have Quartz, you don't have core services, you don't have core foundation. So a lot of the user interface level functionality is missing. So this is useful for command line tools, but really no GUI applications. And we recommend that to create a GUI application, you create a 64-bit back end, and you create a 32-bit front end, and you basically communicate using IPC. And that's what Mathematica, for instance, has done. That's how Mathematica works in the 64-bit mode. It's got a 32-bit front end with a 64-bit back end.

Now in Leopard, of course, we're enabling any Cocoa or Carbon application to run as 64-bit. And this also includes Java. However, we're making Java available only for Intel 64-bit. Now let me talk about what Microsoft has done with Windows. Its 64-bit version of Windows XP, which is known as XP-X64, was released more than a year ago, in April of last year. And it's really marketed at advanced users. It's not something you can go easily get at your local Costco and install on your machine. You have to jump through a few hoops. And it's not been wildly successful. That's according to Microsoft. One of the reasons for this is there are compatibility issues. 64-bit Windows require 64-bit drivers, which originally were a very small number, and since then there's been new ones introduced. 16-bit DOS and Windows applications are unsupported, and also some 32-bit applications don't work. Because really, under 64-bit Windows, 32-bit applications are running under this WOW64, Windows on Windows 64 environment, which Microsoft used the term emulation for. And that's good enough for many, many apps, but not all. So some theory-tube applications do not work under XP-X64.

And this is a quote from Paul Thoreau of Supersight for Windows, a recent quote. And, you know, he said, XP-X64 is doomed to failure because of hardware and software incompatibilities. And truth is, I don't think Microsoft really also put a lot behind this product. For Microsoft, their intent is to make 64-bit mainstream with Vista. And, you know, that's where they're really looking forward to, it seems like. In Vista, 64-bit is not a separate product. However, it is still a separate DVD. or I think that's the way it's going to be shipping, most editions of Vista will come with 64-bit version and 32-bit version in the box. And at the time you install, you choose which one you want to install. So the user has to make a choice. Am I installing 32 or 64?

And from what I understand, it will have some of the same sort of compatibility issues as XPX64, and of course, also some benefits as well that 64-bit brings to the environment. Now let's compare that to Leopard. In Leopard, it's a single install. You know, you buy your Mac OS X machine or you install Leopard, and it's 64-bit capable, assuming your hardware is 64-bit capable. 32 and 64-bit applications coexist. That's because I showed you, you know, each application runs in its own framework. It uses its own binary, so there's no emulation or anything like that going on here. A 32-bit application runs fully well on a 64-bit machine, and so will a 64-bit application.

So it's full compatibility. So the good thing here is that the user's choice for 64-bit is whether they bought a 64-bit capable machine or not. And you as the developer decide whether 64-bit is right for you, but even when you ship a 32-bit app, there's not this danger that, oh, because the user installed 64-bit, my app won't run. So we believe this gives you somewhat more of a choice in choosing 64-bit. So OK, the OS has 64-bit. What does this mean for you?

OK, so there are a number of considerations for moving to 64-bit. And most of them are performance related, because 64-bit is really touted for performance most of time. Now, if your application needs large address space, the answer is a definite yes. I mean, this is the example we saw with the DNA demo Simon showed yesterday. Simon Patience showed it. I don't know if you saw it, but basically, The 32-bit version took many minutes to go through the frames because it had to load a 4-gigabyte file in chunks and process it, while the 64-bit version could load the whole 4-gigabyte file and just run through it and generate a frame every second.

I mean, they're the orders of magnitude faster for the 64-bit case. And there are many applications like this, so if you have one of those applications, the answer is yes, 64-bit is definitely for you. Now how about others? So there are a few other performance considerations to keep in mind. is that when you're running in 64-bit mode, your pointers are larger, some of your data types are larger, so you're going to be using a little more memory. But, you know, it's probably not that much more. In reality, most of the memory used by applications tend to be in window backing stores and images, which don't change size. So this additional memory impact isn't that bad. On the other hand, there's performance benefits. As I mentioned earlier, there are hardware benefits to running in 64-bit. On Intel, you get more registers. There are wider registers, you get addressing mode. And so with those, compute-bound, CPU-bound applications can actually get a good boost from running in 64-bit. So if your application is very much compute-bound and you're really interested in getting every percentage of CPU out of the machine, 64-bit might be interesting for you as well, although you might not be handling lots of data. One other consideration is the memory impact, the overall memory impact of 64-bit to the system. Now let's look at the earlier framework stack we had. You have the stack of 32-bit frameworks. Now when you run 32-bit text edit, it pulls in the 32-bit stack of frameworks.

When you go ahead and run another 32-bit application, like Safari, it also uses the same frameworks. Of course, it might use some other frameworks as well, but the basic system ones are the same ones. Similarly, 32-bit mail and 32-bit iChat All other 32-bit applications all use the same stack of frameworks. Now let's assume you ran 64-bit chess. And by the way, chess on your system is actually 64-bit today. Of course it's also 32-bit and that's why it runs on your laptops, but if you run it on a 64-bit machine, it will run as a 64-bit application.

So when you run 64-bit chess, it pulls in the 64-bit architectures from each one of those frameworks. So clearly there is a little bit of an additional memory overhead here, and so therefore 64-bit very low memory machine will cause some memory pressure. So that's yet another consideration to keep in mind.

So with those in mind, our answer is that in Leopard, if your application is not going to benefit from 64-bit, it's probably not a good idea to move to 64-bit. However, post-Leopard, the answer changes. And we believe the answer is, yeah, likely someday. We don't believe that 64-bit is going to remain a niche for specialized applications going forward. At some point, we believe most applications will probably become 64-bit. It's almost like today, you know, you do not see 16-bit applications anywhere, really. You know, they're really part of the history because the systems are optimized for running 32-bit applications. And, you know, that's probably going to be the case.

You know, five, ten years from now, do you want to be the only 32-bit app in a block of 64-bit applications? You know, that might be the scenario. Now, we don't have a crystal ball. We don't know what date this is, so on. So it's hard to tell, but we believe this is the case. Now, if you have a framework, a library, or a plugin, and especially if you have a public framework or a public library, public plugin, like a plugin that goes into, say, WebKit or some other plugin mechanism, we really do want you to move to 64-bit as soon as possible because by making your product, your framework, library, or plugin available as 64-bit, you'll be enabling others who might be thinking of moving to 64-bit themselves. So just like we're making the system libraries available as 64-bit, we'd ask you to consider doing the same thing as well.

Now let's talk about levels of 64-bitness. This is like the levels you attain as you go through the conversion process. The very first level is the 64-bit wannabes. And these are who can build their applications as 64-bit. What this means is, you know, you open up your project, you check that checkbox, and you deal with the warnings and the errors, and eventually your project builds as 64-bit. Congratulations, you're now a 64-bit wannabe. Of course, that's not where you really want to be. The next level is the 64-bit novice, and that's when you can actually run your application as 64-bit, which might require some debugging. Now, believe me, it's a real thrill to have your application come up as 64-bit. It's probably the same as the thrill you get when you ran it, and you hopefully did, as Intel, you know, running your application on Intel. You have just conquered a brand-new architecture.

Congratulations. But again, novice is where you... It's not where you want to be either. You want to go to a few more levels above this. There is a 64-bit apprentice. Here, not only does your application run under 64-bit, it can run with addresses above 4 gigabytes. Now why is this interesting?

One of the most common errors that you'll see in 64-bit applications is truncation of addresses, where somebody will take an address, put it into a 32-bit quantity, and then extract it. Now clearly, if the address is below 4 gigabytes, that will work, but once you start managing addresses above 4 gigabytes, that will start to truncate addresses and causing your app to crash. So therefore, this might be a harder level to attain than just novice. Now the good news is When you build your application for 10.5, at least on Intel 64, we build with certain options that make your application run with addresses above 4 gigabytes out of the gate. So therefore, by the time your application runs, it's already an apprentice. On PowerPC, the situation is a little more complicated, and I think we'll go into depth in tomorrow's in-depth talk. But again, a 10.5 built app will have most of the addresses above 4 gigabytes. And once you get your application running, it will be an apprentice. But for instance, if you had a Tiger-based 64-bit command line tool and you run it on Leopard, it's not yet an apprentice because it doesn't have these options enabled. Now, apprentice is not really where you want to be either. You really want to be the 64-bit master.

I mean, probably the important reason you went to 64-bit is that you want to handle large amounts of data. And you've proven yourself a master of 64-bit, which can actually manage more than 4 gigabytes of data. So why is this hard to achieve? Well, maybe not hard, but why might it involve difficulties? I don't want to discourage anybody here. Types like int, sint32, uint32t, etc., these are all 32 bits, and they're 32 bits even in a 64-bit environment. If your application still uses these types and uses APIs with those types, you will only be managing up to 4 gigabytes of data. So really, to make your application fully 64-bit capable, you need to switch those ints to longs, switch them to 64-bit capable types, and where necessary, move to APIs that enable 64-bit management of data. And that will make you a master. Now, master isn't the end of this chain either because there's one more level, and that's when you get to be 64-bit shippable. And that is not only do you do all this, you know, you can run, and you can run about 4 gigabytes, but you do it efficiently. And the reason I mention this is, you know, 64-bit gives you the impression of, oh yeah, it's performance nirvana. You get there, you got all the elbow room, you can do whatever you want, allocate memory, just throw it away, who cares, a lot of memory. And that's not true at all. One thing to keep in mind is not every user who has 64-bit machine has 16 gigabytes of memory, for instance. So really, even under 64-bit, you have to stick to both the tried and true performance techniques you have, and in fact some others as well as a way to make sure your application is as efficient as possible. So really, you know, we want you to design those super tankers that can go through the Panama Canal, but we want them to be very efficient, you know, 0 to 60 in 4 seconds.

Okay, now there's some additional framework considerations when you're going through this. So if you own a framework, Now let's remind ourselves, to be a master, you need to be able to handle more than 4 gigabytes of data. Now if you're a framework or a library, meaning you have some public API other people use, there are two steps to attaining this. And this is true for the system APIs as well. Step one is to make your API handle 64-bit quantities.

And step two is to make your implementation handle 64-bit quantities. And you can do these at different times. And let me talk about that. Now, as I mentioned earlier, types such as int, int32, etc., these are not 64-bit capable. And so if you use these types in your APIs, it cannot express 64-bit quantities.

Here's an example. This is from Core Foundation. Core Foundation has a type called CFIndex. And in Tiger, this is declared to be Sint32, which is clearly a 32-bit type. And an API such as CFArrayGetCount returns a CFIndex, so therefore it's only capable of managing 32-bit worth of data. So -- and since it's signed, CFArrayGetCount cannot represent more than 2 billion elements.

So how do you deal with this? Well, one solution is to use 64-bit types-- long, size T, S size T. There's a number of them. Another solution, a separate path you can take, is to update definitions of existing type defs, like CF index could be updated, byte count could be updated, and so on. And we, in fact, done these with some of these types.

And another solution is to just add new type defs and start using them where appropriate. And we've done this in some other cases. For instance, we've introduced NSInteger, PREFCON, and so on. With that, our sample core foundation CF index now is declared to return a long. and thus CFArray get count is now able to represent up to at least 8 quintillion elements not 16 but 8 because of the sign in this. Now one thing that's important is as you do this you want to make sure that the 32-bit API remains compatible. And here after making this change one thing to notice is that CF index is still declared to be a 32-bit quantity on 32-bits so therefore it is compatible in the 32-bit world. And in fact, Sint32 used to be declared as long in the 32-bit environment, so therefore, we have not changed anything on the 32-bit platform after this change. And so this is important, especially like us. You know, you're in a position of providing binary compatibility release to release.

Okay, so now let me do a quick case study of Cocoa. In Cocoa, we solved our problem of getting to the master level by adding new types, NSInteger and NSUInteger. And as you might know, Cocoa used ints and unsigned ints in almost all of the API, and we just replaced all of those with NSInteger and NSUInteger. And NSInteger and UInteger are declared like this. In LP64, which is how you denote 64-bit, they're defined to be longs, and in 32-bit, they're defined to be ints. And again, we have this definition because we wanna maintain that absolute binary compatibility in 32-bit.

And with that, we've changed the definition of the NSRA methods, for instance, for count and objected index to deal with NSU integers, which means they're now capable of 64-bit. Now, one thing you might be wondering is why do this to all the APIs? And the reason for that is consistency, impedance match, so on. Across Cocoa APIs, we want consistency.

So even though there are some APIs which might not benefit from 64-bit, just changing all of them introduces or preserves that kind of consistency that Cocoa is well known for. So we want to continue that consistency across API by just using the same APIs throughout. Another interesting case is Quartz. In the Quartz case, we added the CG float type. It's a float on 32-bit, but it's a double on 64-bit. Now... This value is used for graphical quantities, coordinates, sizes, et cetera. And using doubles gives us much bigger range and much better precision. We didn't do this, though, for LP64 reasons. In fact, LP64 doesn't dictate anything about floating point types. There's no changes. But the reason we did this is to take advantage of this opportunity we have, the binary incompatibility opportunity. Everybody will be rebuilding their applications. So why not introduce this very forward-looking change so we can benefit from this in years to come?

So all Quartz and Quartz-based APIs will not be using CG floats to represent coordinates, and there are doubles on 64-bit. So here are some examples. Move to point takes two CG floats. Set font size takes a CG float. CG color create, which used to take an area of floats, now takes an area of CG floats.

Okay, so at this point you might be thinking, you know, give me a break. Can CFArray really manage billions of values? You know, that doesn't sound realistic at all. And the answer is, you know, even if something doesn't handle 64 bits behind the scenes, behind the API today, it might someday, you know, someday it will probably do it. And the important thing is to get the API right today because we cannot change APIs very easily. Every time we change APIs, we have to go through a deprecation process, we have to let you all know, you all have to get off the old APIs. It's a hassle for us, hassle for you, hassle for our text pubs folks, you know, it's a lot of work.

So it's important to get the APIs right today. The implementation can follow. And one other thing to keep in mind is that APIs like this, which are capable of 64-bit, can enable new approaches. For instance, with a CFArray capable of representing up to 64 bits worth of elements, you might even imagine using sparse arrays. So arrays which actually don't have, you know, billions and billions of elements in it, but which have indexes in the billions and billions of range where the implementation is smart enough to deal with that. And that could enable you to do, you know, different things in the future using such a data structure. there. Okay, so let me talk about the porting process and go through some of the pitfalls you might encounter during this.

So porting to 64-bit could be as simple as checking that checkbox and going. And you can try that. If you're a command line type of person, you can also compile with the appropriate architectures. You can go ahead and say, just compile your file with -arch ppc64 or -arch x8664. Or you can even do Xcode build from the command line by specifying arches with these architectures. That's all pretty straightforward.

Now, some code conversion will likely be needed. 64-bit is not necessarily source-compatible with 32-bit. You know, there are changes in some of the APIs, some APIs are not there anymore, so on, so you'll get warnings, you'll get errors, and you'll have to deal with those. But our intent is that once you've converted your source base to 64-bit, that you can use the same source base to generate both 32 and 64-bit versions of your application or framework and so on, and that will just give you one source base.

Now, for Cocoa, we have this conversion script. I demoed that earlier in another session. But it's described pretty completely in the Cocoa 6-probit transition guide. And basically this conversion script will take your Cocoa application and do a fairly reasonable job of converting it so it's 64-bit ready. And you'll still have to do some work on it to complete the job. But anyway, it'll get you part of the way there. And maybe even 80-90% if you're lucky.

So, when you port, you know, you're going to get compiler warnings and errors. So, it's a good idea to build your project warning-free before you do the port. That way you know all the warnings you're getting are due to 64-bit and not, you know, oh, the warning was there and so on. And sometimes, you know, the warnings you fix in 32-bit end up fixing real crashers in 64-bit.

So, it's a good idea to pay attention to those. There's some additional flags to help some catching some conversion problems. For instance, shorten 64 to 32. You can either use it from the command line, or there's a checkbox next code for this. This will tell you potentially any place where you're truncating a 64-bit prompt to the 32-bit. Unfortunately, this can be a little too noisy. You'll get warnings where you really didn't want to. However, it might still be a good idea to turn it on at some phase during your port and see what warnings you're getting, because some might be interesting. The W format flag will tell you where you have the wrong printf or sprintf arguments, and it's also very useful. This flag is actually on by default if you're using WALL or WMOST. Now, beyond compiler warnings and beyond compiler errors, you might get runtime crashes when you try to run your application. That's actually good news, because that means, you know, you're still able to catch your errors before you ship your application to the users. And, you know, then you work through your runtime crashes. And right now, let me go through some of the pitfalls that you might encounter while you're debugging these runtime crashes. So the very first one is the very common one, it's pretty obvious, I've already referred to it. It's basically truncating pointers because the code made an assumption that the size of a pointer is equal to the size of an int. You have code here, void star a pointer, you assign that to an int, and then you extract that later on. So if you had the value 2 billion something, or that's 2 with 8 zeros following it, which is clearly above 4 gigabytes, after you're done, the top part of the pointer is gone. So clearly a bad idea. To fix this, you can use a type like int pointer t. Int pointer t was designed for this purpose to be the same size as a pointer, an integer which is the same size as a pointer. You can also use other types like long or any other 64-bit quantity depending on your use. But anyway, something like this is the way you fix this issue.

Another common mistake is mismatch in formatting characters. You know that percent D refers to ints. However, it will work with longs in 32-bit. But in 64-bit, if you have a long, and if you have percent D, that's not a good combination. Because if you have a value like, say, 5 billion, or whatever that is, too many zeros. Anyway, so if you have a value that's above 4 billion, it will go ahead and print a truncated value. So not a good idea. In this case, the fix is to simply use %LD, because %LD is what refers to a long. And that is the right thing to use here. And I believe WFormat will catch errors like this.

Failing to use sizeof. You know, this is probably a pretty elementary problem, but there could be some reason why you used a for there. Maybe the memory you're copying into is for. But anyway, if you have a long and you're copying it somewhere, you know, you really want to use sizeof. Because if you don't, it will again truncate a part of the data you're trying to copy, and you'll get different results and different architectures. So this is also a bad idea. Now, this one's easy. Just go ahead and use sizeof. You know, and in other cases, the fixes might vary. But, you know, it's a common problem as well. Mixing signed and unsigned arithmetic. This is a common pitfall that even in 32-bit that sometimes becomes a problem. This occurs because of C's rules for how it deals with unsigned and signed. When you mix the two, the result is unsigned.

Now typically when you mix the two and assign the result to assigned value, you don't feel the effects of this problem. But in this case, for instance, where you have an int and you have an unsigned int, you add them together, and then you assign to assigned larger quantity, the signed extension is a problem.

Let me give you an example. If you had a value, here's what happens. In this case, minus two plus one is supposed to give you minus one. However, because it's treated as unsigned, it gives you 4.2 billion whatever. And then when that's assigned to the long, it still retains that 4.2 billion, which is clearly wrong, not what you wanted. Now there are many fixes to this. The easiest one probably is to make sure that unless you really want an unsigned result, cast your unsigns to signed values when you're doing arithmetic. That way the compiler knows what you mean. And this is one way to fix this. There are other possible solutions.

failing to specify the appropriate type of constant. So you're going to start seeing a lot of decorations on your constants in 64-bit. In this case, we have an int, and we're trying to shift the number 1 by a certain number of bits. Now if you happen to pass 30, this works as expected. If you happen to pass 31, you get this garbage, mainly because of the sign extension to unsigned. Because you're rolling, or you're shifting a signed 1. So you might think the fix is to use a 1u over there, which is of course an unsigned 1. Now that gets you over the 31, which now behaves correctly. However, 32 doesn't work. And the reason for that is you have a int quantity, and if you try to shift an int more than 32, it just becomes zero because it's only 32 bits, even though you're assigning it to a long. So really, the fix here is to use 1 U L. And there you're specifying that you want an unsigned 64-bit quantity that you're going to shift, and you might be shifting it more than 32. You're going to see these kinds of decorations, U's and UL's and so on, a lot in your code as you move forward. So constants are a good thing to take a look at.

Now here's another one. You might assume this code is pretty straightforward. If def ppc print power pc else print intel. And on 64-bit power pc this prints intel. Because if def under-barred bar ppc is only true for 32-bit ppc. There are a number of preprocessor macros you should use. The first four here, PPC, PPC64, I386, X8664, these are all architecture specific. These are a good idea to use in some cases, for instance, if you have assembly code, but they're really not what you might want to use in other cases. If you want to distinguish Big Andean, for instance, you would use FDef Big Andean. If you want to distinguish 64-bit, you would use this FLP64 that I showed earlier. So again, here is how you might handle BigNDN. And here is how you would handle 64-bit, #iflp64.

Now, there is the pitfall of wrong underlying type, and that's something that impacted us, and it might also hit you as well. And the example is SN32, which I showed earlier. In Tiger, SN32 is declared to be signed long. Now, SN32 is clearly a 32-bit quantity. The name says so. So if you were to go ask for the size of SN32 in a 64-bit app, you will get 8, which is not what you want. So not a good idea. So what we did to fix this is we declared SN32 with two definitions. In 64-bit, it's defined as an int, and in 32-bit it's defined as a long, which gives you a 32-bit definition.

Now, we did this because we want to maintain binary compatibility. If this sort of thing happened in your code and you weren't part of a framework, it's all in an app, you could just go ahead and do the simple declaration, which is just declared to be int32t. This is really the straightforward thing. This is what you really want to do here. You have a type called 32-bit signed int. Go ahead and declare it as an underlying signed integer.

Okay, and another pitfall is not upgrading source code to new types. As I said, we're not using CG floats everywhere for Quartz. And this actually, even if you don't update your source code, it will seem to work in many, many cases because a float will get upgraded to a double when you call a function.

However, if you have something like this where you have an array of four floats and you're trying to create a color with them, and CG color create takes an array of CG floats. So if you call this in the 60-bit app, you will get this result, which is not what you expected.

Really, you need to be passing in an array of CG floats. Now, this is interesting to keep in mind because, again, this CG float conversion cases will work most of the time except when pointers to these quantities are involved, so something to keep in mind. And note that this pitfall could occur with NSInteger and NSUInteger as well.

You know, writing data to files is always tricky across architectures. Using size-changing data types across architectures is interesting. Now with Intel, you might have had to worry about endianness of stuff you wrote to files. With 64-bit, you of course have to worry about the size in addition to endianness. Let's say you have a 32-bit process. It writes a long out, and it does it by just F-writing the long using size of long. That's fine. It writes out four bytes. Now if the corresponding process is run in 64-bit, to read it, it will clearly try to read 8 bytes. So while you really meant to write out 20,000 hexes, it will read back 20,000 followed by garbage and might even crash or give you some other unexpected result. And again the problem is size of long is 8 bytes. There are multiple fixes here. One possible one is be very explicit about what you're writing. Use int32t and write out just 4 bytes in both input and output. This is pretty straightforward. This sort of pitfall exists for the old archiver in Cocoa as well. Let's say you have an instance variable, and let's say it used to be an int, and let's say you ran the conversion script we told you about. The conversion script will convert your int to an NSInteger, and you might think that's a good idea because that seems fine, and that's the way you want to leave it, which is good. However, if you're using the old non-keyed archiver, you need to look at the code now.

The code might have looked like this. You're writing out your instance variable using the type i, which clearly refers to a 32-bit element. And here is the reader that corresponds to this. Clearly, if the reader is in a 64-bit application, there is now an inconsistency between the read and the written value. So this also is a bad idea. So let me show you how you might fix this. Assuming you don't want to change your file format because you want to keep your file format the same between 32 and 64-bit, this is one way you could approach this. And this also is complete with error handling here.

If the instance variable's value is less than int max, assign it to a temporary integer, and write it out using the code you were using before. Now, if the value is greater than int max, clearly you have a value that can no longer be written to that file and something you might want to warn the user about.

You might need to upgrade your file format, but that's left as an exercise to you. And then the reader is, you do the similar kind of change. You read the value into a temp, which is an integer, and then you assign it to your instance variable. And the reason we have to use that temporary integer is because we're reading the value via a pointer, you know, by the address, so we really need to provide the correct size there.

Now, the good news is this is only a problem with the old archiver in Cocoa. And we've actually introduced a new archiver back in 10.2, and we've been encouraging you to move over to it. So hopefully this is only a problem for a very small number of cases. One case is clearly if you have old document formats that you're still supporting, this is something to keep in mind if you do that.

Now here is unexpected API behaviors. Let's say you want to write a function that returns a random number from 0 to 1. And you saw in standard lib.h that random returns a long. So you went ahead and wrote this function. It gets the value of random, divides it by long max, and gets you a value between 0 and 1. Now if you were to run this on a 64-bit application, you will get these tiny little values most of the time. And people playing your poker application or whatnot either be delighted that they're winning all the time or they'll be sad that they'll be losing all the time. But it's not going to seem very random. And the reason for that is because if you read the documentation for random, it says that random returns values from 0 to 2 to the 31 minus 1. So although the header says one thing, the documentation clarifies it a little further. So that's the way random is documented to behave. And this is one of these cases where you can trust the documentation. This is one possible fix. Since you know that it returns 2 to the 31 minus 1, you go divide that by value, so this will give you quantities from 0 to 1. Thank you. Now this is in a category of pitfalls that deserves a special mention.

There are some other API changes across the system. Now this random one was not an API change, but there are some other things to watch out in APIs you're using. And one of them is these API changes where we're making our APIs more POSIX compliant. Functions like mmap, putenv, open, select, and a few more now have different behaviors when run in POSIX compliance mode. By default, these changes apply to 10.5 only applications. Existing 32-bit applications are unaffected, but they are enabled for 64-bit applications. When you run your 64-bit application and when you debug it, you need to pay attention to how you're using these functions. The documentation goes through detail as to what the changes are. It's something you really need to pay attention to because some of the changes are subtle and you have to pass the right options and so on.

Now, speaking of these API changes, there's, of course, another category of API changes to be aware of, and these are APIs not available in 64-bit. Simon mentioned these yesterday. I'm not going to go through it in any detail today, but these are some of the APIs that are not available, and these are some of the cars, and I really love that the CFM there. But these are some of the APIs that are not available to you in 64-bit. Now, for tomorrow's in-depth talk, which is tomorrow morning at 9 a.m., Matthew Formica will actually go through these APIs and go through what the replacements are in detail. So be sure to come for that. Yeah. Okay, so with that, let's go to our closing thoughts on 64-bit.

Now, one thing that's important as you move to 64-bit is this notion of interoperability. You really want the file formats used between 32 and 64-bit to be compatible. For instance, Nib files that Interface Builder generates will work either in 32-bit apps or 64-bit apps with no changes needed. This is true for preference files, all other data files your app writes, and also documents as well. Unless there is a reason that your document needs to start holding 64-bit quantities, so you need to upgrade your document format, be sure that the document formats are interchangeable. The user should not care which version of the app wrote the document. So in this way, the interoperability stakes are similar to 32-bit Intel. Just like users don't know whether they're using 32-bit Intel or PowerPC version of the app, the same holds true for 64-bit versions of the applications. Now another consideration is performance. Now we already talked about whether you should move applications 64-bit or not. And the general recommendation is there's absolutely no benefit to move it, no performance benefit. You probably don't need to move it. It's not going to get you any benefit. Why do it?

Now, however, even in cases where it seems like there might be no benefit, let me give you an example. Earlier in another session, I showed how to convert the TextEdit application to 64-bit. And you would think TextEdit is a fairly small, harmless application, and it is harmless. But when run under 64-bit, it actually becomes capable of opening documents larger than 4 gigabytes. That's one hidden benefit. Is it a good enough benefit? Probably not.

We're not going to ship text as a 64-bit application on the system, but it is something to keep in mind. Now, there's one other performance consideration. If you are moving to 64-bit and you're using 64-bit because of the large memory access, you need to check the physical memory in the machine and adjust your algorithms accordingly. Earlier, I said just because a user has a 64-bit machine, it does not mean they have eight or 16 gigabytes of memory in their machine.

In fact, if you ran that DNA demo we saw yesterday, if you ran the 64-bit version of that on an iMac G5 with 512 megs of memory, it would just bog down terribly because it assumes it's got at least 4 gigabytes of memory. So when you write large memory algorithms where you take advantage of large memory, one thing you should do is you should check the memory in the machine and if enough memory isn't available, either fall back to 32-bit algorithms or maybe your application isn't able to run. And here is one way you could do that. You use the syscadil or syscadil by name with the memsize and it returns to you the amount of physical memory in the machine. Fairly straightforward. And this is one way you can check for this. Thank you.

And porting stakes, again, as I said earlier, it's totally our intent to use the same source base for 32 and 64-bit versions of your binary. This means less sources, less divergence, and less opportunity for problems. Now, if you do need to conditionalize parts of your source code, and you might need to, use #if LP64 to distinguish those cases.

And opportunities in the 64-bit transition. I mean, you already saw one of the opportunities that we took, and that was to introduce CG float and make our graphics subsystem capable of more accuracy, more range, wider range. 64-bit is really an opportunity to remove deprecated APIs, to remove compatibility hacks and old subsystems. You know, it's really an opportunity for us to modernize the OS by removing all the stuff, and for you to modernize your apps by also doing things brand new ways, taking advantage of the six-word address space and moving to new APIs and getting rid of the old APIs that might be holding your application back. Now, we will talk about this in the in-depth talk a great deal, so please come tomorrow. Now, for more coverage for 64-bit, for the API changes, for unavailable APIs, for replacements, state of 64-bit in Leopard Preview, because not everything is quite perfect. We have to admit that. And also, a good amount of information on architectural details especially Intel 64, which is a brand new environment to many of us, please come to the in-depth talk tomorrow morning at 9 a.m., and I believe it's in the big hall in the Presidio. And we have a bunch of documentation. This documentation is not on your DVD, so you have to download it from the WWDC site.

We have a 64-bit transition guide, which is an upgraded version of the Tiger document, and we have transition guides for both Carbon and Cocoa, and these cover the topics I touched upon and a bunch more. And the 64-bit Cocoa one, as I mentioned, goes through the conversion process step by step. So if you're gonna be doing that, it's a good thing to read through. We also have three 64-bit labs starting tomorrow afternoon and Thursday morning and Friday morning, and there'll be many engineers from Appletos, and there'll also be 64-bit capable machines. So please come with your projects if you're interested in converting 64-bit, and we can try to answer some questions. you