Commit graph

352 commits

Author SHA1 Message Date
Andreas Hansson
750f33a901 MEM: Remove the Broadcast destination from the packet
This patch simplifies the packet by removing the broadcast flag and
instead more firmly relying on (and enforcing) the semantics of
transactions in the classic memory system, i.e. request packets are
routed from a master to a slave based on the address, and when they
are created they have neither a valid source, nor destination. On
their way to the slave, the request packet is updated with a source
field for all modules that multiplex packets from multiple master
(e.g. a bus). When a request packet is turned into a response packet
(at the final slave), it moves the potentially populated source field
to the destination field, and the response packet is routed through
any multiplexing components back to the master based on the
destination field.

Modules that connect multiplexing components, such as caches and
bridges store any existing source and destination field in the sender
state as a stack (just as before).

The packet constructor is simplified in that there is no longer a need
to pass the Packet::Broadcast as the destination (this was always the
case for the classic memory system). In the case of Ruby, rather than
using the parameter to the constructor we now rely on setDest, as
there is already another three-argument constructor in the packet
class.

In many places where the packet information was printed as part of
DPRINTFs, request packets would be printed with a numeric "dest" that
would always be -1 (Broadcast) and that field is now removed from the
printing.
2012-04-14 05:45:55 -04:00
Andreas Hansson
dccca0d3a9 MEM: Separate snoops and normal memory requests/responses
This patch introduces port access methods that separates snoop
request/responses from normal memory request/responses. The
differentiation is made for functional, atomic and timing accesses and
builds on the introduction of master and slave ports.

Before the introduction of this patch, the packets belonging to the
different phases of the protocol (request -> [forwarded snoop request
-> snoop response]* -> response) all use the same port access
functions, even though the snoop packets flow in the opposite
direction to the normal packet. That is, a coherent master sends
normal request and receives responses, but receives snoop requests and
sends snoop responses (vice versa for the slave). These two distinct
phases now use different access functions, as described below.

Starting with the functional access, a master sends a request to a
slave through sendFunctional, and the request packet is turned into a
response before the call returns. In a system without cache coherence,
this is all that is needed from the functional interface. For the
cache-coherent scenario, a slave also sends snoop requests to coherent
masters through sendFunctionalSnoop, with responses returned within
the same packet pointer. This is currently used by the bus and caches,
and the LSQ of the O3 CPU. The send/recvFunctional and
send/recvFunctionalSnoop are moved from the Port super class to the
appropriate subclass.

Atomic accesses follow the same flow as functional accesses, with
request being sent from master to slave through sendAtomic. In the
case of cache-coherent ports, a slave can send snoop requests to a
master through sendAtomicSnoop. Just as for the functional access
methods, the atomic send and receive member functions are moved to the
appropriate subclasses.

The timing access methods are different from the functional and atomic
in that requests and responses are separated in time and
send/recvTiming are used for both directions. Hence, a master uses
sendTiming to send a request to a slave, and a slave uses sendTiming
to send a response back to a master, at a later point in time. Snoop
requests and responses travel in the opposite direction, similar to
what happens in functional and atomic accesses. With the introduction
of this patch, it is possible to determine the direction of packets in
the bus, and no longer necessary to look for both a master and a slave
port with the requested port id.

In contrast to the normal recvFunctional, recvAtomic and recvTiming
that are pure virtual functions, the recvFunctionalSnoop,
recvAtomicSnoop and recvTimingSnoop have a default implementation that
calls panic. This is to allow non-coherent master and slave ports to
not implement these functions.
2012-04-14 05:45:07 -04:00
Andreas Hansson
b6aa6d55eb clang/gcc: Fix compilation issues with clang 3.0 and gcc 4.6
This patch addresses a number of minor issues that cause problems when
compiling with clang >= 3.0 and gcc >= 4.6. Most importantly, it
avoids using the deprecated ext/hash_map and instead uses
unordered_map (and similarly so for the hash_set). To make use of the
new STL containers, g++ and clang has to be invoked with "-std=c++0x",
and this is now added for all gcc versions >= 4.6, and for clang >=
3.0. For gcc >= 4.3 and <= 4.5 and clang <= 3.0 we use the tr1
unordered_map to avoid the deprecation warning.

The addition of c++0x in turn causes a few problems, as the
compiler is more stringent and adds a number of new warnings. Below,
the most important issues are enumerated:

