How can I find out if library has already been compiled in ModelSim/QuestaSim to speed up simulation scripts?
If a module with a given name has been compiled, there will be a sub-directory in the library "working" directory that has the same name as the module. Also, there will be a file in that subdirectory called _primary.dat. To compile modules for a simulation, I use one or more GNU make rules similar to this, one for each possible source directory:

WORK = work

$(WORK)/%/_primary.dat: ../rtl/uwb_top/v/%.v
    vlog $+


This allows me to type vlib work; make in my simulation directory, and only the modules that have been updated will be recompiled.

However, if your library module names aren't unique, I don't know of any way to tell which source file was used to create a particular working module.

EDIT

Here's an example of a complete Makefile that shows how I typically specify my target modules. This uses features specific to GNU make.

# Modules for the actual device
DUT_MODULES = dut_top dut_module1 dut_module2

# Modules for the simulation testbench
TB_MODULES = tb_top tb_module1 tb_module2 tb_module3

# Convert module names into full target paths
DUT_TARGETS = $(foreach mod, $(DUT_MODULES), $(WORK)/$(mod)/_primary.dat)
TB_TARGETS = $(foreach mod, $(TESTBENCH), $(WORK)/$(mod)/_primary.dat)

WORK = work

# Default 'make' target
tb: $(DUT_TARGETS) $(TB_TARGETS)

# Rule for device modules
$(WORK)/%/_primary.dat: ../rtl/uwb_top/v/%.v
    vlog $+

# Rule for testbench modules
$(WORK)/%/_primary.dat: ../testbench/v/%.v
    vlog $+


UPDATE (2017)

Modelsim/Questasim no longer store compiled code in the manner described above; instead, a set of files containing a database is used, and the names of compiled modules are hidden inside them.

However, the changes to the Makefile to accommodate this are straightforward. First, remove the /_primary.dat from the target path expressions. Second, add a touch command that creates an empty file with the same name as the target module after each vlog command. The Makefile becomes:

# Modules for the actual device
DUT_MODULES = dut_top dut_module1 dut_module2

# Modules for the simulation testbench
TB_MODULES = tb_top tb_module1 tb_module2 tb_module3

# Convert module names into full target paths
DUT_TARGETS = $(foreach mod, $(DUT_MODULES), $(WORK)/$(mod))
TB_TARGETS = $(foreach mod, $(TESTBENCH), $(WORK)/$(mod))

WORK = work

# Default 'make' target
tb: $(DUT_TARGETS) $(TB_TARGETS)

# Rule for device modules
$(WORK)/%/_primary.dat: ../rtl/uwb_top/v/%.v
    vlog $+
    touch $@

# Rule for testbench modules
$(WORK)/%/_primary.dat: ../testbench/v/%.v
    vlog $+
    touch $@


These extra files do not interfere in any way with the simulator, and the rest of the Makefile works the same as before.