Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
387 additions
and
198 deletions.
There are no files selected for viewing
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,3 +6,4 @@ Blog | |
|
||
announcing-topaz | ||
one-year | ||
type-specialized-instance-variables |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
Type Specialized Instance Variables | ||
=================================== | ||
|
||
**Posted: July 13, 2013** | ||
|
||
In Topaz, like most other VMs, all objects are stored in what are called | ||
"boxes". Essentially that means when you have something like ``x = 5``, ``x`` | ||
is really a pointer to an object which contains ``5``, not the value ``5`` | ||
itself. This is often a source of performance problems for VMs, because it | ||
generates more garbage for the GC to process and means that to access the value | ||
``5`` more memory dereferences are needed. Topaz's just-in-time compiler (JIT) | ||
is often able to remove these allocations and memory dereferences in individual | ||
loops or functions, however it's not able to remove them in structures that | ||
stick around in memory, like objects. | ||
|
||
Therefore, over the past week I've been working on an optimization for Topaz | ||
called "type specialized instance variables". Basically what that means is that | ||
Topaz keeps track of what types instance variables in an object tend to have, | ||
and then specializes the storage to remove the indirection for ``Fixnum`` and | ||
``Float`` objects. | ||
|
||
Let's look at an example: | ||
|
||
.. sourcecode:: ruby | ||
|
||
class Point | ||
def initialize(x, y, z) | ||
@x = x | ||
@y = y | ||
@z = z | ||
end | ||
end | ||
|
||
p = Point.new(1, 2, 3) | ||
|
||
Before this optimization, ``p`` looked like this in memory. Each box indicates | ||
an 8-byte (on 64-bit systems) value, and arrows are pointers: | ||
|
||
.. image:: images/type-specialized-instances-before.png | ||
|
||
And after the optimization, it looks like this: | ||
|
||
.. image:: images/type-specialized-instances-after.png | ||
|
||
With this optimization landed, Topaz will use less memory and be faster for | ||
programs that store ``Fixnum`` and ``Float`` objects in memory. If you're | ||
interested in this type of optimization you can read about a `similar one in | ||
PyPy for lists`_ that we're in the process of porting to Topaz. | ||
|
||
We're looking forward to doing our first release soon, we hope you'll test | ||
Topaz out, and give us feedback with the `nightly builds`_ until then | ||
|
||
.. _`similar one in PyPy for lists`: http://morepypy.blogspot.com/2011/10/more-compact-lists-with-list-strategies.html | ||
.. _`nightly builds`: http://topazruby.com/builds/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,41 @@ | ||
import pytest | ||
|
||
from topaz.mapdict import ClassNode | ||
|
||
from .base import BaseTopazTest | ||
from topaz import mapdict | ||
|
||
|
||
class FakeObject(object): | ||
storage = None | ||
|
||
def __init__(self, map): | ||
self.map = map | ||
self.object_storage = self.unboxed_storage = None | ||
|
||
|
||
class TestMapDict(BaseTopazTest): | ||
class TestMapDict(object): | ||
@pytest.mark.parametrize("i", range(10)) | ||
def test_simple_size_estimation(self, space, i): | ||
class_node = ClassNode(i) | ||
assert class_node.size_estimate() == 0 | ||
class_node = mapdict.ClassNode(i) | ||
assert class_node.size_estimate.object_size_estimate() == 0 | ||
assert class_node.size_estimate.unboxed_size_estimate() == 0 | ||
|
||
for j in range(1000): | ||
w_obj = FakeObject(class_node) | ||
for a in "abcdefghij"[:i]: | ||
w_obj.map.add_attr(space, w_obj, a) | ||
assert class_node.size_estimate() == i | ||
w_obj.map = w_obj.map.add(space, mapdict.ObjectAttributeNode, a, w_obj) | ||
assert class_node.size_estimate.object_size_estimate() == i | ||
assert class_node.size_estimate.unboxed_size_estimate() == 0 | ||
|
||
@pytest.mark.parametrize("i", range(1, 10)) | ||
def test_avg_size_estimation(self, space, i): | ||
class_node = ClassNode(i) | ||
assert class_node.size_estimate() == 0 | ||
class_node = mapdict.ClassNode(i) | ||
assert class_node.size_estimate.object_size_estimate() == 0 | ||
assert class_node.size_estimate.unboxed_size_estimate() == 0 | ||
|
||
for j in range(1000): | ||
w_obj = FakeObject(class_node) | ||
for a in "abcdefghij"[:i]: | ||
w_obj.map.add_attr(space, w_obj, a) | ||
w_obj.map = w_obj.map.add(space, mapdict.ObjectAttributeNode, a, w_obj) | ||
w_obj = FakeObject(class_node) | ||
for a in "klmnopqars": | ||
w_obj.map.add_attr(space, w_obj, a) | ||
assert class_node.size_estimate() in [(i + 10) // 2, (i + 11) // 2] | ||
w_obj.map = w_obj.map.add(space, mapdict.ObjectAttributeNode, a, w_obj) | ||
|
||
assert class_node.size_estimate.object_size_estimate() in [(i + 10) // 2, (i + 11) // 2] | ||
assert class_node.size_estimate.unboxed_size_estimate() == 0 |
Oops, something went wrong.