1) the use of namespaces is more strict, e.g. for isnan, and all
   headers opening the entire namespace std are now fixed.

2) another other issue caused by the more stringent compiler is the
   narrowing of the embedded python, which used to be a char array,
   and is now unsigned char since there were values larger than 128.

3) a particularly odd issue that arose with the new c++0x behaviour is
   found in range.hh, where the operator< causes gcc to complain about
   the template type parsing (the "<" is interpreted as the beginning
   of a template argument), and the problem seems to be related to the
   begin/end members introduced for the range-type iteration, which is
   a new feature in c++11.

As a minor update, this patch also fixes the build flags for the clang
debug target that used to be shared with gcc and incorrectly use
"-ggdb".
2012-04-14 05:43:31 -04:00
Andreas Hansson
c9634d9b38 Ruby: Ensure order-dependent iteration uses an ordered map
This patch fixes a bug in Ruby that caused non-deterministic
simulation when changing the underlying hash map implementation. The
reason is order-dependent behaviour in combination with iteration over
the hash map contents. The two locations where a sorted container is
assumed are now changed to make use of a std::map instead of the
unordered hash map.

With this change, the stats changes slightly and the follow-on
changeset will update the relevant statistics.
2012-04-12 08:35:49 -04:00
Brad Beckmann
5dfa4cd3f5 sim-ruby: checkpointing fixes and dependent eventq improvements
Fixes checkpointing with respect to lost events after swapping event queues.
Also adds DPRINTFs to better understand what's going on when Ruby serializes
and unserializes.
2012-04-06 13:47:07 -07:00
Brad Beckmann
0a9f4b950f rubytest: seperated read and write ports.
This patch allows the ruby tester to support protocols where the i-cache and d-cache
are managed by seperate controllers.
2012-04-06 13:47:06 -07:00
Andreas Hansson
b00949d88b MEM: Enable multiple distributed generalized memories
This patch removes the assumption on having on single instance of
PhysicalMemory, and enables a distributed memory where the individual
memories in the system are each responsible for a single contiguous
address range.

All memories inherit from an AbstractMemory that encompasses the basic
behaviuor of a random access memory, and provides untimed access
methods. What was previously called PhysicalMemory is now
SimpleMemory, and a subclass of AbstractMemory. All future types of
memory controllers should inherit from AbstractMemory.

To enable e.g. the atomic CPU and RubyPort to access the now
distributed memory, the system has a wrapper class, called
PhysicalMemory that is aware of all the memories in the system and
their associated address ranges. This class thus acts as an
infinitely-fast bus and performs address decoding for these "shortcut"
accesses. Each memory can specify that it should not be part of the
global address map (used e.g. by the functional memories by some
testers). Moreover, each memory can be configured to be reported to
the OS configuration table, useful for populating ATAG structures, and
any potential ACPI tables.

Checkpointing support currently assumes that all memories have the
same size and organisation when creating and resuming from the
checkpoint. A future patch will enable a more flexible
re-organisation.

--HG--
rename : src/mem/PhysicalMemory.py => src/mem/AbstractMemory.py
rename : src/mem/PhysicalMemory.py => src/mem/SimpleMemory.py
rename : src/mem/physical.cc => src/mem/abstract_mem.cc
rename : src/mem/physical.hh => src/mem/abstract_mem.hh
rename : src/mem/physical.cc => src/mem/simple_mem.cc
rename : src/mem/physical.hh => src/mem/simple_mem.hh
2012-04-06 13:46:31 -04:00
Andreas Hansson
a128ba7cd1 Ruby: Remove the physMemPort and instead access memory directly
This patch removes the physMemPort from the RubySequencer and instead
uses the system pointer to access the physmem. The system already
keeps track of the physmem and the valid memory address ranges, and
with this patch we merely make use of that existing functionality. The
memory is modified so that it is possible to call the access functions
(atomic and functional) without going through the port, and the memory
is allowed to be unconnected, i.e. have no ports (since Ruby does not
attach it like the conventional memory system).
2012-03-30 09:42:36 -04:00
William Wang
f9d403a7b9 MEM: Introduce the master/slave port sub-classes in C++
This patch introduces the notion of a master and slave port in the C++
code, thus bringing the previous classification from the Python
classes into the corresponding simulation objects and memory objects.

The patch enables us to classify behaviours into the two bins and add
assumptions and enfore compliance, also simplifying the two
interfaces. As a starting point, isSnooping is confined to a master
port, and getAddrRanges to slave ports. More of these specilisations
are to come in later patches.

The getPort function is not getMasterPort and getSlavePort, and
returns a port reference rather than a pointer as NULL would never be
a valid return value. The default implementation of these two
functions is placed in MemObject, and calls fatal.

The one drawback with this specific patch is that it requires some
code duplication, e.g. QueuedPort becomes QueuedMasterPort and
QueuedSlavePort, and BusPort becomes BusMasterPort and BusSlavePort
(avoiding multiple inheritance). With the later introduction of the
port interfaces, moving the functionality outside the port itself, a
lot of the duplicated code will disappear again.
2012-03-30 09:40:11 -04:00
Andreas Hansson
ca9790a2db Ruby: Fix Set::print for 32-bit hosts
This patch fixes a compilation error caused by a length mismatch on
32-bit hosts. The ifdef and sprintf is replaced by a csprintf.
2012-03-23 06:54:25 -04:00
Andreas Hansson
c2d2ea99e3 MEM: Split SimpleTimingPort into PacketQueue and ports
This patch decouples the queueing and the port interactions to
simplify the introduction of the master and slave ports. By separating
the queueing functionality from the port itself, it becomes much
easier to distinguish between master and slave ports, and still retain
the queueing ability for both (without code duplication).

As part of the split into a PacketQueue and a port, there is now also
a hierarchy of two port classes, QueuedPort and SimpleTimingPort. The
QueuedPort is useful for ports that want to leave the packet
transmission of outgoing packets to the queue and is used by both
master and slave ports. The SimpleTimingPort inherits from the
QueuedPort and adds the implemention of recvTiming and recvFunctional
through recvAtomic.

The PioPort and MessagePort are cleaned up as part of the changes.

--HG--
rename : src/mem/tport.cc => src/mem/packet_queue.cc
rename : src/mem/tport.hh => src/mem/packet_queue.hh
2012-03-22 06:36:27 -04:00
Andreas Hansson
fb395b56dd Scons: Remove Werror=False in SConscript files
This patch removes the overriding of "-Werror" in a handful of
cases. The code compiles with gcc 4.6.3 and clang 3.0 without any
warnings, and thus without any errors. There are no functional changes
introduced by this patch. In the future, rather than ypassing
"-Werror", address the warnings.
2012-03-22 06:34:50 -04:00
Tushar Krishna
c9e4bca8d8 Garnet: Stats at vnet granularity + code cleanup
This patch
(1) Moves redundant code from fixed and flexible networks to BaseGarnetNetwork.
(2) Prints network stats at vnet granularity.
2012-03-19 17:34:17 -04:00
Andreas Hansson
adc419a13a Ruby: Rename RubyPort::sendTiming to avoid overriding base class
This patch renames the sendTiming member function in the RubyPort to
avoid inadvertently hiding Port::sendTiming (discovered through some
rather painful debugging). The RubyPort does, in fact, rely on the
functionality of the queued port and the implementation merely
schedules a send the next cycle. The new name for the member function
is sendNextCycle to better reflect this behaviour.

In the unlikely event that we ever shift to using C++11 the member
functions in Port should have a "final" identifier to prevent any
overriding in derived classes.
2012-03-02 09:16:50 -05:00
Andreas Hansson
1031b824b9 MEM: Move port creation to the memory object(s) construction
This patch moves all port creation from the getPort method to be
consistently done in the MemObject's constructor. This is possible
thanks to the Swig interface passing the length of the vector ports.
Previously there was a mix of: 1) creating the ports as members (at
object construction time) and using getPort for the name resolution,
or 2) dynamically creating the ports in the getPort call. This is now
uniform. Furthermore, objects that would not be complete without a
port have these ports as members rather than having pointers to
dynamically allocated ports.

This patch also enables an elaboration-time enumeration of all the
ports in the system which can be used to determine the masterId.
2012-02-24 11:43:53 -05:00
Andreas Hansson
5a9a743cfc MEM: Introduce the master/slave port roles in the Python classes
This patch classifies all ports in Python as either Master or Slave
and enforces a binding of master to slave. Conceptually, a master (such
as a CPU or DMA port) issues requests, and receives responses, and
conversely, a slave (such as a memory or a PIO device) receives
requests and sends back responses. Currently there is no
differentiation between coherent and non-coherent masters and slaves.

The classification as master/slave also involves splitting the dual
role port of the bus into a master and slave port and updating all the
system assembly scripts to use the appropriate port. Similarly, the
interrupt devices have to have their int_port split into a master and
slave port. The intdev and its children have minimal changes to
facilitate the extra port.

Note that this patch does not enforce any port typing in the C++
world, it merely ensures that the Python objects have a notion of the
port roles and are connected in an appropriate manner. This check is
carried when two ports are connected, e.g. bus.master =
memory.port. The following patches will make use of the
classifications and specialise the C++ ports into masters and slaves.
2012-02-13 06:43:09 -05:00
Ali Saidi
8aaa39e93d mem: Add a master ID to each request object.
This change adds a master id to each request object which can be
used identify every device in the system that is capable of issuing a request.
This is part of the way to removing the numCpus+1 stats in the cache and
replacing them with the master ids. This is one of a series of changes
that make way for the stats output to be changed to python.
2012-02-12 16:07:38 -06:00
Nilay Vaish
aa513a4a99 Ruby: Remove isTagPresent() calls from Sequencer.cc
This patch removes the calls to isTagPresent() from Sequencer.cc. These
calls are made just for setting the cache block to have been most recently
used. The calls have been folded in to the function setMRU().
2012-02-10 11:29:02 -06:00
Koan-Sin Tan
7d4f187700 clang: Enable compiling gem5 using clang 2.9 and 3.0
This patch adds the necessary flags to the SConstruct and SConscript
files for compiling using clang 2.9 and later (on Ubuntu et al and OSX
XCode 4.2), and also cleans up a bunch of compiler warnings found by
clang. Most of the warnings are related to hidden virtual functions,
comparisons with unsigneds >= 0, and if-statements with empty
bodies. A number of mismatches between struct and class are also
fixed. clang 2.8 is not working as it has problems with class names
that occur in multiple namespaces (e.g. Statistics in
kernel_stats.hh).

clang has a bug (http://llvm.org/bugs/show_bug.cgi?id=7247) which
causes confusion between the container std::set and the function
Packet::set, and this is currently addressed by not including the
entire namespace std, but rather selecting e.g. "using std::vector" in
the appropriate places.
2012-01-31 12:05:52 -05:00
Andreas Hansson
cfc268ad9e MEM: Make the RubyPort physMemPort a PioPort instead of M5Port
This patch makes the physMemPort of the RubyPort a PioPort rather than
an M5Port. This reflects the fact that the M5Port and PioPort have
different roles. The M5Port is really a coherent slave that is
connected to the CPUs and other coherent masters of the system,
e.g. DMA ports. The PioPort, on the other hand, is a master port that
is connected to the memory and other slaves, for example the pio
devices.

This simplifies future changes into master/slave ports and is
consistent with the port roles throughout the system.
2012-01-30 05:38:24 -05:00
Nilay Vaish
63563c9df2 O3, Ruby: Forward invalidations from Ruby to O3 CPU
This patch implements the functionality for forwarding invalidations and
replacements from the L1 cache of the Ruby memory system to the O3 CPU. The
implementation adds a list of ports to RubyPort. Whenever a replacement or an
invalidation is performed, the L1 cache forwards this to all the ports, which
is the LSQ in case of the O3 CPU.
2012-01-23 11:07:14 -06:00
William Wang
e731cf4c1d MEM: Remove the functional ports from the memory system
The functional ports are no longer used and this patch cleans up the
legacy that is still present in buses, memories, CPUs etc. Note that
this does not refer to the class FunctionalPort (already removed), but
rather ports with the name (and use) functional.
2012-01-17 12:55:09 -06:00
Andreas Hansson
07cf9d914b MEM: Separate queries for snooping and address ranges
This patch simplifies the address-range determination mechanism and
also unifies the naming across ports and devices. It further splits
the queries for determining if a port is snooping and what address
ranges it responds to (aiming towards a separation of
cache-maintenance ports and pure memory-mapped ports). Default
behaviours are such that most ports do not have to define isSnooping,
and master ports need not implement getAddrRanges.
2012-01-17 12:55:09 -06:00
Andreas Hansson
f85286b3de MEM: Add port proxies instead of non-structural ports
Port proxies are used to replace non-structural ports, and thus enable
all ports in the system to correspond to a structural entity. This has
the advantage of accessing memory through the normal memory subsystem
and thus allowing any constellation of distributed memories, address
maps, etc. Most accesses are done through the "system port" that is
used for loading binaries, debugging etc. For the entities that belong
to the CPU, e.g. threads and thread contexts, they wrap the CPU data
port in a port proxy.

The following replacements are made:
FunctionalPort      > PortProxy
TranslatingPort     > SETranslatingPortProxy
VirtualPort         > FSTranslatingPortProxy

--HG--
rename : src/mem/vport.cc => src/mem/fs_translating_port_proxy.cc
rename : src/mem/vport.hh => src/mem/fs_translating_port_proxy.hh
rename : src/mem/translating_port.cc => src/mem/se_translating_port_proxy.cc
rename : src/mem/translating_port.hh => src/mem/se_translating_port_proxy.hh
2012-01-17 12:55:08 -06:00
Nilay Vaish
0e6d6a5e25 PerfectCacheMemory: Remove references to CacheMsg
The definition for the class CacheMsg was removed long back. Some declaration
had still survived, which was recently removed. Since the PerfectCacheMemory
class relied on this particular declaration, its absence let to compilation
breaking down. Hence this patch.
2012-01-12 00:35:57 -06:00
Nilay Vaish
bf59a9298f Ruby: Resurrect Cache Warmup Capability
This patch resurrects ruby's cache warmup capability. It essentially
makes use of all the infrastructure that was added to the controllers,
memories and the cache recorder.
2012-01-11 13:48:48 -06:00
Nilay Vaish
2d3cae02f5 Ruby Port: Add a list of cpu ports attached to this port 2012-01-11 13:39:58 -06:00
Nilay Vaish
17fc60ee88 Ruby EventQueue: Remove unused functions 2012-01-11 13:31:04 -06:00
Nilay Vaish
8b3ad17cc3 Ruby Sparse Memory: Add function for collating blocks
This patch adds function to the Sparse Memory so that the blocks can be
recorded in a cache trace. The blocks are added to the cache recorder
which can later write them into a file.
2012-01-11 13:29:54 -06:00
Nilay Vaish
c3109f7775 Ruby: Add infrastructure for recording cache contents
This patch changes CacheRecorder, CacheMemory, CacheControllers
so that the contents of a cache can be recorded for checkpointing
purposes.
2012-01-11 13:29:15 -06:00
Nilay Vaish
ab0347a1c6 Ruby Memory Vector: Functions for collating and populating pages
This patch adds functions to the memory vector class that can be used for
collating memory pages to raw trace and for populating pages from a raw
trace.
2012-01-11 11:46:23 -06:00
Nilay Vaish
bd739a75b9 Ruby: remove the files related to the tracer
The Ruby Tracer is out of date with the changes that are being carried
out to support checkpointing. Hence, it needs to be removed.
2012-01-10 18:35:45 -06:00
Nilay Vaish
adff204c97 Sparse Memory: Simplify the structure for an entry
The SparseMemEntry structure includes just one void* pointer. It seems
unnecessary that we have a structure for this. The patch removes the
structure and makes use of a typedef on void* instead.
2012-01-10 10:20:32 -06:00
Nilay Vaish
10c2e8ae9a Ruby Cache: Add param for marking caches as instruction only 2012-01-07 07:38:53 -06:00
Nilay Vaish
ce941fd2ae AbstractController: Remove some of the unused functions
--HG--
extra : rebase_source : 78df7398a609f1db8a2592cd2d1bdc9156d1b8c3
2012-01-06 05:11:07 -06:00
Nilay Vaish
6da125cc3c Ruby Set: Move NUMBER_WORDS_PER_SET to Set.hh
This constant is currently in System.hh, but is only used in Set.hh. It
is being moved to Set.hh to remove this artificial dependence of Set.hh
on System.hh.

--HG--
extra : rebase_source : 683c43a5eeaec4f5f523b3ea32953a07f65cfee7
2012-01-06 05:11:07 -06:00
Nilay Vaish
ea94029ea5 Ruby: Shuffle some of the included files
This patch adds and removes included files from some of the files so as to
organize remove some false dependencies and include some files directly
instead of transitively.

--HG--
extra : rebase_source : 09b482ee9ae00b3a204ace0c63550bc3ca220134
2011-12-31 18:44:51 -06:00
Nilay Vaish
734ef9a209 SLICC: Use pointers for directory entries
SLICC uses pointers for cache and TBE entries but not for directory entries.
This patch changes the protocols, SLICC and Ruby memory system so that even
directory entries are referenced using pointers.

--HG--
extra : rebase_source : abeb4ac78033d003153751f216fd1948251fcfad
2011-12-31 16:38:30 -06:00
Nathan Binkert
6ef9691035 gcc: fix unused variable warnings from GCC 4.6.1
--HG--
extra : rebase_source : f9e22de341493a25ac6106c16ac35c61c128a080
2011-12-13 11:49:27 -08:00
Tushar Krishna
88e91cafc6 Topology: bug fix in external link initialization
--HG--
extra : rebase_source : c226cd1e5e5ed4d4c64fa9427de4905bd8335e34
2011-11-23 16:34:13 -05:00
Nilay Vaish
f3b4d10a05 Ruby: Process packet instead of RubyRequest in Sequencer
This patch changes the implementation of Ruby's recvTiming() function so
that it pushes a packet in to the Sequencer instead of a RubyRequest. This
requires changes in the Sequencer's makeRequest() and issueRequest()
functions, as they also need to operate on a Packet instead of RubyRequest.
2011-11-14 17:44:35 -06:00
Tushar Krishna
ac993964a9 GARNET: adding a fault model for resilient on-chip network research.
This patch adds a fault model, which provides the probability of a number of
architectural faults in the interconnection network (e.g., data corruption,
misrouting). These probabilities can be used to realistically inject faults
in GARNET and faithfully evaluate the effectiveness of novel resilient NoC
architectures.
2011-11-04 18:40:22 -04:00
Nilay Vaish
a547cf34b9 Ruby: Remove some unused typedefs
This patch removes some of the unused typedefs. It also moves
some of the typedefs from Global.hh to TypeDefines.hh. The patch
also eliminates the file NodeID.hh.
2011-11-03 22:46:45 -05:00
Gabe Black
d735abe5da GCC: Get everything working with gcc 4.6.1.
And by "everything" I mean all the quick regressions.
2011-10-31 01:09:44 -07:00
Nilay Vaish
cbaebb3b5e Ruby: Reorganize mapping of components
In RubySlicc_ComponentMapping.hh, certain '#define's have been used for
mapping MachineType to GenericMachineType. These '#define's are being
eliminated and the code will now be generated by SLICC instead. Also
are being eliminated some of the unused functions from
RubySlicc_ComponentMapping.sm.
2011-10-28 13:00:35 -05:00
Steve Reinhardt
84f0a1bd91 event: minor cleanup
Initialize flags via the Event constructor instead of calling
setFlags() in the body of the derived class's constructor.  I
forget exactly why, but this made life easier when implementing
multi-queue support.

Also rename Event::getFlags() to isFlagSet() to better match
common usage, and get rid of some unused Event methods.
2011-09-22 18:59:55 -07:00
Lisa Hsu
ae12ae3023 Functional Accesses: Update states to support Broadcast/Snooping protocols.
In the current implementation of Functional Accesses, it's very hard to
implement broadcast or snooping protocols where the memory has no idea if it
has exclusive access to a cache block or not. Without this knowledge, making
sure the RW vs. RO permissions are right are next to impossible. So we add a
new state called Backing_Store to enable the conveyance that this is the backup
storage for a block, so that it can be written if it is the only possibly RW
block in the system, or written even if there is another RW block in the
system, without causing problems.

Also, a small change to actually set the m_name field for each Controller so
that debugging can be easier. Now you can access a controller's name just by
controller->getName().
2011-09-01 11:41:44 -07:00
Nilay Vaish
ebda5c14a5 SLICC: Pass arguments by reference
Arguments to functions were being passed by value. This patch
changes SLICC so that arguments are passed by reference.
2011-08-29 06:34:40 -05:00
Nilay Vaish
1bbca50491 Ruby: Remove some unused code 2011-08-29 05:10:23 -05:00
Nilay Vaish
a08cc94936 Ruby: Eliminate modulo op for computing set size. 2011-08-26 12:27:58 -05:00